Domanda Estensione per vedere la durata dei video PPV di OnlyFans?

GretaFM2

Lurker
Iscritto dal
16 Sep 2024
Messaggi
11
Reazioni
0
Avevo un'estensione che permetteva di vedere in anteprima la durata dei video di un bundle. So che era un'estensione per sviluppatori o uno script tampermonkey, ma non ricordo proprio come si chiamava. L'avevo trovata qui, ma non riesco a ritrovarla. Sono sicuro che qualcuno saprà dirmelo. Grazie in anticipo
 
Non ho molto da aggiungere, ma l'ultima volta che ho sentito, non funziona più. E non c'è modo di farlo funzionare. Era un po' di tempo fa, quindi potrei sbagliarmi. Ma dubito fortemente che esista un metodo funzionante.
 
Ne avevo una che funzionava un paio di settimane fa, ma l'ho cancellata quando ho formattato la mia VM e me ne sono completamente dimenticato. Ricordo di aver trovato il nome in un thread di discussione su una modella o in uno di questi post sui tool. Ho provato a rifare i miei passi, senza fortuna
 
Non so se ti serve ancora, ma io uso questa estensione di Chrome.
ofcheck.lol
 
Ho fatto questo script tampermonkey per vedere la durata dei video dei contenuti bloccati.

Codice:
// ==UserScript==
// @name OnlyFans Locked Video Durations
// @version 1.0.2
// @description Shows how long each video is inside a locked message bundle, before you pay for it.
// @match https://onlyfans.com/*
// @run-at document-start
// @grant none
// ==/UserScript==

// The durations are already sitting in the /api2/v2/chats/.../messages response,
// OF just never renders them. You can see it yourself: devtools > Network > filter
// "messages" > Preview > media > any video item. So all this does is grab that
// response on its way past and print the numbers onto the locked chip.
//
// 1.0.0 - first version
// 1.0.1 - fixed durations coming back in ms on some accounts
// 1.0.2 - dropped the total, per-video only

(function () {
'use strict';

var DEBUG = false; // flip this on if durations aren't showing up
var NUMBERED = false; // true gives you "1. 1:03" instead of just "1:03"

function log() {
if (!DEBUG) return;
console.log.apply(console, ['[OFDur]'].concat([].slice.call(arguments)));
}

// everything we've scraped so far, keyed by message id
var seen = new Map();

var API = /\/api2\/v2\/.*(messages|chats|posts|vault|stories)/i;

// ---- reading the response ----

function secs(v) {
if (typeof v === 'number' && isFinite(v) && v > 0) {
return v > 100000 ? Math.round(v / 1000) : Math.round(v); // ms on some endpoints
}
if (typeof v === 'string') {
if (/^\d+(\.\d+)?$/.test(v)) return secs(parseFloat(v));
var mm = v.match(/^(?:(\d+):)?(\d{1,2}):(\d{2})$/);
if (mm) return (+(mm[1] || 0)) * 3600 + (+mm[2]) * 60 + (+mm[3]);
}
return null;
}

// duration isn't always in the same spot (media.duration vs media.info.source.duration
// vs media.source.duration depending on the endpoint), so just dig for it
function durationOf(obj, depth) {
depth = depth || 0;
if (!obj || typeof obj !== 'object' || depth > 5) return null;

if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
var d = durationOf(obj[i], depth + 1);
if (d) return d;
}
return null;
}

var keys = ['duration', 'videoDuration', 'durationSeconds'];
for (var k = 0; k < keys.length; k++) {
if (keys[k] in obj) {
var s = secs(obj[keys[k]]);
if (s) return s;
}
}
for (var key in obj) {
if (obj[key] && typeof obj[key] === 'object') {
var found = durationOf(obj[key], depth + 1);
if (found) return found;
}
}
return null;
}

function stripTags(html) {
if (!html) return '';
try {
var doc = new DOMParser().parseFromString(String(html), 'text/html');
return doc.body ? doc.body.textContent || '' : '';
} catch (e) {
return String(html).replace(/<[^>]*>/g, ' ');
}
}

function clean(s) {
return String(s || '')
.replace(/[\u200B-\u200D\uFEFF\u00A0]/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
}

function money(v) {
if (v == null) return null;
var n = parseFloat(String(v).replace(/[^0-9.]/g, ''));
return isFinite(n) ? n : null;
}

function save(msg) {
var id = String(msg.id);
var vids = [];
var pics = 0;

for (var i = 0; i < msg.media.length; i++) {
var m = msg.media[i];
if (!m || typeof m !== 'object') continue;
var t = String(m.type || '').toLowerCase();
if (t === 'video' || t === 'gif') vids.push(durationOf(m) || 0);
else if (t === 'photo' || t === 'image') pics++;
}
if (!vids.length) return;

// a later response can be more complete than an earlier one, but don't downgrade
var old = seen.get(id);
if (old && old.vids.filter(Boolean).length >= vids.filter(Boolean).length) return;

seen.set(id, {
id: id,
vids: vids,
pics: pics,
price: money(msg.price),
text: clean(stripTags(msg.text))
});
log('got', id, vids.map(mmss).join(', '));
}

// the messages live at different depths depending on the endpoint, so walk the whole thing
function crawl(node, depth) {
depth = depth || 0;
if (!node || typeof node !== 'object' || depth > 8) return;
if (Array.isArray(node)) {
for (var i = 0; i < node.length; i++) crawl(node[i], depth + 1);
return;
}
if (node.id != null && Array.isArray(node.media) && node.media.length) save(node);
for (var k in node) {
if (node[k] && typeof node[k] === 'object') crawl(node[k], depth + 1);
}
}

function gotResponse(url, body) {
if (!body || !API.test(url)) return;
var data;
try { data = JSON.parse(body); } catch (e) { return; }
try { crawl(data); paintSoon(); } catch (e) { log('parse blew up', e); }
}

// ---- hooking the requests (needs @run-at document-start or the app beats us to it) ----

var realFetch = window.fetch;
if (realFetch) {
window.fetch = function () {
var args = arguments;
var p = realFetch.apply(this, args);
try {
return p.then(function (res) {
try {
var first = args[0];
var url = (typeof first === 'string' ? first : (first && first.url)) || res.url || '';
if (API.test(url)) {
res.clone().text().then(function (t) { gotResponse(url, t); }).catch(function () {});
}
} catch (e) {}
return res;
});
} catch (e) {
return p;
}
};
}

var realOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url) {
try { this._ofdurUrl = String(url); } catch (e) {}
return realOpen.apply(this, arguments);
};

var realSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
var xhr = this;
try {
xhr.addEventListener('load', function () {
try {
var url = xhr._ofdurUrl || '';
if (!API.test(url)) return;
if (xhr.responseType === '' || xhr.responseType === 'text') {
gotResponse(url, xhr.responseText);
} else if (xhr.responseType === 'json' && xhr.response) {
crawl(xhr.response);
paintSoon();
}
} catch (e) {}
});
} catch (e) {}
return realSend.apply(this, arguments);
};

// ---- matching a saved message to its bubble ----
// there is no message id anywhere in the chat DOM (checked - no id, no data-id,
// nothing on the wrapper), so we match on what the locked chip is showing:
// video count first, then the message text, then photo count and price.

function mmss(s) {
if (!s) return '?:??';
var h = Math.floor(s / 3600);
var m = Math.floor((s % 3600) / 60);
var sec = Math.round(s % 60);
var pad = function (n) { return n < 10 ? '0' + n : '' + n; };
return h ? h + ':' + pad(m) + ':' + pad(sec) : m + ':' + pad(sec);
}

function readChip(node) {
var info = { pics: null, vids: null, price: null, text: '' };

var list = node.querySelector('.b-purchase__list');
if (list) {
var items = list.querySelectorAll('.b-purchase__list-item');
for (var i = 0; i < items.length; i++) {
var icon = items[i].querySelector('svg[data-icon-name]');
var name = icon ? icon.getAttribute('data-icon-name') : '';
var countEl = items[i].querySelector('.b-purchase__list-item__count');
var n = countEl ? parseInt(String(countEl.textContent).replace(/[^0-9]/g, ''), 10) : NaN;
if (!isFinite(n)) continue;
if (name === 'icon-video') info.vids = n;
else if (name === 'icon-media' || name === 'icon-photo') info.pics = n;
}
}

var priceEl = node.querySelector('.m-price-lock .b-purchase__list-item__count, .b-post__unknown__price');
if (priceEl) info.price = money(priceEl.textContent);

var textEl = node.querySelector('.b-chat__message__text-holder');
if (textEl) info.text = clean(textEl.textContent);

return info;
}

function lookup(info) {
var pool = [];
seen.forEach(function (e) {
if (info.vids == null || e.vids.length === info.vids) pool.push(e);
});
if (!pool.length) return null;

// narrow, but only if the filter doesn't wipe everything out
function tighten(fn) {
var next = pool.filter(fn);
if (next.length) pool = next;
}
if (info.text) tighten(function (e) { return e.text === info.text; });
if (info.pics != null) tighten(function (e) { return e.pics === info.pics; });
if (info.price != null) tighten(function (e) { return e.price === info.price; });

return pool[0] || null;
}

// ---- drawing ----

function pill(text) {
var el = document.createElement('span');
el.textContent = text;
el.style.display = 'inline-block';
el.style.padding = '0 6px';
el.style.borderRadius = '9px';
el.style.fontSize = '11px';
el.style.lineHeight = '17px';
el.style.fontWeight = '500';
el.style.whiteSpace = 'nowrap';
el.style.border = '1px solid currentColor';
el.style.opacity = '.75'; // currentColor so it works in both themes
return el;
}

function buildRow(entry) {
var row = document.createElement('div');
row.setAttribute('data-ofdur', '1');
row.style.display = 'flex';
row.style.flexWrap = 'wrap';
row.style.gap = '4px';
row.style.margin = '6px 0 2px';
row.style.lineHeight = '17px';

for (var i = 0; i < entry.vids.length; i++) {
row.appendChild(pill(NUMBERED ? (i + 1) + '. ' + mmss(entry.vids[i]) : mmss(entry.vids[i])));
}
return row;
}

function attach(node, entry) {
if (node.querySelector('[data-ofdur]')) return;
var row = buildRow(entry);

// sits right under the photo/video counts and above the unlock button.
// can't go in the ul itself, that has g-text-ellipsis on it and long rows get cut off.
var icons = node.querySelector('.b-subscribe-block .content-icons');
if (icons && icons.parentNode) {
icons.parentNode.insertBefore(row, icons.nextSibling);
return;
}
var block = node.querySelector('.b-subscribe-block') || node.querySelector('.b-post__unknown');
if (block) {
block.appendChild(row);
return;
}
(node.querySelector('.b-chat__message__body') || node).appendChild(row);
}

function paint() {
if (!seen.size) return;
var bubbles = document.querySelectorAll('.b-chat__message, [at-attr="chat_message"]');
for (var i = 0; i < bubbles.length; i++) {
var node = bubbles[i];
if (node.querySelector('[data-ofdur]')) continue;
var info = readChip(node);
if (info.vids == null) continue; // not a locked bundle
var entry = lookup(info);
if (!entry) { log('nothing matches', info); continue; }
attach(node, entry);
}
}

// vue rips these out and re-renders them constantly, hence the observer + the interval
var pending = false;
function paintSoon() {
if (pending) return;
pending = true;
requestAnimationFrame(function () {
pending = false;
try { paint(); } catch (e) { log(e); }
});
}

function start() {
try {
new MutationObserver(paintSoon).observe(document.documentElement, {
childList: true,
subtree: true
});
} catch (e) {}
setInterval(paintSoon, 1500);
paintSoon();
}

if (document.documentElement) start();
else document.addEventListener('DOMContentLoaded', start, { once: true });

window.__ofdur = { seen: seen, paint: paint };
})();
 
Grazie per la condivisione. Ho notato che la durata dei video non appare su alcuni messaggi.
 
Indietro
Top