StashDB x 1337x

This script tries to match the scenes on stash.db to torrents on 1337x.

Versão de: 14/10/2023. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @license      MIT
// @name         StashDB x 1337x
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  This script tries to match the scenes on stash.db to torrents on 1337x.
// @match        https://stashdb.org/scenes*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=stashdb.org
// @grant        GM_xmlhttpRequest
// ==/UserScript==

function GetResponse(url, type) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            url: url,
            responseType: type,
            onload: function(response) {
                resolve(response.response);
            },
            onerror: function(error) {
                reject(error);
            }
        });
    });
}

function GetLinks(doc) {
    const links = [];
    const rows = doc.querySelectorAll('div.table-list-wrap table tbody tr');
    rows.forEach(row => {
        const long = row.querySelector('td:nth-child(1) a:nth-child(2)').textContent;
        const link = `https://1337x.to${row.querySelector('td:nth-child(1) a:nth-child(2)').getAttribute('href')}`;
        const size = row.querySelector('td:nth-child(5)').childNodes[0].textContent;
        links.push([long, link, size]);
    });
    return links;
}

async function GetTags(studio, scene, date, performers) {
    // generate keywords
    const studios = [...new Set([studio, studio.replaceAll(' ', '')])];
    const scenes = [scene];
    const dates = [date, date.substring(2)];
    const keywords = [];
    studios.forEach(a => {
        const pairing = [].concat(scenes, dates, performers);
        pairing.forEach(b => {
            keywords.push(`${a} ${b}`);
        });
    });
    dates.forEach(a => {
        const pairing = [].concat(scenes, performers);
        pairing.forEach(b => {
            keywords.push(`${a} ${b}`);
        });
    });

    // get all links
    const links = [];
    await Promise.all(keywords.map(async keyword => {
        //const url = `https://1337x.to/sort-search/${keyword}/time/desc/1/`;
        const url = `https://1337x.to/search/${keyword}/1/`;
        const doc = await GetResponse(url, 'document');
        links.push(...GetLinks(doc));
    }));
    console.log(keywords, links);

    // sort links
    const countMap = new Map();
    const linkMap = new Map();
    links.forEach(link => {
        const count = countMap.get(link[1]);
        if (count == undefined) {
            countMap.set(link[1], 1);
            linkMap.set(link[1], link);
        } else {
            countMap.set(link[1], count + 1);
        }
    });
    const sortedLinks = [...countMap.entries()].sort((a, b) => {
        const hits = b[1] - a[1];
        function Convert(str) {
            str = str.replace(',', '');
            str = str.replace(' MB', '*1e3');
            str = str.replace(' GB', '*1e6');
            try {
                const value = eval(str);
                return value;
            } catch {
                console.log('Bad Value: ', str);
                return 0;
            }
        }
        const sizes = Convert(linkMap.get(b[0])[2]) - Convert(linkMap.get(a[0])[2]);
        return hits * 1e9 + sizes;
    });
    const result = [];
    function GetShortName(long) {
        return long;
        var shortName = long.replaceAll('.', ' ').toLowerCase();
        const words = [].concat(scenes, studios, performers);
        words.forEach(word => {
            shortName = shortName.replaceAll(word.toLowerCase(), '');
        });
        const YY = date.substring(2, 4);
        const MM = date.substring(5, 7);
        const DD = date.substring(8, 10);
        const wordsToDelete = [`${YY} ${MM} ${DD}`, `20${YY} ${MM} ${DD}`, `${DD} ${MM} 20${YY}`, 'xxx', 'and'];
        wordsToDelete.forEach(word => {
            shortName = shortName.replaceAll(word.toLowerCase(), '');
        });
        return shortName;
    }
    sortedLinks.forEach(entry => {
        const link = linkMap.get(entry[0]);
        result.push([`[${entry[1]} hits] [${link[2]}] ${GetShortName(link[0])}`, link[0], link[1]]);
    });
    return result;
}

function WrapTags(tags) {
    const ul = document.createElement('ul');
    ul.style.overflowY = 'scroll';  // Enable vertical scrolling
    ul.style.maxHeight = '100px';   // Limit the height to 200 pixels
    ul.setAttribute('class', 'scene-tag-list');
    tags.forEach(tag => {
        const a = document.createElement('a');
        a.setAttribute('href', tag[2]);
        a.appendChild(document.createTextNode(tag[0]));
        const abbr = document.createElement('abbr');
        abbr.setAttribute('title', tag[1]);
        abbr.appendChild(a);
        const span = document.createElement('span');
        span.setAttribute('class', 'tag-item badge bg-none');
        span.appendChild(abbr);
        const li = document.createElement('li');
        li.appendChild(span);
        ul.appendChild(li);
    });
    return ul;
}

async function GetPerformers(url) {
    const data = await new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: 'POST',
            responseType: 'json',
            data: `{"operationName":"Scene","variables":{"id":"${url.substring(url.lastIndexOf('/') + 1)}"},"query":"query Scene($id: ID!) {\n  findScene(id: $id) {\n    ...SceneFragment\n    __typename\n  }\n}\n\nfragment URLFragment on URL {\n  url\n  site {\n    id\n    name\n    icon\n    __typename\n  }\n  __typename\n}\n\nfragment ImageFragment on Image {\n  id\n  url\n  width\n  height\n  __typename\n}\n\nfragment ScenePerformerFragment on Performer {\n  id\n  name\n  disambiguation\n  deleted\n  gender\n  aliases\n  __typename\n}\n\nfragment SceneFragment on Scene {\n  id\n  release_date\n  title\n  deleted\n  details\n  director\n  code\n  duration\n  urls {\n    ...URLFragment\n    __typename\n  }\n  images {\n    ...ImageFragment\n    __typename\n  }\n  studio {\n    id\n    name\n    parent {\n      id\n      name\n      __typename\n    }\n    __typename\n  }\n  performers {\n    as\n    performer {\n      ...ScenePerformerFragment\n      __typename\n    }\n    __typename\n  }\n  fingerprints {\n    hash\n    algorithm\n    duration\n    submissions\n    user_submitted\n    created\n    updated\n    __typename\n  }\n  tags {\n    id\n    name\n    description\n    aliases\n    __typename\n  }\n  __typename\n}"}`.replaceAll('\n', ''),
            url: 'https://stashdb.org/graphql',
            headers: { "Content-Type": "application/json" },
            onload: function(response) {
                resolve(response.response);
            },
            onerror: function(error) {
                reject(error);
            }
        });
    });
    const performers = data.data.findScene.performers;
    const result = [];
    performers.forEach(performer => {
        //console.log(performer);
        if (performer.performer.gender != 'MALE') {
            result.push(performer.performer.name);
        }
    });
    return result;
}

function SearchTorrents() {
    const cards = document.querySelectorAll('div.card-footer');
    //const cards = [document.querySelector('div.card-footer')];
    cards.forEach(async card => {
        const studio = card.querySelector('div.text-muted a').textContent;
        const scene = card.querySelector('div.d-flex a h6').textContent;
        const date = card.querySelector('div.text-muted strong').textContent;
        const performers = await GetPerformers(card.querySelector('div.d-flex a').getAttribute('href'));
        const tags = await GetTags(studio, scene, date, performers);
        const ul = WrapTags(tags);
        const existingUl = card.querySelector('ul');
        if (existingUl != undefined) {
            existingUl.remove();
        }
        card.appendChild(ul);
    });
}

function AddSearchTorrentsButton() {
    const button = document.createElement('button');
    button.setAttribute('type', 'button');
    button.setAttribute('class', 'btn btn-primary');
    button.onclick = SearchTorrents;
    button.appendChild(document.createTextNode('⚡️'));
    const a = document.createElement('a');
    a.setAttribute('class', 'ms-auto');
    a.appendChild(button);
    const favoriteFilter = document.querySelector('div.FavoriteFilter');
    favoriteFilter.parentNode.insertBefore(a, favoriteFilter.nextSibling);
}


// Convenience function to execute your callback only after an element matching readySelector has been added to the page.
// Example: runWhenReady('.search-result', augmentSearchResults);
// Gives up after 1 minute.
function runWhenReady(readySelector, callback) {
    var numAttempts = 0;
    var tryNow = function() {
        var elem = document.querySelector(readySelector);
        if (elem) {
            callback(elem);
        } else {
            numAttempts++;
            if (numAttempts >= 34) {
                console.warn('Giving up after 34 attempts. Could not find: ' + readySelector);
            } else {
                setTimeout(tryNow, 250 * Math.pow(1.1, numAttempts));
            }
        }
    };
    tryNow();
}

(function() {
    'use strict';
    runWhenReady('div.scenes-list', AddSearchTorrentsButton);
})();