Raw Source
xaxim / Extratorrent Enhancements

// ==UserScript==
// @name         Extratorrent Enhancements
// @name:en      Extratorrent Enhancements
// @name:pt      Melhorias pro Extratorrent
// @namespace    https://github.com/xaxim/
// @version      0.9.1
// @description  Make decisions easier when searching torrents
// @description:en  Make decisions easier when searching torrents
// @description:pt  Facilita decisões quando procura torrents
// @author       xaxim
// @match        *://*.extratorrent.cc/*
// @match        *://*.etmirror.com/*
// @match        *://*.etproxy.com/*
// @match        *://*.extratorrentonline.com/*
// @match        *://*.extratorrentlive.com/*
// @match        *://*.extra.to/*
// @match        *://*.extratorrent.com/*
// @grant        none
// @downloadURL  https://github.com/xaxim/extratorrent-userscript/raw/master/Extratorrent%20Enhancements.user.js
// ==/UserScript==

(function () {
    'use strict';
    /* eslint-disable no-console */
    console.info('Extratorrent Enhancements 0.9.1 loaded at ', new Date());
    /* eslint-enable no-console */
    const bestResultsStyles = [
        { bg: '#1B5E20', color: '#FFF' },
        { bg: '#2E7D32', color: '#FFF' },
        { bg: '#388E3C', color: '#FFF' },
        { bg: '#43A047', color: '#FFF' },
        { bg: '#4CAF50', color: '#000' },
        { bg: '#66BB6A', color: '#000' },
        { bg: '#81C784', color: '#000' },
        { bg: '#A5D6A7', color: '#000' },
        { bg: '#C8E6C9', color: '#000' },
        { bg: '#E8F5E9', color: '#000' },
    ];
    const body = document.querySelector('body');

    let order = 0;
    let bestResultsPosition = 0;
    let bestFileSizePosition = 0;
    let sizes = {};

    createActivationButton();

    function createActivationButton() {
        const div = document.createElement('div');
        div.style.display = 'flex';
        div.style.justifyContent = 'center';
        const button = document.createElement('input');
        button.type = 'button';
        button.value = 'Activate Extratorrent Enhancements';
        button.id = 'activateExtratorrentEnhancementsButton';
        button.style.backgroundColor = '#4CAF50';
        button.style.border = 'none';
        button.style.color = 'white';
        button.style.padding = '15px 32px';
        button.style.textAlign = 'center';
        button.style.textDecoration = 'none';
        button.style.display = 'inline - block';
        button.style.fontSize = '16px';
        button.style.margin = '4px 2px';
        button.style.cursor = 'pointer';
        button.addEventListener('click', activateExtratorrentEnhancements, { once: true });
        div.appendChild(button);

        body.insertBefore(div, body.childNodes[0]);
    }

    /* eslint-disable no-console */
    function activateExtratorrentEnhancements() {
        console.info('Extratorrent Enhancements Activated!');
        //removes the ad redirecter, which is linked to the document click eventlistener.
        document.onclick = null;

        const resultsTableHeader = body.querySelector('thead');
        const resultsTable = resultsTableHeader.parentElement;
        if (resultsTable) {
            removeUselessPageElements(resultsTable);
            cleanResults(resultsTable);
        } else {
            console.error('No result table was found. Contact developer at github.com/xaxim');
        }
        console.info('Finished Extratorrent Enhancements');
    }
    /* eslint-enable no-console */

    function removeUselessPageElements(resultsTable) {
        Array
            .from(body.children)
            .forEach(elem => elem.remove());
        document
            .querySelectorAll('script')
            .forEach(script => script.remove());
        body.appendChild(resultsTable);
    }

    function cleanResults(resultsTable) {
        const resultsWithoutSeeds = Array.from(resultsTable.querySelectorAll('.sn')).map(seedCell => seedCell.parentElement);
        resultsWithoutSeeds.map(removeWithoutSeeds);
        const results = Array.from(resultsTable.querySelectorAll('.sy')).map(seedCell => seedCell.parentElement);
        const subresults = indexTitles(results);
        const tbody = results[0].parentElement;
        createExtraHeaders(tbody);
        Object.getOwnPropertyNames(subresults).map(title => handleSubresults(subresults, title, tbody));
    }

    function removeWithoutSeeds(result) {
        if (result === null) return null;
        result.remove();
        return null;
    }

    function indexTitles(resultados) {
        const titleIndex = 2;
        const titles = {};
        resultados.forEach(resultado => {
            const titulo = resultado.children[titleIndex].textContent;
            const title = titulo.replace(/[^a-zA-Z]/gi, ' ').trim().split(' ')[0];
            if (!titles[title]) {
                titles[title] = {
                    counter: 1,
                    titulos: [titulo],
                    resultados: [resultado]
                };
            } else {
                titles[title].counter = titles[title].counter + 1;
                titles[title].titulos = [...titles[title].titulos, titulo];
                titles[title].resultados = [...titles[title].resultados, resultado];
            }
        });
        return titles;
    }

    function createExtraHeaders(tbody) {
        const thead = tbody.parentElement.children[0];
        const theadRow = thead.children[0];
        const seedPosition = document.createElement('th');
        seedPosition.textContent = 'Seeds Pos';
        const fileSizePosition = document.createElement('th');
        fileSizePosition.textContent = 'Size Pos';
        theadRow.appendChild(seedPosition);
        theadRow.appendChild(fileSizePosition);
    }

    function handleSubresults(subresults, title, tbody) {
        const seedsIndex = 5;
        const maxSeeds = Math.max.apply(null, subresults[title].resultados.map(resultLine => resultLine.children[seedsIndex].textContent));
        const minSeeds = Math.min.apply(null, subresults[title].resultados.map(resultLine => resultLine.children[seedsIndex].textContent));
        const seedsMean = ((maxSeeds + minSeeds) / 2);
        //console.log(maxSeeds, minSeeds, seedsMean);
        createSeparatorRow(subresults, title, tbody);
        subresults[title].resultados
            .sort(orderBySeeds)
            .map(createSortPositionColumn)
            .map(colorResult)
            .map(result => rearrangeOrderedBySeeds(result, seedsMean))
            .sort(orderByFileSize)
            .map(colorResultFileSize)
            .map(createSortPositionColumn)
            .map(removeLowHealth)
            .sort(orderBySeeds)
            .map(removeNextDuplicatesWithLessSeeds);
        return subresults;
    }

    function createSeparatorRow(subresults, title, tbody) {
        const separator = document.createElement('tr');
        const separatorTitle = document.createElement('td');
        separatorTitle.textContent = `${title} (${subresults[title].resultados.length} raw results)`;
        separatorTitle.colSpan = 6;
        separator.style.border = 1;
        separator.style.borderColor = 'rgb(90,90,90)';
        separator.appendChild(separatorTitle);
        tbody.insertBefore(separator, tbody.childNodes[order]);
        order = order + 1;
        resetGlobalVariables();
    }

    function resetGlobalVariables() {
        bestResultsPosition = 0;
        bestFileSizePosition = 0;
        sizes = {};
    }

    function removeLowHealth(result) {
        if (result === null) return null;
        const healthThreshold = 8;
        const healthIndex = 7;
        const currentResultHealth = result.children[healthIndex].children[0].className.replace('r', '');
        if (currentResultHealth < healthThreshold) {
            result.remove();
            return null;
        }

        return result;
    }

    function orderBySeeds(a, b) {
        if (a === null || b === null) return 0;

        const seedsIndex = 5;
        return b.children[seedsIndex].textContent - a.children[seedsIndex].textContent;
    }

    function orderByFileSize(a, b) {
        if (a === null || b === null) return 0;

        const tdSizeA = a.children[4];
        let [sizeA, unitA] = tdSizeA.innerHTML.split('&nbsp;');
        if (unitA === 'GB') {
            sizeA = sizeA * 1000;
        }
        const tdSizeB = b.children[4];
        let [sizeB, unitB] = tdSizeB.innerHTML.split('&nbsp;');
        if (unitB === 'GB') {
            sizeB = sizeB * 1000;
        }
        return sizeB - sizeA;
    }

    function rearrangeOrderedBySeeds(result, seedsMean) {
        if (result === null) return null;

        const tbody = result.parentElement;
        tbody.insertBefore(result, tbody.childNodes[order]);
        order = order + 1;
        const current = result.textContent;
        if (current > seedsMean) {
            result.innerHTML = `<mark>${current}</mark>`;
        }
        if (current < (seedsMean * 0.1)) {
            result.innerHTML = `<span style="color: red">${current}</span>`;
        }
        return result;
    }

    function createSortPositionColumn(result, index) {
        if (result === null) return null;

        const position = document.createElement('td');
        position.textContent = index + 1;
        result.appendChild(position);
        return result;
    }

    function colorResult(result) {
        if (result === null) return null;

        const tdTitulo = result.children[2];
        const tdSeeds = result.children[5];
        const tdLeechs = result.children[6];

        result.style.backgroundColor = bestResultsStyles[bestResultsPosition].bg;
        result.style.color = bestResultsStyles[bestResultsPosition].color;
        tdSeeds.style.color = bestResultsStyles[bestResultsPosition].color;
        tdLeechs.style.color = bestResultsStyles[bestResultsPosition].color;
        tdTitulo.style.color = bestResultsStyles[bestResultsPosition].color;
        tdTitulo.innerHTML = tdTitulo.textContent;

        bestResultsPosition = bestResultsPosition < (bestResultsStyles.length - 1) ?
            bestResultsPosition + 1 :
            bestResultsStyles.length - 1;
        return result;
    }

    function removeNextDuplicatesWithLessSeeds(result) {
        if (result === null) return null;

        const tdTamanho = result.children[4];
        let [size, unit] = tdTamanho.innerHTML.split('&nbsp;');
        if (unit === 'GB') {
            size = size * 1000;
        }
        if (!sizes[size]) {
            sizes[size] = unit;
            return result;
        }
        //console.log('removing duplicate file with less seeds: ', result);
        result.remove();
        return null;
    }

    function colorResultFileSize(result) {
        if (result === null) return null;

        const tdSize = result.children[4];
        tdSize.style.backgroundColor = bestResultsStyles[bestFileSizePosition].bg;
        tdSize.style.color = bestResultsStyles[bestFileSizePosition].color;
        bestFileSizePosition = bestFileSizePosition < (bestResultsStyles.length - 1) ?
            bestFileSizePosition + 1 :
            bestResultsStyles.length - 1;

        return result;
    }

})();