`;
break;
default:
console.error(`unknown type "${typeof KG.preferences[i]}" of KG.preferences.${i}`);
}
$("#KG-preferences-container").append(html);
}
}
KG.savePreferences = () => {
$("#KG-preferences-container input").each((i, obj) => {
var id = obj.id.slice(14);
var value;
switch (obj.type) {
case "checkbox":
value = obj.checked;
break;
default:
value = obj.value;
break;
}
KG.preferences[id] = value;
});
GM_setValue("KG-preferences", JSON.stringify(KG.preferences));
}
KG.resetPreferences = () => {
GM_setValue("KG-preferences", "");
location.reload();
}
//patches the knownServers object based on the current url
KG.applySiteOverrides = () => {
var over = KG.serverOverrides[location.hostname]
for (var i in over) {
if (KG.knownServers[i]) {
if (over[i] === null) { //server should be removed
delete KG.knownServers[i];
} else { //server should be patched
console.err("KG: patching server entries not implemented");
}
} else { //server should be added
KG.knownServers[i] = over[i];
}
}
}
//injects element into page
KG.injectWidgets = () => {
var site = KG.supportedSites[location.hostname];
var epCount = $(".listing a").length;
//css
$(document.head).append(``);
//box on the right
$(`#rightside .clear2:eq(${site.optsPosition || 0})`).after(optsHTML);
$("#KG-input-to").val(epCount)
.attr("max", epCount);
$("#KG-input-from").attr("max", epCount);
for (var i in KG.knownServers) {
$(``);
for (var i in KG.exporters) {
var $exporter = $(``).appendTo("#KG-input-export");
if ((KG.exporters[i].requireSamePage && !onSamePage) ||
(KG.exporters[i].requireDirectLinks && KG.status.linkType != "direct")
) {
$exporter.attr("disabled", true);
}
}
//actions
$("#KG-action-container .KG-button").remove();
for (var i in KG.actions) {
if (
(!KG.actions[i].requireLinkType || KG.status.linkType == KG.actions[i].requireLinkType) &&
KG.actions[i].servers.includes(KG.status.server)
) {
if (KG.actions[i].automatic && KG.preferences.enable_automatic_actions && !KG.status.automaticDone) {
KG.status.automaticDone = true;
KG.actions[i].execute(KG.status);
}
if (KG.status.automaticDone) {
continue;
}
$("#KG-action-container")
.append(``);
}
}
//colors again
KG.applyColors();
$("#KG-linkdisplay").show();
}
//invokes a exporter
KG.exportData = (exporter) => {
$("#KG-input-export").val("");
var text = KG.exporters[exporter].export(KG.status);
$("#KG-linkdisplay-export-text").text(text);
$("#KG-input-export-download").attr({
href: `data:text/plain;charset=utf-8,${encodeURIComponent(text)}`,
download: `${KG.status.title}.${KG.exporters[exporter].extension}`,
})
$("#KG-linkdisplay-export").show();
}
KG.showSpinner = () => {
$("#KG-linkdisplay-text").html(`
Loading...
`);
}
//hides the linkdisplay
KG.closeLinkdisplay = () => {
$("#KG-linkdisplay").hide();
KG.clearStatus();
}
//saves a new preferred server
KG.updatePreferredServer = () => {
localStorage["KG-preferredServer"] = $("#KG-input-server").val();
}
//loads preferred server
KG.loadPreferredServer = () => {
$("#KG-input-server").val(localStorage["KG-preferredServer"]);
}
KG.showPreferences = () => {
$("#KG-preferences").slideDown();
}
KG.closePreferences = () => {
KG.savePreferences();
$("#KG-preferences").slideUp();
}
//applies regex to html page to find a link
KG.findLink = (regexString) => {
var re = new RegExp(regexString);
var result = document.body.innerHTML.match(re);
if (result && result.length > 0) {
return result[0].split('"')[1];
}
return "";
}
//wildcard-enabled string comparison
KG.if = (str, rule) => {
return new RegExp("^" + rule.split("*").join(".*") + "$").test(str);
}
//iterates over an array with supplied function
//either (array, min, max, func)
//or (array, func)
KG.for = (array, min, max, func) => {
if (typeof min == "function") {
func = min;
max = array.length - 1;
}
min = Math.max(0, min) || 0;
max = Math.min(array.length - 1, max);
for (var i = min; i <= max; i++) {
func(i, array[i]);
}
}
KG.get = (url) => {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: (o) => {
resolve(o.response);
},
onerror: () => {
reject();
}
});
});
}
KG.head = (url) => {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "HEAD",
url: url,
onload: (o) => {
resolve(o.status);
},
onerror: () => {
reject();
}
});
});
}
//allows multiple different approaches to collecting links, if sites differ greatly
KG.steps = {};
//default
KG.steps.defaultBegin = () => {
KG.status.func = "defaultGetLink";
KG.saveStatus();
location.href = KG.status.episodes[KG.status.current].kissLink + `&s=${KG.status.server}`;
}
KG.steps.defaultGetLink = () => {
if (!KG.if(location.pathname, KG.supportedSites[location.hostname].contentPath)) { //captcha
return;
}
link = KG.findLink(KG.knownServers[KG.status.server].regex);
KG.status.episodes[KG.status.current].grabLink = link || "error (selected server may not be available)";
KG.status.current++;
if (KG.status.current >= KG.status.episodes.length) {
KG.status.func = "defaultFinished";
location.href = KG.status.url;
} else {
location.href = KG.status.episodes[KG.status.current].kissLink + `&s=${KG.status.server}`;
}
KG.saveStatus();
}
KG.steps.defaultFinished = () => {
KG.displayLinks();
}
//allows for multiple ways to export collected data
KG.exporters = {};
KG.exporters.list = {
name: "list",
extension: "txt",
requireSamePage: false,
requireDirectLinks: false,
export: (data) => {
var str = "";
for (var i in data.episodes) {
str += data.episodes[i].grabLink + "\n";
}
return str;
}
}
KG.exporters.m3u = {
name: "m3u8 playlist",
extension: "m3u8",
requireSamePage: true,
requireDirectLinks: true,
export: (data) => {
var listing = $(".listing a").get().reverse();
var str = "#EXTM3U\n";
KG.for(data.episodes, (i, obj) => {
str += `#EXTINF:0,${listing[obj.num-1].innerText}\n${obj.grabLink}\n`;
});
return str;
}
}
KG.exporters.json = {
name: "json",
extension: "json",
requireSamePage: true,
requireDirectLinks: false,
export: (data) => {
var listing = $(".listing a").get().reverse();
var json = {
title: data.title,
server: data.server,
linkType: data.linkType,
episodes: []
};
for (var i in data.episodes) {
json.episodes.push({
number: data.episodes[i].num,
name: listing[data.episodes[i].num - 1].innerText,
link: data.episodes[i].grabLink
});
}
return JSON.stringify(json);
},
}
KG.exporters.html = {
name: "html list",
extension: "html",
requireSamePage: true,
requireDirectLinks: true,
export: (data) => {
var listing = $(".listing a").get().reverse();
var str = "\n \n";
KG.for(data.episodes, (i, obj) => {
str += ` ${listing[obj.num-1].innerText} \n`;
});
str += " \n\n";
return str;
}
}
KG.exporters.csv = {
name: "csv",
extension: "csv",
requireSamePage: true,
requireDirectLinks: false,
export: (data) => {
var listing = $(".listing a").get().reverse();
var str = "episode, name, url\n";
for (var i in data.episodes) {
str += `${data.episodes[i].num}, ${listing[data.episodes[i].num-1].innerText}, ${data.episodes[i].grabLink}\n`;
}
return str;
}
}
KG.exporters.aria2c = {
name: "aria2c file",
extension: "txt",
requireSamePage: false,
requireDirectLinks: true,
export: (data) => {
var padLength = Math.max(2, data.episodes[data.episodes.length - 1].num.toString().length);
var str = "";
KG.for(data.episodes, (i, obj) => {
str += `${obj.grabLink}\n -o E${obj.num.toString().padStart(padLength, "0")}.mp4\n`;
});
return str;
}
}
//further options after grabbing, such as converting embed to direct links
KG.actions = {};
KG.actionAux = {};
KG.actions.rapidvideo_getDirect = {
name: "get direct links",
requireLinkType: "embed",
servers: ["rapidvideo", "rapid"],
execute: async (data) => {
KG.showSpinner();
var promises = [];
for (var i in data.episodes) {
promises.push(KG.actionAux["rapidvideo_getDirect"](data.episodes[i]));
}
await Promise.all(promises);
data.linkType = "direct";
KG.saveStatus();
KG.displayLinks();
},
}
//additional function to reduce clutter
//asynchronously gets the direct link
KG.actionAux.rapidvideo_getDirect = async (ep) => {
$html = $(await KG.get(ep.grabLink));
$sources = $html.find("source");
if ($sources.length == 0) {
ep.grabLink = "error: no sources found";
return;
}
var sources = {};
KG.for($sources, (i, obj) => {
sources[obj.dataset.res] = obj.src;
});
var parsedQualityPrefs = KG.preferences.quality_order.replace(/\ /g, "").split(",");
for (var i of parsedQualityPrefs) {
if (sources[i]) {
ep.grabLink = sources[i];
return;
}
}
ep.grabLink = "error: preferred qualities not found";
}
KG.actions.beta_setQuality = {
name: "set quality",
requireLinkType: "direct",
servers: ["beta", "beta2"],
automatic: true,
execute: async (data) => {
KG.showSpinner();
var promises = [];
for (var i in data.episodes) {
promises.push(KG.actionAux["beta_tryGetQuality"](data.episodes[i]));
}
await Promise.all(promises);
data.automaticDone = true;
KG.saveStatus();
KG.displayLinks();
},
}
KG.actionAux.beta_tryGetQuality = async (ep) => {
var rawLink = ep.grabLink.slice(0, -4);
var qualityStrings = {"1080": "=m37", "720": "=m22", "360": "=m18"};
var parsedQualityPrefs = KG.preferences.quality_order.replace(/\ /g, "").split(",");
for (var i of parsedQualityPrefs) {
if (qualityStrings[i]) {
if (await KG.head(rawLink + qualityStrings[i]) == 200) {
ep.grabLink = rawLink + qualityStrings[i];
return;
}
}
}
}
//if something doesn't look right on a specific site, a fix can be written here
KG.fixes = {}
KG.fixes["kimcartoon.to_UIFix"] = () => {
//linkdisplay
var $ld = $("#KG-linkdisplay");
$ld.find(".barTitle").removeClass("barTitle")
.css({
"height": "20px",
"padding": "5px",
});
$("#KG-linkdisplay-title").css({
"font-size": "20px",
"color": $("a.bigChar").css("color"),
})
$ld.find(".arrow-general").remove();
//preference panel
var $pf = $("#KG-preferences");
$pf.find(".barTitle").removeClass("barTitle")
.css({
"height": "20px",
"padding": "5px",
});
$("#KG-linkdisplay-title").css({
"font-size": "20px",
"color": $("a.bigChar").css("color"),
});
$pf.find(".arrow-general").remove();
//opts
var $opts = $("#KG-opts-widget");
var title = $opts.find(".barTitle").html();
$opts.before(`
${title}
`);
$(".icon:eq(1)").css({ "width": "100%", "box-sizing": "border-box" });
$(".KG-preferences-button").css("margin-top", "5px");
$opts.find(".barTitle").remove();
$opts.find(".arrow-general").remove();
//general
$(".KG-dialog-title").css("font-size", "18px");
}
KG.fixes["kissasian.sh_UIFix"] = () => {
$(".KG-preferences-button").css("filter", "invert(0.7)");
$(".KG-dialog-close").css("color", "#000");
$(".KG-dialog-close").hover((e) => {
$(e.target).css("color", e.type == "mouseenter" ? "#fff" : "#000");
});
}
//HTML and CSS pasted here because Tampermonkey apparently doesn't allow resources to be updated
//if you have a solution for including extra files that are updated when the script is reinstalled please let me know: thorio.git@gmail.com
//the grabber widget injected into the page
var optsHTML = `
KissGrabber
from
to
`;
//initially hidden HTML that is revealed and filled in by the grabber script
var linkListHTML = `