StashDB x 1337x

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

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

You will need to install an extension such as Tampermonkey to install this script.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         StashDB x 1337x
// @license      MIT
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  This script tries to match the scenes on stash.db to torrents on 1337x.
// @match        https://stashdb.org/*
// @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);
    document.querySelector('div.scenes-list div').appendChild(a);
}


// 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);
})();