// ==UserScript== // @name KGrabber // @namespace thorou // @version 3.3.2-b107 // @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 fembed.com // @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 https://update.greasyfork.icu/scripts/383649/KGrabber.user.js // @updateURL https://update.greasyfork.icu/scripts/383649/KGrabber.meta.js // ==/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, { linkType: LinkTypes.OVELWRAP, servers: servers, }); }, automatic: true, }); } async function decrypt(episode) { if (episode.error) { return; } episode.processedLink = await util.kissCrypto.decrypt(episode.functionalLink); } },{"../util":55,"./shared":5,"kgrabber-types":37}],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.hasReset = true; }, availableFunc: (action, status) => { for (let episode of status.episodes) { if ((episode.error && episode.grabbedLink) || episode.processedLink) { return true; } } return false; }, }), ]; async function reset(episode) { episode.error = ""; episode.processedLink = ""; } },{"../config":6,"./shared":5,"kgrabber-types":37}],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("./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":41,"./beta":1,"./generic":2,"./nova":4}],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, { linkType: LinkTypes.EMBED, servers: ["nova", "fe"], }); }, }), ]; async function getDirect(episode) { if (episode.error) return; let sources = await getSources(episode); if (!sources) return; let url = findQuality(episode, sources); if (!url) return; episode.processedLink = url; } async function getSources(episode) { let response = await ajax.post(`https://www.fembed.com/api/source/${episode.functionalLink.match(/\/([^/]*?)$/)[1]}`); let json; try { json = JSON.parse(response.response); } catch (error) { episode.error = "parsing error"; util.log.err(episode.error, episode, response); return null; } if (!json.success) { episode.error = json.data; util.log.err(episode.error, episode, response); return null; } return json.data; } function findQuality(episode, sources) { let parsedQualityPrefs = preferences.general.quality_order.replace(/\s/g, "").split(","); for (let quality of parsedQualityPrefs) { let source = sources.find((s) => s.label == quality + "p"); if (source) return source.file; } let availableQualities = sources.map((s) => s.label).join(", "); episode.error = "preferred qualities not found. available: " + availableQualities; util.log.err(episode.error, episode, sources); } },{"../config/preferenceManager":7,"../util":55,"../util/ajax":54,"./shared":5,"kgrabber-types":37}],5:[function(require,module,exports){ "use strict"; // src\js\actions\shared.js const util = require("../util"); 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, { linkType, servers }) => { if (!servers.includes(status.serverID)) { return false; } if (linkType != status.linkType) { return false; } return true; }; },{"../util":55}],6:[function(require,module,exports){ "use strict"; // src\js\config\index.js module.exports = { preferenceManager: require("./preferenceManager"), sites: require("./sites"), }; },{"./preferenceManager":7,"./sites":8}],7:[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":55}],8:[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":48,"./kimcartoon":9,"./kissanime_ru":10,"./kissasian":11,"./kisstvshow":12,"kgrabber-types":37}],9:[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("hx", { regex: /src="(\/\/playhydrax.com\/\?v=.*?)"/, captureGroup: 1, trimQuotes: false, name: "HX (hydrax)", linkType: LinkTypes.EMBED, }), new Server("fe", { regex: /"(https:\/\/www.luxubu.review\/v\/.*?)"/, captureGroup: 1, trimQuotes: false, name: "FE (luxubu.review)", linkType: LinkTypes.EMBED, }), new Server("beta", { regex: /"(https:\/\/redirector.googlevideo.com\/videoplayback\?.*?)"/, captureGroup: 1, trimQuotes: false, name: "Beta", linkType: LinkTypes.DIRECT, }), new Server("alpha", { regex: /"(https:\/\/redirector.googlevideo.com\/videoplayback\?.*?)"/, captureGroup: 1, trimQuotes: false, name: "Alpha (googleusercontent.com)", linkType: LinkTypes.DIRECT, }), ]); module.exports = new Site("kimcartoon.to", { contentPath: "Cartoon", noCaptchaServer: "rapid", buttonColor: "#ecc835", buttonTextColor: "#000", servers, patches: uiFix, }); },{"./patches/kimcartoon_UIFix":13,"kgrabber-types":37}],10:[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).(?:feurl.com|novelplanet.me)\/v\/.*?)"/, captureGroup: 1, trimQuotes: false, name: "Nova", linkType: LinkTypes.EMBED, customStep: "modalBegin", }), new Server("beta", { regex: /