您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Batch download torrents from nyaa.se
当前为
- // ==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=*
- // @include *://www.nyaa.eu/?page=search&cats=*&filter=*&term=*&user=*
- // @include *://www.nyaa.eu/?page=search&filter=*&term=*&user=*
- // @include *://www.nyaa.eu/?page=search&term=*&user=*
- // @version 5.4.5
- // @icon https://i.imgur.com/nx5ejHb.png
- // @license MIT
- // @run-at document-idle
- // @grant none
- // @require https://code.jquery.com/jquery-2.2.3.min.js
- // @require https://greasyfork.org/scripts/19117-jsutils/code/JsUtils.js
- // ==/UserScript==
- /*
- var e = document.createElement("script");
- e.src = 'http://localhost/userScripts/AutoDownloader.user.js';
- e.type = "application/javascript;version=1.7";
- 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
- * @param {String} title The title of the episode
- * @param {Number} size The size in MB of the episode
- * @returns {Object} the proto of itself
- */
- function Episode(res, downloadLink, seeds, leechers, uid, resides, title, size) {
- 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';
- }
- if (typeof title !== 'string') {
- throw 'Title must be a string';
- }
- if (typeof size !== 'number') {
- throw 'size must be a number';
- }
- var _res = res;
- var _downloadLink = downloadLink;
- var _seeds = seeds;
- var _leechers = leechers;
- var _uid = uid;
- var _resides = resides;
- var _title = title;
- var _size = size;
- 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;
- };
- this.getTitle = function () {
- return _title;
- };
- this.getSize = function () {
- return _size;
- };
- return this;
- }
- Episode.prototype = {
- constructor: Episode
- };
- return Episode;
- }());
- var Anime = (function () {
- var currentAnime = null;
- var currentSubber = null;
- var _AbstractEps = (function () {
- /**
- * Array of Episode Objects
- */
- var eps = [
- ];
- var abstractGetEps = function (skipSeedLimit) {
- if (typeof skipSeedLimit !== 'boolean') {
- throw 'skipOptions must be true or false';
- }
- var minSeeds = Options.Seeds.minSeeds;
- if (minSeeds > -1 && skipSeedLimit == false) {
- var arrayOfEps = [
- ];
- for (let i = 0, len = eps.length; i < len; i++) {
- var currentEp = eps[i];
- if (currentEp.getSeeds() < minSeeds) {
- continue;
- }
- arrayOfEps.push(currentEp);
- }
- return arrayOfEps;
- } else {
- return eps;
- }
- };
- var addEp = function (ep) {
- if (!(ep instanceof Episode)) {
- throw 'addEp must take an Episode object';
- }
- if (_validRes(ep.getRes()) === false) {
- throw new TypeError('The Episode supplied does not have a valid resolution');
- }
- for (let i = 0, len = eps.length; i < len; i++) {
- var epi = eps[i];
- if (Utils.deepEquals(epi, ep)) {
- console.warn('The episode supplied already exsists, this episode has been ignored');
- return;
- }
- }
- eps.push(ep);
- };
- var removeEpisodeFromAnime = function (obj) {
- var arr = eps;
- let i = arr.length;
- while (i--) {
- if (arr[i] === obj) {
- arr.splice(i, 1);
- }
- }
- };
- return {
- abstractGetEps: abstractGetEps,
- addEp: addEp,
- removeEpisodeFromAnime: removeEpisodeFromAnime
- };
- }());
- /**
- * Array of available resolutions on the page
- */
- var availableRes = [
- ];
- /**
- * Array of supported resolutions for this program
- */
- var supportedRes = [
- {
- 'id': 1,
- 'res': 1080,
- 'fullRes': '1920x1080'
- },
- {
- 'id': 2,
- 'res': 720,
- 'fullRes': '1280x720'
- },
- {
- 'id': 3,
- 'res': 480,
- 'fullRes': '640x480'
- },
- {
- 'id': 4,
- 'res': 360,
- 'fullRes': '640x360'
- }
- ];
- /**
- * Set the current Anime name
- * @param {String} anime The of the Anime
- */
- var setCurrentAnime = function (anime) {
- if (anime === '*') {
- anime = 'Everything';
- }
- 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;
- };
- var getSupportedRes = function () {
- return supportedRes;
- };
- var addSupportedRes = function (ResObj) {
- if (typeof ResObj !== 'object') {
- throw 'ResObj must be a object';
- }
- supportedRes.push(ResObj);
- };
- var getAvailableResolutions = function () {
- return availableRes;
- };
- var addAvailableResolutions = function (res, fullRes) {
- if (typeof res !== 'number') {
- throw 'res must be of type number';
- }
- if (typeof fullRes !== 'string' && fullRes !== null) {
- throw 'Full res must be a string or null';
- }
- if (_resExists(res)) {
- return;
- }
- availableRes.push({
- 'res': res,
- 'fullRes': fullRes
- });
- };
- var removeAvailableResolutions = function (resToRemove) {
- if (typeof resToRemove !== 'number' && typeof resToRemove !== 'string') {
- throw 'the res to remove can only be a number or string';
- }
- for (let i = 0; i < availableRes.length; i++) {
- var currentRes = availableRes[i];
- for (var res in currentRes) {
- if (currentRes.hasOwnProperty(res)) {
- var localRes = currentRes[res];
- if (localRes === resToRemove) {
- availableRes._remove_(currentRes);
- }
- }
- }
- }
- };
- var _resExists = function (_res) {
- for (let i = 0; i < availableRes.length; i++) {
- var currentRes = availableRes[i];
- for (var res in currentRes) {
- if (currentRes.hasOwnProperty(res)) {
- var localRes = currentRes[res];
- if (localRes === _res) {
- return true;
- }
- }
- }
- }
- return false;
- };
- /**
- * 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, skipSeedLimit) {
- if (typeof res !== 'number') {
- throw 'res Must be an number';
- }
- if (typeof skipSeedLimit !== 'boolean') {
- throw 'skipSeedLimit Must be an boolean';
- }
- var seedCount = 0;
- if (getamountOfEpsFromRes(res, skipSeedLimit) === 0) {
- return 0;
- }
- var eps = _AbstractEps.abstractGetEps(skipSeedLimit);
- for (let i = 0, len = eps.length; i < len; i++) {
- var currentEp = eps[i];
- if (currentEp.getRes() === res) {
- seedCount += currentEp.getSeeds();
- }
- }
- return Math.round(seedCount = seedCount / getamountOfEpsFromRes(res, skipSeedLimit));
- };
- /**
- * 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, skipSeedLimit) {
- if (typeof res !== 'number') {
- throw 'res Must be an number';
- }
- if (typeof skipSeedLimit !== 'boolean') {
- throw 'skipSeedLimit Must be an boolean';
- }
- var leechCount = 0;
- if (getamountOfEpsFromRes(res, skipSeedLimit) === 0) {
- return 0;
- }
- var eps = _AbstractEps.abstractGetEps(skipSeedLimit);
- for (let i = 0, len = eps.length; i < len; i++) {
- var currentEp = eps[i];
- if (currentEp.getRes() === res) {
- leechCount += currentEp.getLeechers();
- }
- }
- return Math.round(leechCount = leechCount / getamountOfEpsFromRes(res, skipSeedLimit));
- };
- var getTotalSizeForRes = function (res, skipSeedLimit, decimals) {
- if (typeof res !== 'number') {
- throw 'res Must be an number';
- }
- if (typeof skipSeedLimit !== 'boolean') {
- throw 'skipSeedLimit Must be an boolean';
- }
- var eps = getEpsForRes(res, skipSeedLimit);
- return Utils.getHumanReadableSize(eps, decimals);
- };
- var getTotalSizeFromEps = function (eps, decimals) {
- if (!Array.isArray(eps) && !(eps instanceof Episode)) {
- throw 'eps Must be an array or a single Episode';
- }
- return Utils.getHumanReadableSize(eps, decimals);
- };
- /**
- * 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, skipSeedLimit) {
- if (typeof res !== 'number') {
- throw 'res must be of type \'number\'';
- }
- return getEpsForRes(res, skipSeedLimit).length;
- };
- /**
- * Add Episodes to the array
- * @param {Episode} ep The Anime object to add
- */
- var addEps = function (ep) {
- _AbstractEps.addEp(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 (let i = 0; i < episode.length; i++) {
- _AbstractEps.addEp(episode[i]);
- }
- };
- var _validRes = function (res) {
- return _resExists(res); // if the res exists, then it's valid
- };
- /**
- * 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, skipSeedLimit) {
- if (typeof res !== 'number') {
- throw 'res Must be an int';
- }
- var arrayOfEps = [
- ];
- var eps = _AbstractEps.abstractGetEps(skipSeedLimit);
- for (let i = 0, len = eps.length; i < len; 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 (!Utils.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));
- }());
- return anchor;
- }
- return null;
- };
- /**
- * Get the Episode from a given anchor tag or url
- * @param {Object} anchor Jquery or pure JS anchor dom element or URL String
- * @returns {Episode} The eipside that matches the Anchor
- */
- var getEpisodeFromAnchor = function (anchor) {
- var link = (function () {
- if (Utils.isjQueryObject(anchor)) {
- return anchor.get(0);
- }
- return anchor;
- }());
- var uid = _getUidFromAnchor(link);
- return getEpisodeFromUid(uid, true);
- };
- /**
- * 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, skipSeedLimit) {
- if (typeof uid !== 'string') {
- throw 'uid must be of type String';
- }
- var eps = _AbstractEps.abstractGetEps(skipSeedLimit);
- for (let i = 0, len = eps.length; i < len; 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, skipSeedLimit) {
- if (typeof resides !== 'number') {
- throw 'resides must be a number';
- }
- var arrayOfEps = [
- ];
- var eps = _AbstractEps.abstractGetEps(skipSeedLimit);
- for (let i = 0, len = eps.length; i < len; 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) {
- if (typeof anchor === 'string') {
- return anchor.split('=').pop();
- }
- return anchor.href.split('=').pop();
- };
- /**
- * Get an array of all the pages avalible in the Anime (tables)
- * @returns {Array} Array of URLS
- */
- var getPageUrls = function (asHref, page) {
- if (typeof page === 'undefined') {
- page = $('div.pages');
- }
- if (typeof asHref !== 'boolean') {
- throw 'asHref must be a boolean';
- }
- if (asHref === false) {
- return $(page).filter(function (i) {
- return i === 0;
- });
- }
- var urls = [
- ];
- $.each(page.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 episode = getEpisodeFromUid(uid, true);
- _AbstractEps.removeEpisodeFromAnime(episode);
- };
- /**
- * 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 eps = getEpisodesFromResidence(resides, exclude, true);
- for (let i = 0, len = eps.length; i < len; i++) {
- var currentEp = eps[i];
- _AbstractEps.removeEpisodeFromAnime(currentEp);
- }
- };
- var getAmountOfEps = function () {
- return _AbstractEps.abstractGetEps(true).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,
- addAvailableResolutions: addAvailableResolutions,
- getAvailableResolutions: getAvailableResolutions,
- removeAvailableResolutions: removeAvailableResolutions,
- getSupportedRes: getSupportedRes,
- addSupportedRes: addSupportedRes,
- getTotalSizeForRes: getTotalSizeForRes,
- getTotalSizeFromEps: getTotalSizeFromEps
- };
- }());
- /** Utility functions ***/
- var Utils = (function () {
- var sortSelect = function (selElem) {
- var tmpAry = [];
- for (let i = 0, length = selElem.options.length; i < length; i++) {
- tmpAry[i] = [];
- tmpAry[i][0] = selElem.options[i].text;
- tmpAry[i][1] = selElem.options[i].dataset.url;
- }
- tmpAry.sort(function (a, b) {
- return a[0].toUpperCase().localeCompare(b[0].toUpperCase());
- });
- selElem.innerHTML = "";
- for (let i = 0, len = tmpAry.length; i < len; i++) {
- var op = new Option(tmpAry[i][0]);
- op.dataset.url = tmpAry[i][1];
- selElem.options[i] = op;
- }
- };
- let isjQueryObject = function (obj) {
- return ObjectUtil.isjQuery(obj);
- };
- /**
- * 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) {
- $('#crossPage').prop('disabled', true);
- var type = $(event.target).data('type');
- var amountOfAnime;
- var collectionOfAnime;
- var download = false;
- var html = UI.builDownloadAlert(type);
- var urlsToDownload = [
- ];
- $('#alertUser').html(html).slideDown('slow');
- if (type === 'downloadSelected') {
- $.each($('.checkboxes:checked').prev('a'), function (k, v) {
- var ep = Anime.getEpisodeFromAnchor(this);
- urlsToDownload.push(ep.getDownloadLink());
- });
- } else if (type === 'downloadSelects') {
- $.each($('#animeSelection option:selected'), function (k, v) {
- var url = this.dataset.url;
- urlsToDownload.push(url);
- });
- } else {
- var eps = Anime.getEpsForRes(parseInt($('#downloadRes').val()), false);
- for (let i = 0, len = eps.length; i < len; i++) {
- urlsToDownload.push(eps[i].getDownloadLink());
- }
- }
- bindAlertControls();
- function bindAlertControls() {
- $('#alertButtonCancel').on('click', function () {
- $('#alertUser').slideUp('slow');
- $('#crossPage').prop('disabled', false);
- });
- $('#alertButton').on('click', function () {
- doIt(urlsToDownload);
- });
- }
- function doIt(urls) {
- for (let i = 0; i < urls.length; i++) {
- var currentUrl = urls[i];
- var link = document.createElement('a');
- link.href = currentUrl;
- link.download = '';
- link.click();
- link.remove();
- }
- $('#alertUser').slideUp('slow');
- $('#crossPage').prop('disabled', false);
- }
- };
- /**
- * 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;
- };
- /**
- * 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 cleanAvailableResolutions = function () {
- var avRes = ArrayUtils.arrayCopy(Anime.getAvailableResolutions(), true);
- var resLength = avRes.length;
- for (let i = 0, len = avRes.length; i < len; i++) {
- var currentRes = avRes[i].res;
- if (Anime.getamountOfEpsFromRes(currentRes, true) === 0) {
- Anime.removeAvailableResolutions(currentRes);
- }
- }
- };
- var sortAllControls = function () {
- sortSelect(document.getElementById('animeSelection'));
- sortSelect(document.getElementById('downloadRes'));
- $('#info').sortTable(0);
- }
- var reBindSelectFilters = function () {
- $('input[name=\'filterSelect\']').off('change').on('change', handleSelect);
- $('#clearResOptions').off('click').on('click', handleSelect);
- function handleSelect(event) {
- var resTOFilter = $(event.target).data('set')
- $('#selectAnime').html(UI.buildSelect(resTOFilter));
- Utils.sortAllControls();
- var searchApplied = UI.getAppliedSearch();
- if (searchApplied !== '') {
- UI.applySearch(searchApplied);
- }
- reBindSelectFilters();
- }
- };
- var equals = function (episode, toEqual) {
- if (!(episode instanceof Episode && toEqual instanceof Episode)) {
- throw 'both objects must be episodes';
- }
- return episode.getUid() === toEqual.getUid();
- };
- var deepEquals = function (episode, toEqual) {
- if (!(episode instanceof Episode && toEqual instanceof Episode)) {
- throw 'both objects must be episodes';
- }
- for (var methods in episode) {
- if (episode.hasOwnProperty(methods)) {
- if (typeof episode[methods] === 'function') {
- var method = episode[methods];
- var method2 = toEqual[methods];
- if (method.call(this) !== method2.call(this)) {
- return false;
- }
- }
- }
- }
- return true;
- };
- var getHumanReadableSize = function (from, decimals) {
- var bits = 0;
- if (Array.isArray(from)) {
- for (let i = 0; i < from.length; i++) {
- var ep = from[i];
- bits += ep.getSize();
- }
- } else if (typeof from === 'number') {
- bits = from;
- } else {
- bits += from.getSize();
- }
- function formatBytes(bytes, decimals) {
- if (bytes == 0) {
- return '0 Byte';
- }
- var k = 1024;
- var dm = decimals + 1 || 3;
- var sizes = [
- 'Bytes',
- 'KB',
- 'MB',
- 'GB',
- 'TB',
- 'PB',
- 'EB',
- 'ZB',
- 'YB'
- ];
- let i = Math.floor(Math.log(bytes) / Math.log(k));
- return (bytes / Math.pow(k, i)).toPrecision(dm) + ' ' + sizes[i];
- }
- return formatBytes(bits, decimals);
- };
- return {
- disableButton: disableButton,
- enableButton: enableButton,
- doDownloads: doDownloads,
- checkBoxValid: checkBoxValid,
- getCurrentPageOffset: getCurrentPageOffset,
- html5StoreSupport: html5StoreSupport,
- cleanAvailableResolutions: cleanAvailableResolutions,
- sortAllControls: sortAllControls,
- reBindSelectFilters: reBindSelectFilters,
- sortSelect: sortSelect,
- isjQueryObject: isjQueryObject,
- equals: equals,
- deepEquals: deepEquals,
- getHumanReadableSize: getHumanReadableSize
- };
- }());
- var UI = (function () {
- var epsInSelect = [
- ];
- var searchApplied = '';
- /**
- * 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 += '<th>Total size</th>';
- html += '</tr>';
- html += '</thead>';
- html += '<tbody>';
- var allRes = Anime.getAvailableResolutions();
- for (let i = 0; i < allRes.length; i++) {
- var currRes = allRes[i];
- var localRes = currRes.res;
- html += '<tr>';
- html += '<td>' + (localRes === -1 ? 'Others' : localRes + 'p') + '</td>';
- html += '<td>' + Anime.getamountOfEpsFromRes(localRes, true) + '</td>';
- html += '<td>' + Anime.avgSeedsForRes(localRes, true) + '</td>';
- html += '<td>' + Anime.avgPeersForRes(localRes, true) + '</td>';
- html += '<td>' + Anime.getTotalSizeForRes(localRes, true) + ' (aprox)</td>';
- html += '</tr>';
- }
- html += '</tbody>';
- html += '</table>';
- return html;
- };
- var buildDropdownSelections = function () {
- var html = '';
- html += '<select style="margin-right:5px;" id="downloadRes">';
- var allRes = Anime.getAvailableResolutions();
- for (let i = 0; i < allRes.length; i++) {
- var currRes = allRes[i];
- var localRes = currRes.res;
- html += '<option value=' + localRes + '>' + (localRes === -1 ? 'Others' : localRes + 'p') + '</option>';
- }
- html += '</select>';
- return html;
- };
- var builDownloadAlert = function (type) {
- if (typeof type !== 'string') {
- throw 'type must a string';
- }
- var amountOfAnime = 0;
- var selectedRes = parseInt($('#downloadRes').val());
- var res = null;
- var totalSize = null;
- if (type === 'downloadSelected') {
- amountOfAnime = $('.checkboxes:checked').length;
- res = 'custom';
- } else if (type === 'downloadSelects') {
- amountOfAnime = $('#animeSelection option:selected').length;
- totalSize = Utils.getHumanReadableSize((function () {
- var localSize = 0;
- $('#animeSelection option:selected').each(function (k, v) {
- var url = this.dataset.url;
- var epSize = Anime.getEpisodeFromAnchor(url).getSize();
- localSize += epSize;
- });
- return localSize;
- }()));
- res = 'custom';
- } else {
- amountOfAnime = Anime.getamountOfEpsFromRes(selectedRes, false);
- res = selectedRes === -1 ? 'Others' : selectedRes + 'p';
- totalSize = Anime.getTotalSizeForRes(parseInt(res), false);
- }
- var seedLimit = Options.Seeds.minSeeds === -1 ? 'None' : Options.Seeds.minSeeds;
- var html = '';
- html += '<div class=\'alert alert-success\'>';
- html += '<div><strong>Download: ' + res + '</strong></div> <br />';
- html += '<div><strong>Seed Limit: ' + seedLimit + '</strong></div>';
- if (totalSize !== null) {
- html += '<br /><div><strong>Total size: ' + totalSize + ' (aprox)</strong></div>';
- }
- html += '<p>You are about to download ' + amountOfAnime + ' ep(s)</p>';
- html += '<p>This will cause ' + amountOfAnime + ' download pop-up(s) Are you sure you want to continue?</p>';
- html += '<p>If there are a lot of eps, your browser might stop responding for a while. This is normal. If you are on Google Chrome, it will ask you to allow multiple-downloads</p>';
- html += '<button id=\'alertButton\'>Okay</button>';
- html += '<button id=\'alertButtonCancel\'>Cancel</button>';
- html += '</div>';
- return html;
- };
- var showAjaxErrorAlert = function (AjaxInfo) {
- if (!$('#parseErrors').is(':hidden')) {
- return;
- }
- $('#parseErrors').html('');
- var html = '';
- html += '<div class=\'alert alert-danger\'>';
- html += '<p>There was an error in getting the infomation from page: \'' + AjaxInfo.error.pageAtError + '\'</p>';
- html += '<p>The last successful page parsed was page number ' + (AjaxInfo.currentPage === null ? 1 : AjaxInfo.currentPage) + ' </p>';
- html += '<button id=\'errorClose\'> close </button>';
- html += '</div>';
- $('#parseErrors').html(html);
- $('#parseErrors').slideDown('slow');
- $('#errorClose').off('click').on('click', function () {
- $('#parseErrors').slideUp('slow', function () {
- $(this).html('');
- });
- });
- };
- var buildSelect = function (resTOFilter) {
- resTOFilter = typeof resTOFilter === 'undefined' ? 'none' : resTOFilter;
- var html = '';
- epsInSelect = [
- ];
- html += '<div id=\'selectWrapper\'>';
- html += '<div id=\'selectContainer\'>';
- html += '<p>Or you can select episodes here:</p>';
- html += '<p>Seed limit: ' + (Options.Seeds.minSeeds === -1 ? 'None' : Options.Seeds.minSeeds) + '</p>'
- html += '<select id=\'animeSelection\' multiple size=\'20\'>';
- var allRes = Anime.getAvailableResolutions();
- for (let i = 0; i < allRes.length; i++) {
- var currRes = allRes[i];
- var localRes = currRes.res;
- var eps = Anime.getEpsForRes(localRes, false);
- for (var j = 0, len = eps.length; j < len; j++) {
- var currentEp = eps[j];
- if (resTOFilter == 'none' || currentEp.getRes() == resTOFilter) {
- html += '<option data-url=\'' + currentEp.getDownloadLink() + '\'>';
- html += currentEp.getTitle() + ' - Seeders: ' + currentEp.getSeeds();
- epsInSelect.push(currentEp);
- html += '</option>';
- } else {
- break;
- }
- }
- }
- html += '</select>';
- html += '<span>Filter select control: </span>';
- var checked = false;
- for (let i = 0; i < allRes.length; i++) {
- if (resTOFilter == allRes[i].res) {
- checked = true;
- }
- html += '<input type=\'radio\' ' + (checked ? 'checked' : '') + ' data-set= \'' + allRes[i].res + '\' name=\'filterSelect\'/>' + '<label>' + (allRes[i].res === -1 ? 'Others' : allRes[i].res + 'p') + '</label>';
- checked = false;
- }
- html += '<a id=\'clearResOptions\' data-set=\'none\' >clear</a>';
- html += '</div>';
- html += '</div>';
- return html;
- };
- var applySearch = function (textToFilter) {
- var opts = epsInSelect;
- var rxp = new RegExp(textToFilter);
- var optlist = $('#animeSelection').empty();
- for (let i = 0, len = opts.length; i < len; i++) {
- var ep = opts[i];
- if (rxp.test(ep.getTitle())) {
- optlist.append('<option data-url=\'' + ep.getDownloadLink() + '\'>' + ep.getTitle() + ' - Seeders: ' + ep.getSeeds() + '</option>');
- }
- }
- searchApplied = textToFilter;
- Utils.sortSelect(document.getElementById("animeSelection"));
- };
- var getAppliedSearch = function () {
- return searchApplied;
- };
- var getEpsInSelect = function () {
- return epsInSelect;
- };
- return {
- buildDropdownSelections: buildDropdownSelections,
- buildTable: buildTable,
- builDownloadAlert: builDownloadAlert,
- showAjaxErrorAlert: showAjaxErrorAlert,
- buildSelect: buildSelect,
- getEpsInSelect: getEpsInSelect,
- applySearch: applySearch,
- getAppliedSearch: getAppliedSearch
- };
- }());
- var DataParser = (function () {
- var table = null;
- let isParsing = false;
- var setTable = function (_table) {
- table = _table;
- };
- /**
- * 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 (currentPage) {
- if (table === null) {
- throw 'no table to parse on, table is null';
- }
- var trRow = table.find('img[src*=\'www-37.png\']').closest('tr');
- var eps = [
- ];
- $.each($(trRow), function (k, v) {
- var Dres = parseRes(this);
- if (Dres === -1) {
- Anime.addAvailableResolutions(-1, null);
- } else {
- Anime.addAvailableResolutions(Dres.res, Dres.fullRes);
- }
- let info = getEpisodeInfo(this);
- eps.push(new Episode(typeof Dres.res === ('undefined') ? -1 : Dres.res, info.currentDownloadLink, info.seeds, info.leech, info.uid, currentPage, info.title, info.size));
- });
- return eps;
- function parseRes(eventContent) {
- var suppRes = Anime.getSupportedRes();
- for (let i = 0; i < Anime.getSupportedRes().length; i++) {
- var currRes = suppRes[i].res;
- var currFullRes = suppRes[i].fullRes;
- if ($(eventContent).children('td:nth-child(2)').text().indexOf(currRes + 'p') > -1 || $(eventContent).children('td:nth-child(2)').text().indexOf(currFullRes) > -1) {
- return suppRes[i];
- }
- }
- return -1;
- }
- function getEpisodeInfo(eventContent) {
- return {
- 'currentDownloadLink': $(eventContent).find('td:nth-child(3) >a').attr('href'),
- 'seeds': (isNaN(parseInt($(eventContent).find('td.tlistsn').text()))) ? 0 : parseInt($(eventContent).find('td.tlistsn').text()),
- 'leech': (isNaN(parseInt($(eventContent).find('td.tlistln').text()))) ? 0 : parseInt($(eventContent).find('td.tlistln').text()),
- 'title': $(eventContent).children('.tlistname').text(),
- 'uid': Anime.getUidFromJqueryObject($(eventContent)),
- 'size': (function () {
- var sizeValue = $(eventContent).find('td.tlistsize').text();
- var sizeText = $.trim(sizeValue.split(' ').pop());
- let intValue = parseInt(sizeValue);
- switch (sizeText) {
- case 'MiB':
- return ((Math.pow(2, 20)) / 1) * intValue;
- break;
- case 'GiB':
- return intValue * 1073741824;
- break;
- default:
- return 0;
- break;
- }
- }())
- };
- }
- };
- return {
- parseTable: parseTable,
- setTable: setTable,
- isParsing: isParsing
- };
- }());
- var QueryString = function () {
- var query_string = {};
- var query = window.location.search.substring(1);
- var vars = query.split('&');
- for (let 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 */
- init(); // init the pannel and set up objects and listeners
- AfterInit(); // set page laod items and settings after the object and ui is built
- function init() {
- setAnimeObj();
- buildUi();
- bindListeners();
- function setAnimeObj() {
- // Set currentAnime
- if (QueryString.term !== '') {
- Anime.setCurrentAnime(decodeURIComponent(QueryString.term).split('+').join(' '));
- } else {
- Anime.setCurrentAnime('Unknown');
- }
- // set subber
- Anime.setCurrentSubber($('.notice > a > *').html());
- // Set eps
- DataParser.setTable($('table.tlist'));
- // set the seed limit
- var eps = DataParser.parseTable(Utils.getCurrentPageOffset());
- if (Localstore.getMinSeedsFromStore() !== null) {
- var minSeeds = parseInt(Localstore.getMinSeedsFromStore());
- Options.Seeds.minSeeds = minSeeds;
- }
- Anime.addAllEps(eps);
- }
- function buildUi() {
- makeStyles();
- buildPanel();
- afterBuild();
- function makeStyles() {
- var styles = '';
- // Panel
- 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; margin-left: 8px; margin-right: 8px; width: auto; margin-top: 19px;}';
- styles += '.panel-success {border-color: #d6e9c6;}';
- styles += '.collapse{cursor: pointer; position: absolute; right: 4px; top: 2px;}';
- 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; position: relative;}';
- styles += '.panel-title {color: inherit; margin-bottom: 0; margin-top: 0; padding: 6px; display: inline-block;}';
- 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; z-index: 1;}';
- styles += '#topbar{z-index: 2 !important;}';
- // Infomation
- 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;}';
- // Alerts
- styles += '.alert {border: 1px solid transparent;border-radius: 4px;margin-bottom: 20px;padding: 15px; position:relative;}';
- styles += '.alert-success {background-color: #dff0d8;border-color: #d6e9c6;color: #3c763d;}';
- styles += '.alert-danger {color: #A94442;background-color: #F2DEDE;border-color: #EBCCD1;}';
- styles += '#alertUser, #parseErrors{margin-top: 15px;}';
- styles += '#alertButton{position:absolute; bottom:5px; right:5px;}';
- styles += '#alertButtonCancel{position:absolute; bottom:5px; right: 66px;}';
- styles += '#errorClose{position:absolute; bottom:5px; right: 11px;}';
- // Anime Selects
- styles += '#animeSelection{width: 100%;}';
- styles += '#clearResOptions{margin-left: 10px; cursor: pointer;}';
- styles += '#downloadCustomButton{float:right;}';
- styles += '#findEp{float: right; position: relative; bottom: 20px; width: 180px;}'
- DomUtil.injectCss('https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css');
- DomUtil.injectCss(styles);
- }
- function buildPanel() {
- var html = '';
- html += '<div class="panel panel-success">';
- html += '<div class="panel-heading">';
- html += '<h3 id="panel-title" class="panel-title"></h3>';
- html += '<i class="fa fa-minus collapse" id="collapseToggle" title="Hide"></i>';
- html += '</div>';
- html += '<div class="panel-body" id="pannelContent"></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);
- return;
- }
- html += '<span>Pick a resolution: </span>';
- html += '<span id=\'selectDownload\'>';
- html += UI.buildDropdownSelections();
- html += '</span>';
- 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.\'/>';
- html += '<button type=\'button\' id=\'SaveMinSeeds\'>Save</button>';
- html += '<div id=\'tableInfo\'>';
- html += UI.buildTable();
- html += '</div>';
- html += '<div id=\'alertUser\' class=\'hide\'></div>';
- html += '<div class=\'selectAnime\' id=\'selectAnime\'>';
- html += UI.buildSelect();
- html += '</div>';
- html += '<input type=\'text\' id=\'findEp\' placeholder=\'Search Select (or use regex)\' />';
- html += '<button id=\'acceptSelect\' data-type=\'downloadSelects\'>Select for download</button>';
- html += '<div id=\'parseErrors\' class =\'hide\'></div>';
- $('#pannelContent').html(html);
- }
- }
- function afterBuild() {
- makeCheckBoxes();
- sortLists();
- function makeCheckBoxes() {
- $('.tlistdownload > a').after('<input class=\'checkboxes\' type=\'checkbox\'/>');
- }
- function sortLists() {
- Utils.sortAllControls();
- }
- }
- }
- function bindListeners() {
- Utils.reBindSelectFilters();
- $('#downloadAll').on('click', function (e) {
- Utils.doDownloads(e);
- });
- $('#downloadCustomButton').on('click', function (e) {
- Utils.doDownloads(e);
- });
- $('.checkboxes').on('click', function (e) {
- if (Utils.checkBoxValid($('.checkboxes'))) {
- Utils.enableButton($('#downloadCustomButton'));
- } else {
- Utils.disableButton($('#downloadCustomButton'));
- }
- });
- $('#crossPage').on('click', function (e) {
- preformParsing(Anime.getPageUrls(true));
- function preformParsing(urls) {
- if (urls.length === 0) {
- return;
- }
- if (Utils.checkBoxValid($('#crossPage'))) {
- $('#tableInfo').html('<p>Please wait while we parse each page...</p>');
- $('#selectAnime').html('');
- $('#acceptSelect').hide();
- $('#crossPage, #downloadAll').prop('disabled', true);
- $('#parseErrors').slideUp('fast', function () {
- $(this).html('');
- });
- var AjaxInfo = {
- error: {
- pageAtError: null
- },
- currentPage: null
- };
- urls.reduce(function (prev, cur, index) {
- return prev.then(function (data) {
- return $.ajax(cur).then(function (data) {
- DataParser.isParsing = true;
- var currentPage = 0;
- if (cur.indexOf('offset') > -1) {
- currentPage = cur.substring(cur.indexOf('offset')).split('&')[0].split('=').pop();
- } else {
- currentPage = 1;
- }
- AjaxInfo.currentPage = currentPage;
- var table = $(data).find('table.tlist');
- DataParser.setTable(table);
- Anime.addAllEps(DataParser.parseTable(parseInt(currentPage)));
- $('#tableInfo').append('<div>Page ' + currentPage + ' Done </div>');
- }, function () {
- AjaxInfo.error.pageAtError = cur.split('=').pop();
- });
- });
- }, $().promise()).always(function () {
- if (AjaxInfo.error.pageAtError !== null) {
- UI.showAjaxErrorAlert(AjaxInfo);
- }
- $('#tableInfo').html(UI.buildTable());
- $('#downloadRes').html(UI.buildDropdownSelections());
- $('#selectAnime').html(UI.buildSelect());
- Utils.sortAllControls();
- $('#acceptSelect').show();
- Utils.reBindSelectFilters();
- $('#crossPage, #downloadAll').prop('disabled', false);
- DataParser.isParsing = 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);
- Utils.cleanAvailableResolutions();
- $('#downloadRes').html(UI.buildDropdownSelections());
- $('#tableInfo').html(UI.buildTable());
- $('#selectAnime').html(UI.buildSelect());
- Utils.reBindSelectFilters();
- Utils.sortAllControls();
- }
- }
- });
- $('#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);
- }
- $('#selectAnime').html(UI.buildSelect());
- Utils.sortAllControls();
- Utils.reBindSelectFilters();
- });
- $('#collapseToggle').on('click', function () {
- $('#pannelContent').stop(true, true).slideToggle('slow', function () {
- if ($(this).is(':hidden')) {
- $('#collapseToggle').removeClass('fa-minus').addClass('fa-plus');
- $('#collapseToggle').attr('title', 'Show');
- } else {
- $('#collapseToggle').addClass('fa-minus').removeClass('fa-plus');
- $('#collapseToggle').attr('title', 'Hide');
- }
- });
- });
- $('#acceptSelect').on('click', function (e) {
- Utils.doDownloads(e);
- });
- $('#findEp').on('keyup', function () {
- UI.applySearch($(this).val());
- });
- }
- }
- function AfterInit() {
- initButtonStates();
- if (Utils.html5StoreSupport()) {
- setOptionsFromLocalStore();
- }
- function initButtonStates() {
- if (Utils.checkBoxValid($('.checkboxes'))) {
- Utils.enableButton($('#downloadCustomButton'));
- } else {
- Utils.disableButton($('#downloadCustomButton'));
- }
- }
- function setOptionsFromLocalStore() {
- // Min seeds
- if (Localstore.getMinSeedsFromStore() !== null) {
- $('#MinSeeds').val(Options.Seeds.minSeeds);
- }
- }
- }