Nyaa.se Batch downloader

Batch download torrents from nyaa.se

目前为 2015-07-07 提交的版本。查看 最新版本

// ==UserScript==
// @name        Nyaa.se Batch downloader
// @namespace   Autodownload
// @author      Victorique
// @description Batch download torrents from nyaa.se
// @include     http://www.nyaa.se/?page=search&cats=*&filter=*&term=*&user=*
// @include     http://www.nyaa.se/?page=search&filter=*&term=*&user=*
// @include     http://www.nyaa.se/?page=search&term=*&user=*
// @version     4.1.3
// @license     MIT
// @run-at      document-start
// @grant       none
// @require     https://code.jquery.com/jquery-2.1.4.min.js
// ==/UserScript==

/*
var e = document.createElement("script");

e.src = 'http://localhost/userScripts/AutoDownloader.user.js';
e.type = "text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
*/

"use strict";

/* OBJECT CREATION START */
var Episode = (function () {
    /**
     * An Episode represents an table row in the current page
     * @param   {Number} res          The resolution used for this episode
     * @param   {String} downloadLink The download link for this episode
     * @param   {Number} seeds        The seed count for this episode
     * @param   {Number} leechers     The leech count for this episode
     * @param   {String} uid          The ID of this episode
     * @param   {Number} resides      The page that this Episode resides in
     * @returns {Object} the proto of itself
     */
    function Episode(res, downloadLink, seeds, leechers, uid, resides) {

        if (typeof res !== "number") {
            throw "res must be a number";
        }

        if (typeof downloadLink !== "string") {
            throw "downloadLink must be a string";
        }

        if (typeof seeds !== "number") {
            throw "seeds must be a number";
        }

        if (typeof leechers !== "number") {
            throw "leechers must be a number";
        }

        if (typeof uid !== "string") {
            throw "uid must be a string";
        }

        if (typeof resides !== "number") {
            throw "resides must be a number";
        }
        var _res = res;

        var _downloadLink = downloadLink;

        var _seeds = seeds;

        var _leechers = leechers;

        var _uid = uid;

        var _resides = resides;

        this.getRes = function () {
            return _res;
        };

        this.getDownloadLink = function () {
            return _downloadLink;
        };

        this.getSeeds = function () {
            return _seeds;
        };

        this.getLeechers = function () {
            return _leechers;
        };

        this.getUid = function () {
            return _uid;
        };

        this.getResides = function () {
            return _resides;
        };
        return this;
    }
    Episode.prototype = {
        constructor: Episode
    }
    return Episode;
}());

var Anime = (function () {

    var currentAnime = null;
    var currentSubber = null;
    /**
     * Array of Episode Objects
     */
    var eps = [];

    /**
     * Set the current Anime name
     * @param {String} anime The of the Anime
     */
    var setCurrentAnime = function (anime) {
        currentAnime = anime;
    };

    /**
     * Set the name of the current subber
     * @param {String} sub Name of the current subber
     */
    var setCurrentSubber = function (sub) {
        currentSubber = sub;
    };

    /**
     * Get the current Subber
     * @returns {String} The name of the Subber for this anime
     */
    var getCurrentSubber = function () {
        return currentSubber;
    };

    /**
     * Get the current anime name
     * @returns {String} Name of the anime
     */
    var getCurrentAnime = function () {
        return currentAnime;
    };


    /**
     * Get the avrage seeds for a specified res
     * @param   {Number} res The res to get the avg seeds for
     * @returns {Number} The avg seeds
     */
    var avgSeedsForRes = function (res) {
        if (typeof res !== "number") {
            throw "res Must be an int";
        }
        var seedCount = 0;
        if (getamountOfEpsFromRes(res) === 0) {
            return 0;
        }
        for (var i = 0; i < eps.length; i++) {
            var currentEp = eps[i];
            if (currentEp.getRes() === res) {
                seedCount += currentEp.getSeeds();
            }
        }
        return Math.round(seedCount = seedCount / getamountOfEpsFromRes(res));
    };

    /**
     * Get the avrage leechers for a specified res
     * @param   {Number} res The res to get the avg seeds for
     * @returns {Number} The avg leechers
     */
    var avgPeersForRes = function (res) {
        if (typeof res !== "number") {
            throw "res Must be an int";
        }
        var leechCount = 0;
        if (getamountOfEpsFromRes(res) === 0) {
            return 0;
        }
        for (var i = 0; i < eps.length; i++) {
            var currentEp = eps[i];
            if (currentEp.getRes() === res) {
                leechCount += currentEp.getLeechers();
            }
        }
        return Math.round(leechCount = leechCount / getamountOfEpsFromRes(res));
    };

    /**
     * Get the total amount of eps for a res
     * @param   {Number} res res
     * @returns {Number} The amount of eps for the res
     */
    var getamountOfEpsFromRes = function (res) {
        if (typeof res !== "number") {
            throw "res must be of type 'number'";
        }
        return getEpsForRes(res).length;
    };


    var _isjQueryObject = function (obj) {
        if (obj instanceof jQuery || 'jquery' in Object(obj)) {
            return true;
        } else {
            return false;
        }
    };

    /**
     * Add Episodes to the array
     * @param {Episode} ep The Anime object to add
     */
    var addEps = function (ep) {
        if (typeof ep !== "object" && !ep instanceof Episode) {
            throw "addEps must take an Episode object";
        }
        eps.push(ep);
    };


    /**
     * Add an array of Episode object to the Anime object
     * @param {Array} episode Array of Episode objects to add
     */
    var addAllEps = function (episode) {
        for (var i = 0; i < episode.length; i++) {
            var currEp = episode[i];
            if (typeof currEp !== "object" && !currEp instanceof Episode) {
                throw "addEps must take an Episode object";
            }
            eps.push(currEp);
        }
    };

    /**
     * Get the Anime objects for a specified res
     * @param   {Number}  res res to use
     * @returns {Episode} Array of Episodes that match the specified res
     */
    var getEpsForRes = function (res) {
        if (typeof res !== "number") {
            throw "res Must be an int";
        }
        var arrayOfEps = [];
        for (var i = 0; i < eps.length; i++) {
            var currentEp = eps[i];
            if (currentEp.getRes() === res) {
                arrayOfEps.push(currentEp);
            }
        }
        return arrayOfEps;
    };

    /**
     * Given a JQuery object that represents a "tr" of the table, this will return that Episode's UID
     * @param   {Object} obj The Jquery representation of a tr (table row)
     * @returns {String} The UID of that Episode
     */
    var getUidFromJqueryObject = function (obj) {
        if (!_isjQueryObject(obj)) {
            throw "Object must be of type 'Jquery'";
        }
        if (obj.is("tr")) {
            var anchor = (function () {
                var tableRows = obj.find("td.tlistdownload > a");
                if (tableRows.length > 1) {
                    throw "Object must be unique";
                }
                return _getUidFromAnchor(tableRows.get(0))
            }());
        } else {
            return null;
        }
        return anchor;
    };

    /**
     * Get the Episode from a given anchor tag
     * @param   {Object}  anchor Jquery or pure JS anchor dom element
     * @returns {Episode} The eipside that matches the Anchor
     */
    var getEpisodeFromAnchor = function (anchor) {
        var link = (function () {
            if (_isjQueryObject(anchor)) {
                return anchor.get(0);
            }
            return anchor;
        }());
        var uid = _getUidFromAnchor(link);
        return getEpisodeFromUid(uid);
    };

    /**
     * Get the Episode object given a UID
     * @param   {String}  uid The Episode UID
     * @returns {Episode} The Episode that matches the UID
     */
    var getEpisodeFromUid = function (uid) {
        if (typeof uid !== "string") {
            throw "uid must be of type String";
        }
        for (var i = 0; i < eps.length; i++) {
            var currentEp = eps[i];
            if (currentEp.getUid() === uid) {
                return currentEp;
            }
        }
        return null;
    };

    /**
     * Get an array of Episodes that from the page that it came from
     * @param   {Number}  resides The page where the Episode originated from
     * @param   {Boolean} exclude Match this page only, or exculde this page and return all other objects. if true, will return all Episodes that are not of the passed in page
     * @returns {Episode} Array of Episodes
     */
    var getEpisodesFromResidence = function (resides, exclude) {
        if (typeof resides !== "number") {
            throw "resides must be a number";
        }
        var arrayOfEps = [];
        for (var i = 0; i < eps.length; i++) {
            var currentEp = eps[i];
            if (exclude === true) {
                if (currentEp.getResides() !== resides) {
                    arrayOfEps.push(currentEp);
                }
            } else {
                if (currentEp.getResides() === resides) {
                    arrayOfEps.push(currentEp);
                }
            }
        }
        return arrayOfEps;
    };

    /**
     * Get the UID from an anchor tag
     * @param   {Object} anchor Dom element of an Anchor
     * @returns {String} The UID
     */
    var _getUidFromAnchor = function (anchor) {
        return anchor.href.split("=").pop();
    };

    /**
     * Remove an object from an array
     * @param {Array}  arr Array to search in
     * @param {Object} obj Object to remove
     */
    var _removeObjectFromArray = function (arr, obj) {
        var i = arr.length;
        while (i--) {
            if (arr[i] === obj) {
                arr.splice(i, 1);
            }
        }
    }

    /**
     * Get an array of all the pages avalible in the Anime (tables)
     * @returns {Array} Array of URLS
     */
    var getPageUrls = function () {
        var urls = [];
        $.each($('div.pages').filter(function (i) {
            return i === 0;
        }).find('a'), function (k, v) {
            urls.push(this.href);
        });
        return urls;
    };

    /**
     * Remove an episode from the Episode array based on UI
     * @param {String} uid the UID
     */
    var removeEpisodesFromUid = function (uid) {
        var episodes = getEpisodeFromUid(uid);
        for (var i = 0; i < episodes.length; i++) {
            var currentEp = episodes[i];
            _removeObjectFromArray(eps, currentEp);
        }
    };

    /**
     * Remove all episodes that match a page number
     * @param {Number}  resides The page number
     * @param {Boolean} exclude if true, this will remove all Episode objects that do not match the passed in page number. if false, removes all episodes that match the passed in page number
     */
    var removeEpisodesFromResidence = function (resides, exclude) {
        if (typeof exclude !== "boolean") {
            throw "excluse must be true or false";
        }
        var episodes = getEpisodesFromResidence(resides, exclude);
        for (var i = 0; i < episodes.length; i++) {
            var currentEp = episodes[i];
            _removeObjectFromArray(eps, currentEp);
        }
    };

    var getAmountOfEps = function () {
        return eps.length;
    };


    return {
        setCurrentAnime: setCurrentAnime,
        getCurrentAnime: getCurrentAnime,
        addEps: addEps,
        getEpsForRes: getEpsForRes,
        getamountOfEpsFromRes: getamountOfEpsFromRes,
        setCurrentSubber: setCurrentSubber,
        getCurrentSubber: getCurrentSubber,
        avgSeedsForRes: avgSeedsForRes,
        getUidFromJqueryObject: getUidFromJqueryObject,
        getEpisodeFromUid: getEpisodeFromUid,
        getEpisodeFromAnchor: getEpisodeFromAnchor,
        getPageUrls: getPageUrls,
        addAllEps: addAllEps,
        getEpisodesFromResidence: getEpisodesFromResidence,
        removeEpisodesFromUid: removeEpisodesFromUid,
        removeEpisodesFromResidence: removeEpisodesFromResidence,
        avgPeersForRes: avgPeersForRes,
        getAmountOfEps: getAmountOfEps
    };
}());


/** Utility functions ***/
var Utils = (function () {
    /**
     * Display a message to the use (built in alert)
     * @param   {String}  message    Message to show
     * @param   {Boolean} confirmMsg If true, message will be a confirm alert (yes or no)
     * @returns {Boolean} Returns true if confirmMsg is false, else returns true or false depending on the user selection
     */
    var displayMessage = function (message, confirmMsg) {
        if (typeof confirmMsg === "boolean") {
            if (confirmMsg === true) {
                return confirm(message);
            }
        }
        alert(message);
        return true;
    };

    /**
     * Disable the given button
     * @param {Object} button Jquery object of the button
     */
    var disableButton = function (button) {
        button.prop('disabled', true);
    };

    /**
     * Enable the given button
     * @param {Object} button Jquery object of the button
     */
    var enableButton = function (button) {
        button.prop('disabled', false);
    };

    /**
     * Do the downloads
     * @param {Object} event The event to decide if it is a download all or a downlaod selection (to make this method more abstract)
     */
    var doDownloads = function (event) {
        var type = $(event.target).data("type");
        var amountOfAnime;
        var collectionOfAnime;
        var userSelect;
        var _minSeeds = Options.Seeds.minSeeds === -1 ? 0 : Options.Seeds.minSeeds;
        if (type === "downloadAll") {
            var selectedRes = parseInt($("#downloadRes").val());
            amountOfAnime = Anime.getamountOfEpsFromRes(selectedRes);
            collectionOfAnime = Anime.getEpsForRes(selectedRes);
            userSelect = displayMessage("You are about to download " + amountOfAnime + " eps.\n\
This will cause " + amountOfAnime + " download pop-ups\nAre you sure you want to continue?\nIf there are a lot of eps, your browser might stop responding for a while.\
This is normal\nIf you are on Google Chrome, it will ask you to allow multiple-downloads\n\nYour min seeds restriction is: " + _minSeeds + "\
 This will not be reflected in this message download count, but all episodes with this restriction will be skipped", true);
        } else if (type === "downloadSelected") {
            amountOfAnime = $(".checkboxes:checked").length;
            collectionOfAnime = [];
            $.each($('.checkboxes:checked').prev('a'), function (k, v) {
                var episode = Anime.getEpisodeFromAnchor(this);
                collectionOfAnime.push(episode);
            });
            userSelect = displayMessage("You are about to download " + amountOfAnime + " items. if there are a lot of itmes, it might make your browser stop responding for a while This is normal\n\
            Chrome will ask you to accept multiple downloads ", true);
        } else {
            throw "Unexpected Error";
        }
        if (userSelect === false) {
            return;
        }
        for (var i = 0; i < collectionOfAnime.length; i++) {
            var currentEp = collectionOfAnime[i];
            if (_minSeedsSet() && type !== "downloadSelected") {
                if (currentEp.getSeeds() < _minSeeds) {
                    continue;
                }
            }
            var currentDownloadLink = currentEp.getDownloadLink();
            var link = document.createElement("a");
            link.download = "";
            link.href = currentDownloadLink;
            link.click();
        }
    };

    /**
     * Returns if the checkbox is checked
     * @param   {Object}  checkbox The checkbox
     * @returns {Boolean} If ehcked or not
     */
    var checkBoxValid = function (checkbox) {
        return checkbox.is(":checked");
    };

    var _minSeedsSet = function () {
        return Options.Seeds.minSeeds !== -1 ? true : false;
        //(typeof Options.Seeds.minSeeds !== "undefined") ? 0: Options.Seeds.minSeeds;
    }

    /**
     * Build the download infomation table
     * @returns {String} The html of the built table
     */
    var buildTable = function () {
        var html = "";
        html += "<table style='width: 100%' id='info'>";

        html += "<caption>Download infomation</caption>";

        html += "<thead>";
        html += "<tr>";
        html += "<th>resolution</th>";
        html += "<th>Episode count</th>";
        html += "<th>Average seeds</th>";
        html += "<th>Average leechers</th>";
        html += "</tr>";
        html += "</thead>";

        html += "<tbody>";
        html += "<tr>";
        html += "<td>1080p</td>";
        html += "<td>" + Anime.getamountOfEpsFromRes(1080) + "</td>";
        html += "<td>" + Anime.avgSeedsForRes(1080) + "</td>";
        html += "<td>" + Anime.avgPeersForRes(1080) + "</td>";
        html += "</tr>";

        html += "<tr>";
        html += "<td>720p</td>";
        html += "<td>" + Anime.getamountOfEpsFromRes(720) + "</td>";
        html += "<td>" + Anime.avgSeedsForRes(720) + "</td>";
        html += "<td>" + Anime.avgPeersForRes(720) + "</td>";
        html += "</tr>";


        html += "<tr>";
        html += "<td>480p</td>";
        html += "<td>" + Anime.getamountOfEpsFromRes(480) + "</td>";
        html += "<td>" + Anime.avgSeedsForRes(480) + "</td>";
        html += "<td>" + Anime.avgPeersForRes(480) + "</td>";
        html += "</tr>";

        html += "<tr>";
        html += "<td>360p</td>";
        html += "<td>" + Anime.getamountOfEpsFromRes(360) + "</td>";
        html += "<td>" + Anime.avgSeedsForRes(360) + "</td>";
        html += "<td>" + Anime.avgPeersForRes(360) + "</td>";
        html += "</tr>";
        html += "</tbody>";
        html += "</table>";


        return html;
    };

    /**
     * Return the current page offset (what table page you are on)
     * @returns {Number} The offset
     */
    var getCurrentPageOffset = function () {
        return parseInt((typeof QueryString.offset === "undefined") ? 1 : QueryString.offset);
    };

    /**
     * Returns true of false if you can support HTML5 storeag
     * @returns {Boolean}
     */
    var html5StoreSupport = function () {
        try {
            return 'localStorage' in window && window['localStorage'] !== null;
        } catch (e) {
            return false;
        }
    };

    var injectCss = function (css) {
        if (_isUrl(css)) {
            $("<link>").prop({
                "type": "text/css",
                "rel": "stylesheet"
            }).attr("href", css).appendTo("head");
        } else {
            $("<style>").prop("type", "text/css").html(css).appendTo("head");
        }
    };

    var _isUrl = function (url) {
        var matcher = new RegExp(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/);
        return matcher.test(url);
    };

    return {
        displayMessage: displayMessage,
        disableButton: disableButton,
        enableButton: enableButton,
        doDownloads: doDownloads,
        checkBoxValid: checkBoxValid,
        buildTable: buildTable,
        getCurrentPageOffset: getCurrentPageOffset,
        html5StoreSupport: html5StoreSupport,
        injectCss: injectCss
    };
}());

var DataParser = (function () {
    /**l
     * Parses a table and returns an array of Episodes from it
     * @param   {Object}  table Jquery representation of the anime table
     * @returns {Episode} Array of Episodes
     */
    var parseTable = function (table, currentPage) {
        var trRow = table.find("img[src='http://files.nyaa.se/www-37.png']").closest("tr");

        var eps = [];
        $(trRow).filter(function (e) {
            return ($(this).children('td:nth-child(2)').text().indexOf('1080p') > -1 || $(this).children('td:nth-child(2)').text().indexOf('1920x1080') > -1);
        }).each(function (k, v) {
            var currentDownloadLink = $(this).find('td:nth-child(3) >a').attr('href');
            var seeds = (isNaN(parseInt($(this).find("td.tlistsn").text()))) ? 0 : parseInt($(this).find("td.tlistsn").text());
            var leech = (isNaN(parseInt($(this).find("td.tlistln").text()))) ? 0 : parseInt($(this).find("td.tlistln").text());
            var uid = Anime.getUidFromJqueryObject($(this));
            eps.push(new Episode(1080, currentDownloadLink, seeds, leech, uid, currentPage));
        });

        $(trRow).filter(function (e) {
            return ($(this).children("td:nth-child(2)").text().indexOf("720p") > -1 || $(this).children("td:nth-child(2)").text().indexOf("1280x720")) > -1;
        }).each(function (k, v) {
            var currentDownloadLink = $(this).find('td:nth-child(3) >a').attr('href');
            var seeds = (isNaN(parseInt($(this).find("td.tlistsn").text()))) ? 0 : parseInt($(this).find("td.tlistsn").text());
            var leech = (isNaN(parseInt($(this).find("td.tlistln").text()))) ? 0 : parseInt($(this).find("td.tlistln").text());
            var uid = Anime.getUidFromJqueryObject($(this));
            eps.push(new Episode(720, currentDownloadLink, seeds, leech, uid, currentPage));
        });

        $(trRow).filter(function (e) {
            return ($(this).children("td:nth-child(2)").text().indexOf("480p") > -1 || $(this).children("td:nth-child(2)").text().indexOf("640x480") > -1);
        }).each(function (k, v) {
            var currentDownloadLink = $(this).find('td:nth-child(3) >a').attr('href');
            var seeds = (isNaN(parseInt($(this).find("td.tlistsn").text()))) ? 0 : parseInt($(this).find("td.tlistsn").text());
            var leech = (isNaN(parseInt($(this).find("td.tlistln").text()))) ? 0 : parseInt($(this).find("td.tlistln").text());
            var uid = Anime.getUidFromJqueryObject($(this));
            eps.push(new Episode(480, currentDownloadLink, seeds, leech, uid, currentPage));
        });

        $(trRow).filter(function (e) {
            return ($(this).children("td:nth-child(2)").text().indexOf("360p") > -1 || $(this).children("td:nth-child(2)").text().indexOf("640×360") > -1);
        }).each(function (k, v) {
            var currentDownloadLink = $(this).find('td:nth-child(3) >a').attr('href');
            var seeds = (isNaN(parseInt($(this).find("td.tlistsn").text()))) ? 0 : parseInt($(this).find("td.tlistsn").text());
            var leech = (isNaN(parseInt($(this).find("td.tlistln").text()))) ? 0 : parseInt($(this).find("td.tlistln").text());
            var uid = Anime.getUidFromJqueryObject($(this));
            eps.push(new Episode(360, currentDownloadLink, seeds, leech, uid, currentPage));
        });

        return eps;
    };
    return {
        parseTable: parseTable,
    };
}());

var QueryString = function () {
    var query_string = {};
    var query = window.location.search.substring(1);
    var vars = query.split('&');
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split('=');
        if (typeof query_string[pair[0]] === 'undefined') {
            query_string[pair[0]] = pair[1];
        } else if (typeof query_string[pair[0]] === 'string') {
            var arr = [
        query_string[pair[0]],
        pair[1]
      ];
            query_string[pair[0]] = arr;
        } else {
            query_string[pair[0]].push(pair[1]);
        }
    }
    return query_string;
}();

var Options = (function () {
    var Seeds = {};
    Object.defineProperty(Seeds, "minSeeds", {
        enumerable: true,
        set: function (seeds) {
            if (typeof seeds !== "number") {
                throw "seeds must be a number";
            }
            this._minSeeds = seeds;
            if (Utils.html5StoreSupport() === true) { // also set it on the local DB
                if (this._minSeeds === -1) {
                    Localstore.removeMinSeedsFromStore();
                } else {
                    Localstore.setMinSeedsFromStore(this._minSeeds);
                }
            }
        },
        get: function () {
            return typeof this._minSeeds === "undefined" ? -1 : this._minSeeds;
        }

    });

    return {
        Seeds: Seeds
    }
}());

//Local storeage object
var Localstore = {
    getMinSeedsFromStore: function () {
        return localStorage.getItem("minSeeds");
    },
    setMinSeedsFromStore: function (seeds) {
        localStorage.setItem('minSeeds', seeds);
    },
    removeMinSeedsFromStore: function () {
        localStorage.removeItem("minSeeds");
    }
};

// Download fix for firefox
HTMLElement.prototype.click = function () {
    var evt = this.ownerDocument.createEvent('MouseEvents');
    evt.initMouseEvent('click', true, true, this.ownerDocument.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
    this.dispatchEvent(evt);
};

/* OBJECT CREATION END */

$(document).ready(function () {
    init(); // init the pannel and set up objects and listeners
    if (Utils.checkBoxValid($(".checkboxes"))) { // TODO: Refactor this statement!
        Utils.enableButton($("#downloadCustomButton"));
    } else {
        Utils.disableButton($("#downloadCustomButton"));
    }
});

function init() {
    var doDownload = true;
    setAnimeObj();
    buildUi();
    bindListeners();
    if (Utils.html5StoreSupport()) {
        setOptionsFromLocalStor();
    }

    function setAnimeObj() {
        // Set currentAnime
        if (QueryString.term !== "") {
            Anime.setCurrentAnime(decodeURIComponent(QueryString.term).split("+").join(" "));
        } else {
            Anime.setCurrentAnime("Unknown");
        }

        // set subber
        Anime.setCurrentSubber($(".notice > a > span").html());

        // Set eps

        var eps = DataParser.parseTable($("table.tlist"), Utils.getCurrentPageOffset());

        Anime.addAllEps(eps);

    }

    function buildUi() {
        makeStyles();
        buildPanel();
        makeCheckBoxes();

        function makeStyles() {
            var styles = "";
            styles += ".panel{background-color: #fff; border: 1px solid transparent; border-radius: 4px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); margin-bottom: 20px;}";
            styles += ".panel-success {border-color: #d6e9c6;}";
            styles += ".panel-heading{   border-bottom: 1px solid transparent; border-top-left-radius: 3px; border-top-right-radius: 3px; padding: 4px 15px; text-align:center}";
            styles += ".panel-success > .panel-heading {background-color: #dff0d8; border-color: #d6e9c6; color: #3c763d;}";
            styles += ".panel-success > .panel-footer + .panel-collapse > .panel-body {border-bottom-color: #d6e9c6;}";
            styles += ".panel-footer {background-color: #f5f5f5; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top: 1px solid #ddd; padding: 10px 15px;}";
            styles += ".panel-title {color: inherit; margin-bottom: 0; margin-top: 0; padding: 6px;}";
            styles += ".panel-body {padding: 15px;}";
            styles += ".avgSeeds{floar:left; padding-right:10px; color:#3c763d;}";
            styles += ".checkboxes{left:1px; margin:0; padding:0; position: relative; top: 1px;}";

            styles += "#info, #info th, #info td {border: 1px solid black;border-collapse: collapse;}";
            styles += "#info th, #info td {padding: 5px;text-align: left;}";
            styles += "label[for='MinSeeds']{ display: block; margin-top: 10px;}";
            styles += "#SaveMinSeeds{margin-left:5px;}"

            styles += "#downloadCustomButton{float:right;}";
            Utils.injectCss(styles);

        }

        function buildPanel() {
            var html = "";
            html += '<div class="panel panel-success" style="margin-left: 9px; margin-top: 19px; width: 851px;">';

            html += '<div class="panel-heading">';
            html += '<h3 id="panel-title" class="panel-title"></h3>';
            html += "</div>";

            html += '<div class="panel-body" id="pannelContent"></div>';
            html += '<div class="panel-footer" id="panelFooter">';

            html += '</div>';

            html += '</div>';
            $(".content > .notice").after(html);
            buildPanelContent();

            function buildPanelContent() {
                var html = "";
                html += "<div>";
                $("#panel-title").html("<span> Download \"" + Anime.getCurrentAnime() + " (" + Anime.getCurrentSubber() + ")\"</span>");
                if (Anime.getAmountOfEps() === 0) {
                    html += "<span> No translated anime found or error occured</span>";
                    html += "</div>";
                    $("#pannelContent").html(html);
                    doDownload = false;
                    return;
                }
                html += "<span>Pick a resolution: </span>";
                html += "<select style=\"margin-right:5px;\" id=\"downloadRes\">";
                if (Anime.getamountOfEpsFromRes(1080) >= 1) {
                    html += "<option value=\"1080\">1080p</option>";
                }
                if (Anime.getamountOfEpsFromRes(720) >= 1) {
                    html += "<option value=\"720\">720p</option>";
                }
                if (Anime.getamountOfEpsFromRes(480) >= 1) {
                    html += "<option value=\"480\">480p</option>";
                }
                if (Anime.getamountOfEpsFromRes(360) >= 1) {
                    html += "<option value=\"360\">360p</option>";
                }
                html += "</select>";
                html += "<button type=\"button\" data-type='downloadAll' id=\"downloadAll\">Download all</button>";

                html += "<button type='button' id='downloadCustomButton' data-type='downloadSelected' >download your selected items</button>";

                html += "</div>";

                html += "<div id='options'>";


                html += "<label for='crossPage'> include Cross pages</label>";
                html += "<input type='checkbox' id='crossPage' /> ";

                html += "<label for='MinSeeds'>Minimum seeders:</label>";
                html += "<input type='number' min='0' id='MinSeeds' title='Any episode that is below this limit will be excluded from the download but not the infomation table (coming soon).'/>";
                html += "<button type='button' id='SaveMinSeeds'>Save</button>";

                html += "</div>";

                html += "<div id='tableInfo'>";
                html += Utils.buildTable();
                html += "</div>";

                $("#pannelContent").html(html);
            }
        }

        function makeCheckBoxes() {
            $(".tlistdownload > a").after("<input class='checkboxes' type='checkbox'/>");
        }
    }

    function bindListeners() {
        $("#downloadAll").on("click", function (e) {
            if (doDownload === true) {
                Utils.doDownloads(e);
            }
        });

        $("#downloadCustomButton").on("click", function (e) {
            if (doDownload === true) {
                Utils.doDownloads(e);
            }
        });

        $(".checkboxes").on("click", function (e) {
            if (Utils.checkBoxValid($(".checkboxes"))) {
                Utils.enableButton($("#downloadCustomButton"));
            } else {
                Utils.disableButton($("#downloadCustomButton"));
            }
        });

        $("#crossPage").on("click", function (e) {
            if (Utils.checkBoxValid($("#crossPage"))) {
                $("#tableInfo").html("<p>Please wait while we parse each page...</p>");
                $("#crossPage").prop("disabled", true);
                Anime.getPageUrls().reduce(function (prev, cur, index) {
                    return prev.then(function (data) {
                        return $.ajax(cur).then(function (data) {
                            var currentPage = cur.split("=").pop();
                            if (cur.indexOf("offset") === -1) {
                                currentPage = 1;
                            }
                            var table = $(data).find("table.tlist");

                            Anime.addAllEps(DataParser.parseTable(table, parseInt(currentPage)));
                            $("#tableInfo").append("<div>Page " + currentPage + " Done </div>")
                        });
                    })
                }, $().promise()).done(function () {
                    $("#tableInfo").html(Utils.buildTable());
                    $("#crossPage").prop("disabled", false);
                });
            } else { // when un-chekced, clear the Episodes of all eps that are not of the current page
                $("#tableInfo").html("<p>Please wait while we re-calculate the Episodes</p>");
                var currentPage = Utils.getCurrentPageOffset();
                Anime.removeEpisodesFromResidence(currentPage, true);
                $("#tableInfo").html(Utils.buildTable());
            }
        });

        $("#SaveMinSeeds").on("click", function (e) {
            if (parseInt($("#MinSeeds").val()) < 0) {
                alert("number cannot be negative");
                return;
            }
            var value = parseInt($("#MinSeeds").val() === "" ? -1 : $("#MinSeeds").val());
            Options.Seeds.minSeeds = value;
            if (value === -1) {
                alert("Minimum seeds have been cleared");
            } else {
                alert("Minimum seeds now set to: " + value);
            }
        });
    }

    function setOptionsFromLocalStor() {
        // Min seeds
        if (Localstore.getMinSeedsFromStore() !== null) {
            var minSeeds = parseInt(Localstore.getMinSeedsFromStore());
            Options.Seeds.minSeeds = minSeeds;
            $("#MinSeeds").val(minSeeds);
        }
    }
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址