// ==UserScript== // @name KGrabber // @namespace thorou // @version 3.2.0-b79 // @description extracts links from kissanime.ru and similar sites // @author Thorou // @license GPLv3 - http://www.gnu.org/licenses/gpl-3.0.txt // @homepageURL https://github.com/thorio/KGrabber/ // @match http*://kissanime.ru/* // @match http*://kimcartoon.to/* // @match http*://kissasian.sh/* // @match http*://kisstvshow.to/* // @connect rapidvideo.com // @connect googleusercontent.com // @connect googlevideo.com // @connect novelplanet.me // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @run-at document-end // @noframes // @require https://code.jquery.com/jquery-3.3.1.min.js // @downloadURL none // ==/UserScript== // bundled with browserify (function() { function outer(modules, cache, entry) { var previousRequire = typeof require == "function" && require; function newRequire(name, jumped){ if(!cache[name]) { if(!modules[name]) { var currentRequire = typeof require == "function" && require; if (!jumped && currentRequire) return currentRequire(name, true); if (previousRequire) return previousRequire(name, true); var err = new Error('Cannot find module \'' + name + '\''); err.code = 'MODULE_NOT_FOUND'; throw err; } var m = cache[name] = {exports:{}}; modules[name][0].call(m.exports, function(x){ var id = modules[name][1][x]; return newRequire(id ? id : x); },m,m.exports,outer,modules,cache,entry); } return cache[name].exports; } for(var i=0;i { await shared.eachEpisode(status.episodes, decrypt, setProgress); status.linkType = resultType; }, availableFunc: (action, status) => { return shared.availableFunc(status, { automatic: action.automatic, linkType: LinkTypes.OVELWRAP, servers: servers, }); }, automatic: true, }); } async function decrypt(episode) { if (episode.error) { return; } episode.processedLink = await util.kissCrypto.decrypt(episode.functionalLink); } },{"../config/preferenceManager":8,"../util":56,"./shared":6,"http-status-codes":59,"kgrabber-types":38}],2:[function(require,module,exports){ "use strict"; // src\js\actions\generic.js const shared = require("./shared"), config = require("../config"), { Action } = require("kgrabber-types"); module.exports = [ new Action("reset", { executeFunc: async (status, setProgress) => { await shared.eachEpisode(status.episodes, reset, setProgress); status.linkType = config.sites.current().servers.get(status.serverID).linkType; status.automaticDone = false; }, availableFunc: (action, status) => { for (let episode of status.episodes) { if (episode.error || episode.processedLink) { return true; } } return false; }, }), ]; async function reset(episode) { episode.error = ""; episode.processedLink = ""; } },{"../config":7,"./shared":6,"kgrabber-types":38}],3:[function(require,module,exports){ "use strict"; // src\js\actions\index.js const statusManager = require("../statusManager"); const status = statusManager.get(); let actions = [].concat( require("./generic"), require("./rapidvideo"), require("./beta"), require("./nova") ); exports.all = () => actions; exports.available = () => actions.filter((action) => action.isAvailable(status) ); exports.add = (...action) => { actions.push(...action); }; exports.execute = async (action, setSpinnerText) => { await action.invoke(status, setSpinnerText); if (action.automatic) { status.automaticDone = true; } }; },{"../statusManager":42,"./beta":1,"./generic":2,"./nova":4,"./rapidvideo":5}],4:[function(require,module,exports){ "use strict"; // src\js\actions\nova.js const ajax = require("../util/ajax"), util = require("../util"), preferenceManager = require("../config/preferenceManager"), shared = require("./shared"), { Action, LinkTypes } = require("kgrabber-types"); const preferences = preferenceManager.get(); module.exports = [ new Action("get direct links", { executeFunc: async (status, setProgress) => { await shared.eachEpisode(status.episodes, getDirect, setProgress); status.linkType = LinkTypes.DIRECT; }, availableFunc: (action, status) => { return shared.availableFunc(status, { automatic: action.automatic, linkType: LinkTypes.EMBED, servers: ["nova"], }); }, }), ]; async function getDirect(episode) { if (episode.error) { return; } let response = await ajax.post(`https://www.novelplanet.me/api/source/${episode.functionalLink.match(/\/([^/]*?)$/)[1]}`); if (response.success === false && response.data.includes("encoding")) { episode.error = "video is still being encoded"; util.log.err(`nova: ${episode.error}`, response); return; } let json = JSON.parse(response.response); if (!json.data || json.data.length < 1) { episode.error = "no sources found"; util.log.err(`nova: ${episode.error}`, response); return; } let sources = json.data; let parsedQualityPrefs = preferences.general.quality_order.replace(/\s/g, "").split(","); for (let i of parsedQualityPrefs) { for (let j of sources) { if (j.label == i + "p") { episode.processedLink = j.file; return; } } } episode.error = "preferred qualities not found"; util.log.err(`nova: ${episode.error}`, response); } },{"../config/preferenceManager":8,"../util":56,"../util/ajax":55,"./shared":6,"kgrabber-types":38}],5:[function(require,module,exports){ "use strict"; // src\js\actions\rapidvideo.js const util = require("../util"), { ajax } = util, preferenceManager = require("../config/preferenceManager"), shared = require("./shared"), { Action, LinkTypes } = require("kgrabber-types"), HttpStatusCodes = require("http-status-codes"); const preferences = preferenceManager.get(); module.exports = [ new Action("revert domain", { executeFunc: async (status, _setProgress) => { for (let i in status.episodes) { status.episodes[i].processedLink = status.episodes[i].processedLink.replace("rapidvid.to", "rapidvideo.com"); } }, availableFunc: (action, status) => { return shared.availableFunc(status, { automatic: action.automatic, linkType: LinkTypes.EMBED, servers: ["rapid"], }); }, automatic: true, }), new Action("get direct links", { executeFunc: async (status, setProgress) => { await shared.eachEpisode(status.episodes, _rapidvideo_getDirect, setProgress); status.linkType = LinkTypes.DIRECT; }, availableFunc: (action, status) => { return shared.availableFunc(status, { automatic: action.automatic, linkType: LinkTypes.EMBED, servers: ["rapid"], }); }, }), ]; async function _rapidvideo_getDirect(episode) { if (episode.error) { return; } let response = await ajax.get(episode.functionalLink); if (response.status != HttpStatusCodes.OK) { episode.error = `http status ${response.status}`; return; } let $html = $(response.response); let $sources = $html.find("source"); if ($sources.length == 0) { episode.error = "no sources found"; return; } let sources = {}; util.for($sources, (i, obj) => { sources[obj.dataset.res] = obj.src; }); let parsedQualityPrefs = preferences.general.quality_order.replace(/\s/g, "").split(","); for (let i of parsedQualityPrefs) { if (sources[i]) { episode.processedLink = sources[i]; return; } } episode.error = "preferred qualities not found"; } },{"../config/preferenceManager":8,"../util":56,"./shared":6,"http-status-codes":59,"kgrabber-types":38}],6:[function(require,module,exports){ "use strict"; // src\js\actions\shared.js const util = require("../util"), { preferenceManager } = require("../config"); const preferences = preferenceManager.get(); exports.eachEpisode = (episodes, func, setProgress) => { let promises = []; let progress = 0; for (let episode of episodes) { promises.push( func(episode).catch((e) => { episode.error = "something went wrong; see console for details"; util.log.err(e); }).finally(() => { progress++; setProgress(`${progress}/${promises.length}`); }) ); } setProgress(`0/${promises.length}`); return Promise.all(promises); }; exports.availableFunc = (status, { automatic, linkType, servers }) => { if (!servers.includes(status.serverID)) { return false; } if (linkType != status.linkType) { return false; } if (automatic && status.automaticDone || preferences.compatibility.disable_automatic_actions) { return false; } return true; }; },{"../config":7,"../util":56}],7:[function(require,module,exports){ "use strict"; // src\js\config\index.js module.exports = { preferenceManager: require("./preferenceManager"), sites: require("./sites"), }; },{"./preferenceManager":8,"./sites":9}],8:[function(require,module,exports){ "use strict"; // src\js\config\preferenceManager.js const util = require("../util"); const defaultPreferences = { general: { quality_order: "1080, 720, 480, 360", }, internet_download_manager: { idm_path: "C:\\Program Files (x86)\\Internet Download Manager\\IDMan.exe", download_path: "%~dp0", arguments: "/a", keep_title_in_episode_name: false, }, compatibility: { force_default_grabber: false, enable_experimental_grabbers: false, disable_automatic_actions: false, }, }; let preferences; exports.get = () => { if (preferences === undefined) { preferences = load(defaultPreferences); } return preferences; }; let save = exports.save = (newPreferences) => { util.clear(preferences); util.merge(preferences, newPreferences); GM_setValue("KG-preferences", JSON.stringify(preferences)); }; exports.reset = () => save({}); function load(defaults) { let saved = JSON.parse(GM_getValue("KG-preferences", "{}")); for (let i in saved) { if (defaults[i] === undefined) { delete saved[i]; } else { for (let j in saved[i]) { if (defaults[i][j] === undefined) { delete saved[i][j]; } } } } return util.merge(util.clone(defaults), saved); } function getPreferredServers() { return JSON.parse(GM_getValue("preferredServers", "{}")); } function savePreferredServers(servers) { GM_setValue("preferredServers", JSON.stringify(servers)); } exports.getPreferredServer = (host) => getPreferredServers()[host]; exports.setPreferredServer = (host, server) => { let saved = getPreferredServers(); saved[host] = server; savePreferredServers(saved); }; },{"../util":56}],9:[function(require,module,exports){ "use strict"; // src\js\config\sites\index.js const { Dictionary } = require("kgrabber-types"), page = require("../../ui/page"); const sites = new Dictionary([ require("./kissanime_ru"), require("./kimcartoon"), require("./kissasian"), require("./kisstvshow"), ]); exports.current = () => sites.get(page.location.hostname); exports.add = (...newSites) => { sites.add(...newSites); }; },{"../../ui/page":49,"./kimcartoon":10,"./kissanime_ru":11,"./kissasian":12,"./kisstvshow":13,"kgrabber-types":38}],10:[function(require,module,exports){ "use strict"; // src\js\config\sites\kimcartoon.js const { Server, Site, Dictionary, LinkTypes } = require("kgrabber-types"), uiFix = require("./patches/kimcartoon_UIFix"); let servers = new Dictionary([ new Server("openload", { regex: /"https:\/\/openload.co\/embed\/.*?"/, name: "Openload", linkType: LinkTypes.EMBED, }), new Server("streamango", { regex: /"https:\/\/streamango.com\/embed\/.*?"/, name: "Streamango", linkType: LinkTypes.EMBED, }), new Server("beta", { regex: /"https:\/\/redirector.googlevideo.com\/videoplayback\?.*?"/, name: "Beta", linkType: LinkTypes.DIRECT, }), new Server("rapid", { regex: /"https:\/\/w*?.*?rapidvid.to\/e\/.*?"/, name: "RapidVideo", linkType: LinkTypes.EMBED, }), new Server("fs", { regex: /"https:\/\/video.xx.fbcdn.net\/v\/.*?"/, name: "FS (fbcdn.net)", linkType: LinkTypes.DIRECT, }), new Server("gp", { regex: /"https:\/\/redirector.googlevideo.com\/videoplayback\?.*?"/, name: "GP (googleusercontent.com)", linkType: LinkTypes.DIRECT, }), new Server("fe", { regex: /"https:\/\/www.luxubu.review\/v\/.*?"/, name: "FE (luxubu.review)", linkType: LinkTypes.EMBED, }), ]); module.exports = new Site("kimcartoon.to", { contentPath: "Cartoon", noCaptchaServer: "rapid", buttonColor: "#ecc835", buttonTextColor: "#000", servers, patches: uiFix, }); },{"./patches/kimcartoon_UIFix":14,"kgrabber-types":38}],11:[function(require,module,exports){ "use strict"; // src\js\config\sites\kissanime_ru.js const { Server, Site, Dictionary, LinkTypes } = require("kgrabber-types"); let servers = new Dictionary([ new Server("hydrax", { regex: /"(https:\/\/playhydrax.com\/\?v=.*?)"/, captureGroup: 1, trimQuotes: false, name: "HydraX (no captcha)", linkType: LinkTypes.EMBED, customStep: "modalBegin", }), new Server("nova", { regex: /"(https:\/\/www.novelplanet.me\/v\/.*?)"/, captureGroup: 1, trimQuotes: false, name: "Nova", linkType: LinkTypes.EMBED, customStep: "modalBegin", }), new Server("beta", { regex: /