Sleazy Fork is available in English.
Stash: performer search shortcuts + scene modal player. Free drops: t.me/aofmainhub · Packs & VIP: t.me/aof_chat_farm_2bot
// ==UserScript==
// @name AOF Stash Enhancer
// @namespace https://t.me/aofmainhub
// @version 1.0.1
// @license MIT
// @author AOF Network
// @description Stash: performer search shortcuts + scene modal player. Free drops: t.me/aofmainhub · Packs & VIP: t.me/aof_chat_farm_2bot
// @homepageURL https://t.me/aofmainhub
// @supportURL https://t.me/aof_chat_farm_2bot
// @match http*://*/performers/*
// @match http://localhost:9999/*
// @match http://127.0.0.1:9999/*
// @match http*://*/scenes*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function (root) {
'use strict';
const STORAGE_PREFIX = 'tbccAofPromoDismissed:';
const STYLE_ID = 'tbcc-aof-promo-chip-css';
const DEFAULT_LINKS = [
{ href: 'https://t.me/aofmainhub', label: 't.me/aofmainhub' },
{ href: 'https://t.me/aof_chat_farm_2bot', label: 'Packs & VIP' },
{ href: 'https://t.me/aof_chat_farm_bot', label: 'Support' },
];
function ensureStyles() {
if (document.getElementById(STYLE_ID)) return;
const s = document.createElement('style');
s.id = STYLE_ID;
s.textContent = `
#tbcc-aof-promo-chip-root{
position:fixed;inset:0;z-index:2147483638;pointer-events:none;
}
#tbcc-aof-promo-chip-root .tbcc-aof-promo-chip{
position:fixed;bottom:12px;left:50%;transform:translateX(-50%);z-index:2147483639;
max-width:min(96vw,520px);padding:8px 14px;border-radius:8px;
background:rgba(10,10,12,.92);color:#e5e5e5;font:12px/1.45 system-ui,Segoe UI,sans-serif;
box-shadow:0 4px 20px rgba(0,0,0,.45);text-align:center;pointer-events:auto;
}
#tbcc-aof-promo-chip-root .tbcc-aof-promo-chip a{
color:#8ec5ff;text-decoration:none;pointer-events:auto;cursor:pointer;
}
#tbcc-aof-promo-chip-root .tbcc-aof-promo-chip a:hover{text-decoration:underline}
#tbcc-aof-promo-chip-root .tbcc-aof-promo-chip button{
margin-left:8px;background:transparent;border:0;color:inherit;cursor:pointer;opacity:.7;font-size:14px;line-height:1;
}
#tbcc-aof-promo-chip-root .tbcc-aof-promo-chip button:hover{opacity:1}
`;
(document.head || document.documentElement).appendChild(s);
}
function normalizeSiteId(raw) {
return String(raw || 'default')
.trim()
.toLowerCase()
.replace(/[^\w-]+/g, '-')
.slice(0, 32) || 'default';
}
function isDismissed(siteId) {
try {
return localStorage.getItem(STORAGE_PREFIX + siteId) === '1';
} catch (_) {
return false;
}
}
function dismiss(siteId) {
try {
localStorage.setItem(STORAGE_PREFIX + siteId, '1');
} catch (_) {}
}
function openDirect(href) {
const url = String(href || '').trim();
if (!url) return;
try {
const opened = window.open(url, '_blank', 'noopener,noreferrer');
if (!opened) window.location.assign(url);
} catch (_) {
try {
window.location.assign(url);
} catch (_) {}
}
}
function makeLink(href, label) {
const a = document.createElement('a');
a.href = href;
a.textContent = label;
a.target = '_blank';
a.rel = 'noopener noreferrer';
a.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
openDirect(href);
});
return a;
}
function appendText(parent, text) {
if (text == null || text === '') return;
parent.appendChild(document.createTextNode(String(text)));
}
function mount(opts) {
if (!document.body) return;
const siteId = normalizeSiteId(opts && opts.siteId);
const elId = `tbcc-aof-promo-${siteId}`;
if (document.getElementById(elId) || isDismissed(siteId)) return;
ensureStyles();
let rootEl = document.getElementById('tbcc-aof-promo-chip-root');
if (!rootEl) {
rootEl = document.createElement('div');
rootEl.id = 'tbcc-aof-promo-chip-root';
document.body.appendChild(rootEl);
}
const lead = String((opts && opts.lead) || 'More on AOF').trim();
let links = Array.isArray(opts && opts.links) ? opts.links.slice() : DEFAULT_LINKS.slice(0, 2);
const includeLoot = !opts || opts.includeLoot !== false;
if (includeLoot && links.length <= 2) {
links = links.concat(DEFAULT_LINKS.slice(2));
}
const filtered = links.filter((l) => l && l.href && l.label);
const el = document.createElement('div');
el.id = elId;
el.className = 'tbcc-aof-promo-chip';
appendText(el, lead);
filtered.forEach((l, i) => {
appendText(el, i === 0 ? ' · ' : ' · ');
el.appendChild(makeLink(String(l.href), String(l.label)));
});
const btn = document.createElement('button');
btn.type = 'button';
btn.setAttribute('aria-label', 'Dismiss');
btn.title = 'Dismiss';
btn.textContent = '×';
appendText(el, ' ');
el.appendChild(btn);
btn.addEventListener('click', (e) => {
e.stopPropagation();
dismiss(siteId);
el.remove();
if (rootEl && !rootEl.children.length) rootEl.remove();
});
rootEl.appendChild(el);
}
function unmount(siteId) {
const id = normalizeSiteId(siteId);
document.getElementById(`tbcc-aof-promo-${id}`)?.remove();
const rootEl = document.getElementById('tbcc-aof-promo-chip-root');
if (rootEl && !rootEl.children.length) rootEl.remove();
}
function unmountAll() {
document.querySelectorAll('[id^="tbcc-aof-promo-"]').forEach((n) => n.remove());
document.getElementById('tbcc-aof-promo-chip-root')?.remove();
}
root.TBCCAofPromoChip = { mount, unmount, unmountAll, makeLink, openDirect };
})(typeof globalThis !== 'undefined' ? globalThis : window);
/* ---- feature: performer-search ---- */
(function __tbccFeature_performer_search() {
'use strict';
if (window.__TBCC_FEATURE_performer_search__) return;
window.__TBCC_FEATURE_performer_search__ = true;
(function () {
'use strict';
/* ---------- SimpCity Prefill Helper ---------- */
if (
location.hostname.includes('simpcity.cr') &&
location.pathname.startsWith('/search') &&
new URLSearchParams(location.search).has('sus_auto')
) {
window.addEventListener('DOMContentLoaded', () => {
const params = new URLSearchParams(location.search);
const qParam = params.get('q') || '';
if (!qParam) return;
const term = decodeURIComponent(qParam.replace(/\+/g, ' '));
let attempts = 0;
const tryFill = () => {
const input = document.querySelector('input[name="q"]');
if (input) {
input.value = term;
input.focus();
} else if (attempts < 20) {
attempts++;
setTimeout(tryFill, 250);
}
};
tryFill();
});
return;
}
/* ---------- Settings ---------- */
const LS_KEY = 'stash-universal-search:settings';
const DEFAULT_SETTINGS = {
openInNewTabs: true,
enabledSites: ['empornium', 'bunkr_albums', 'bunkr_pics', 'simpcity'],
};
const loadSettings = () => {
try {
return Object.assign({}, DEFAULT_SETTINGS, JSON.parse(localStorage.getItem(LS_KEY) || '{}'));
} catch { return { ...DEFAULT_SETTINGS }; }
};
const saveSettings = (s) => localStorage.setItem(LS_KEY, JSON.stringify(s));
let SETTINGS = loadSettings();
/* ---------- Site Definitions ---------- */
const SITES = {
empornium: {
label: 'Empornium',
tooltip: 'Empornium: taglist uses firstname.lastname (lowercase); mononyms plain.',
buildUrls: (name) => {
const parts = name.trim().split(/\s+/);
const tag =
parts.length === 1
? parts[0].toLowerCase()
: `${parts[0].toLowerCase()}.${parts.pop().toLowerCase()}`;
return [`https://www.empornium.sx/torrents.php?&taglist=${encodeURIComponent(tag)}`];
},
},
/*
bunkr_archive: {
label: 'Bunkr Explorer',
tooltip: 'Bunkr Explorer: spaces become + (literal, not encoded).',
buildUrls: (name) => {
const q = name.trim().replace(/\s+/g, '+');
return [`https://bunkr-explorer.pages.dev/search?q=${q}`];
},
},
*/
bunkr_albums: {
label: 'Bunkr Albums',
tooltip: 'balbums.st: spaces become +.',
buildUrls: (name) => {
const q = name.trim().replace(/\s+/g, '+');
return [`https://balbums.st/?search=${q}&mode=broad&per=20&sort=latest`];
},
},
bunkr_pics: {
label: 'Bunkr Pics',
tooltip: 'bunkrr.pics: spaces become +.',
buildUrls: (name) => {
const q = name.trim().replace(/\s+/g, '+');
return [`https://bunkrr.pics/search?q=${q}`];
},
},
simpcity: {
label: 'SimpCity',
tooltip: 'SimpCity: opens search page (title-only), prefilled and focused. MUST HIT ENTER OR CLICK SEARCH.',
buildUrls: (name) => [
`https://simpcity.cr/search/?t=post&c[title_only]=1&q=${encodeURIComponent(name)}&sus_auto=1`,
],
},
// --- Coomer with OnlyFans/Fansly username extraction ---
coomer: {
label: 'Coomer',
tooltip: 'Coomer: prefers OnlyFans/Fansly handle if performer URLs contain one.',
buildUrls: (name, _aliases, _inc, _cap, performer) => {
const extractHandle = (urls) => {
if (!urls) return null;
const all = Array.isArray(urls) ? urls.join('\n') : String(urls);
const m = all.match(/(?:onlyfans\.com|fansly\.com)\/([A-Za-z0-9._-]+)/i);
return m ? m[1] : null;
};
const handle = performer?.urls ? extractHandle(performer.urls) : null;
let term;
if (handle) {
term = handle;
console.log(`[SUS] Using ${handle} from performer URLs for Coomer search`);
} else {
term = name.trim().replace(/\s+/g, '');
}
return [`https://coomer.st/artists?q=${encodeURIComponent(term)}`];
},
},
};
/* ---------- GraphQL ---------- */
const gql = async (query, variables) => {
const res = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables }),
credentials: 'same-origin',
});
const j = await res.json();
if (j.errors) throw new Error(JSON.stringify(j.errors));
return j.data;
};
const fetchPerformer = async (id) => {
const q = `
query FindPerformer($id: ID!) {
findPerformer(id: $id) {
id
name
alias_list
urls
}
}`;
const d = await gql(q, { id });
return d?.findPerformer || null;
};
/* ---------- Helpers ---------- */
const isPerf = () => /\/performers\/\d+/.test(location.pathname);
const getPerfId = () => location.pathname.match(/\/performers\/(\d+)/)?.[1];
const addStyles = () => {
if (document.getElementById('sus-styles')) return;
const css = `
.sus-group{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.sus-btn{border:1px solid #4b5563;background:#1f2937;color:#e5e7eb;
padding:6px 10px;border-radius:6px;font-size:12px;cursor:pointer}
.sus-btn:hover{filter:brightness(1.15)}
#sus-panel{position:fixed;top:20px;right:20px;z-index:99999;
background:#111827;color:#e5e7eb;border:1px solid #6b7280;
border-radius:8px;padding:12px;max-width:360px;}
#sus-panel input[type="checkbox"]{accent-color:#93c5fd;}
#sus-panel label{display:block;margin:4px 0;}
#sus-panel button{margin-top:6px;}
`;
const s = document.createElement('style');
s.id = 'sus-styles';
s.textContent = css;
document.head.appendChild(s);
};
const openUrls = (urls) => {
urls.forEach((u, i) =>
setTimeout(() => window.open(u, SETTINGS.openInNewTabs ? '_blank' : '_self'), i * 150)
);
};
/* ---------- Settings Panel ---------- */
function showSettingsPanel() {
const existing = document.getElementById('sus-panel');
if (existing) existing.remove();
const p = document.createElement('div');
p.id = 'sus-panel';
const sites = Object.keys(SITES);
p.innerHTML = `
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
<b>Universal Search Settings</b>
<span style="cursor:pointer;" id="sus-close">✕</span>
</div>
<label><input type="checkbox" id="sus-tabs" ${SETTINGS.openInNewTabs ? 'checked' : ''}>
Open results in new tabs</label>
<hr>
<b>Enabled sites:</b>
${sites.map((k) =>
`<label><input type="checkbox" data-site="${k}" ${SETTINGS.enabledSites.includes(k) ? 'checked' : ''}>
${SITES[k].label}</label>`).join('')}
<div style="text-align:right;">
<button id="sus-save" class="sus-btn">Save</button>
</div>
`;
document.body.appendChild(p);
p.querySelector('#sus-close').onclick = () => p.remove();
p.querySelector('#sus-save').onclick = () => {
SETTINGS.openInNewTabs = p.querySelector('#sus-tabs').checked;
SETTINGS.enabledSites = Array.from(p.querySelectorAll('input[data-site]:checked'))
.map((i) => i.getAttribute('data-site'));
saveSettings(SETTINGS);
p.remove();
alert('Settings saved. Refresh performer page to apply.');
};
}
/* ---------- Build Buttons ---------- */
const buildButtons = (perf) => {
const anchor = Array.from(document.querySelectorAll('button'))
.find((b) => /Edit/i.test(b.textContent || ''))?.parentElement;
if (!anchor) return;
const group = document.createElement('div');
group.className = 'sus-group';
SETTINGS.enabledSites.forEach((k) => {
const site = SITES[k];
if (!site) return;
const btn = document.createElement('button');
btn.className = 'sus-btn';
btn.textContent = site.label;
btn.title = site.tooltip;
btn.onclick = () => {
const urls = site.buildUrls(perf.name, perf.alias_list, false, 0, perf);
openUrls(urls);
};
group.appendChild(btn);
});
const gear = document.createElement('button');
gear.className = 'sus-btn';
gear.textContent = '⚙️ Settings';
gear.title = 'Configure Universal Search';
gear.onclick = showSettingsPanel;
group.appendChild(gear);
anchor.parentElement.insertBefore(group, anchor.nextSibling);
};
/* ---------- SPA / Hard Refresh Fallback ---------- */
let lastPath = location.pathname;
let isInitializing = false;
async function init() {
if (isInitializing) return;
isInitializing = true;
if (!isPerf()) { isInitializing = false; return; }
const id = getPerfId();
if (!id) { isInitializing = false; return; }
try {
const perf = await fetchPerformer(id);
if (perf) {
if (!document.querySelector('.sus-group')) {
addStyles();
buildButtons(perf);
}
}
} catch (e) {
console.error('[SUS] GraphQL fail', e);
}
isInitializing = false;
// If the page is still a performer page but buttons weren't inserted
// (DOM may not have been fully ready during the GraphQL fetch), retry.
if (isPerf() && !document.querySelector('.sus-group')) {
setTimeout(waitForPerformerPage, 500);
}
}
function waitForPerformerPage() {
const editButton = Array.from(document.querySelectorAll('button')).find(
(b) => /Edit/i.test(b.textContent || '')
);
if (editButton) {
init();
} else {
setTimeout(waitForPerformerPage, 500);
}
}
waitForPerformerPage();
const observer = new MutationObserver(() => {
if (location.pathname !== lastPath) {
lastPath = location.pathname;
document.querySelectorAll('.sus-group').forEach((e) => e.remove());
waitForPerformerPage();
}
});
observer.observe(document, { childList: true, subtree: true });
})();
})();
/* ---- feature: scene-modal-player ---- */
(function __tbccFeature_scene_modal_player() {
'use strict';
if (window.__TBCC_FEATURE_scene_modal_player__) return;
window.__TBCC_FEATURE_scene_modal_player__ = true;
(function () {
'use strict';
const MODAL_ID = 'tbcc-stash-scene-modal';
const STYLE_ID = 'tbcc-stash-scene-modal-css';
const LS_KEY = 'tbcc-stash-scene-modal:enabled';
const enabled = () => {
try {
return localStorage.getItem(LS_KEY) !== '0';
} catch (_) {
return true;
}
};
function sceneIdFromHref(href) {
const m = String(href || '').match(/\/(scenes|images|groups)\/(\d+)/);
return m ? { kind: m[1], id: m[2] } : null;
}
function isListContext() {
const p = location.pathname;
if (/^\/(scenes|images|groups)\/\d+/.test(p)) return false;
return /^\/(scenes|images|groups)(\/|$)/.test(p) || p === '/' || p === '/scenes';
}
function ensureStyles() {
if (document.getElementById(STYLE_ID)) return;
const s = document.createElement('style');
s.id = STYLE_ID;
s.textContent = `
#${MODAL_ID}{
position:fixed;inset:0;z-index:2147483600;display:none;align-items:center;justify-content:center;
background:rgba(0,0,0,.72);backdrop-filter:blur(4px);
}
#${MODAL_ID}.open{display:flex}
#${MODAL_ID} .tbcc-stash-modal-shell{
width:min(96vw,1280px);height:min(92vh,900px);background:#202b33;border-radius:10px;
box-shadow:0 16px 48px rgba(0,0,0,.55);display:flex;flex-direction:column;overflow:hidden;
border:1px solid rgba(255,255,255,.08);
}
#${MODAL_ID} .tbcc-stash-modal-bar{
display:flex;align-items:center;gap:10px;padding:8px 12px;background:#1a2329;border-bottom:1px solid rgba(255,255,255,.06);
}
#${MODAL_ID} .tbcc-stash-modal-title{flex:1;font:600 13px/1.4 system-ui,Segoe UI,sans-serif;color:#e5e7eb;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#${MODAL_ID} .tbcc-stash-modal-bar button{
border:1px solid rgba(255,255,255,.12);background:#2a3942;color:#e5e7eb;border-radius:6px;padding:6px 10px;font:12px system-ui;cursor:pointer;
}
#${MODAL_ID} .tbcc-stash-modal-bar button:hover{background:#33424c}
#${MODAL_ID} iframe{flex:1;border:0;width:100%;background:#0f1419}
`;
document.head.appendChild(s);
}
function ensureModal() {
ensureStyles();
let root = document.getElementById(MODAL_ID);
if (root) return root;
root = document.createElement('div');
root.id = MODAL_ID;
root.innerHTML =
'<div class="tbcc-stash-modal-shell" role="dialog" aria-modal="true">' +
'<div class="tbcc-stash-modal-bar">' +
'<span class="tbcc-stash-modal-title">Scene</span>' +
'<button type="button" data-act="tab">Open in tab</button>' +
'<button type="button" data-act="close">Close</button>' +
'</div>' +
'<iframe title="Stash scene" sandbox="allow-same-origin allow-scripts allow-forms allow-popups"></iframe>' +
'</div>';
document.body.appendChild(root);
root.addEventListener('click', (e) => {
if (e.target === root) closeModal();
});
root.querySelector('[data-act="close"]').addEventListener('click', closeModal);
root.querySelector('[data-act="tab"]').addEventListener('click', () => {
const href = root.dataset.href;
if (href) window.open(href, '_blank', 'noopener,noreferrer');
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && root.classList.contains('open')) closeModal();
});
return root;
}
function closeModal() {
const root = document.getElementById(MODAL_ID);
if (!root) return;
root.classList.remove('open');
const iframe = root.querySelector('iframe');
if (iframe) iframe.src = 'about:blank';
}
function openModal(kind, id, label) {
const root = ensureModal();
const href = '/' + kind + '/' + id;
root.dataset.href = href;
root.querySelector('.tbcc-stash-modal-title').textContent = label || href;
root.querySelector('iframe').src = href;
root.classList.add('open');
}
document.addEventListener(
'click',
(e) => {
if (!enabled() || !isListContext()) return;
if (e.defaultPrevented || e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;
const anchor = e.target.closest('a[href*="/scenes/"], a[href*="/images/"], a[href*="/groups/"]');
if (!anchor) return;
const parsed = sceneIdFromHref(anchor.getAttribute('href'));
if (!parsed) return;
e.preventDefault();
e.stopPropagation();
const card = anchor.closest('.card, .grid-card, [class*="card"]');
const label =
(card && (card.querySelector('.card-section-title, h5, .truncated') || {}).textContent) ||
anchor.getAttribute('title') ||
anchor.textContent ||
parsed.kind + ' ' + parsed.id;
openModal(parsed.kind, parsed.id, String(label).trim().slice(0, 120));
},
true
);
})();
})();
/* TBCC stash_enhancer — boot */
(function () {
'use strict';
function mountPromo() {
try {
globalThis.TBCCAofPromoChip?.mount?.({ siteId: 'stash', lead: 'AOF network' });
} catch (_) {}
}
if (document.body) mountPromo();
else document.addEventListener('DOMContentLoaded', mountPromo, { once: true });
})();