From 72e770646124a5bd7fbe274192671f004ed315c0 Mon Sep 17 00:00:00 2001 From: aka paul <50n50@noreply.localhost> Date: Wed, 26 Nov 2025 17:50:57 +0000 Subject: [PATCH] Add mangafire/mangafire.js --- mangafire/mangafire.js | 427 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 mangafire/mangafire.js diff --git a/mangafire/mangafire.js b/mangafire/mangafire.js new file mode 100644 index 0000000..8146ed9 --- /dev/null +++ b/mangafire/mangafire.js @@ -0,0 +1,427 @@ +async function searchContent(input,page=0){ + function parseSearchResults(html) { + const results = []; + const regex = /
[\s\S]*?]*class="poster"[\s\S]*?]*alt="([^"]*)"/g; + + let match; + while ((match = regex.exec(html)) !== null) { + results.push({ + title: match[3], + imageURL: match[2], + id: match[1] + }); + } + + return results; + } const vrf = generate_vrf(input); + const response = await fetch("https://mangafire.to/filter?keyword=" + encodeURIComponent(input) + "&vrf=" + vrf); + const data = await response.text(); + console.log(JSON.stringify(parseSearchResults(data))); + return parseSearchResults(data); +} + +async function getContentData(url) { + function parseHtmlData(htmlContent) { + const genreRegex = /([^<]+)<\/a>/g; + const tags = []; + let match; + + while ((match = genreRegex.exec(htmlContent)) !== null) { + tags.push(match[1]); + } + + const uniqueTags = [...new Set(tags)]; + + const ogDescriptionRegex = /]*>([^<]+)<\/a>/g; + + let match; + while ((match = chapterRegex.exec(htmlContent)) !== null) { + const langCode = match[1]; + const chapterNumber = match[2]; + const chapterId = match[3]; + const title = match[4].trim(); + + if (!chapters[langCode]) { + chapters[langCode] = []; + } + + chapters[langCode].push([ + chapterNumber, + { + id: chapterId, + scanlation_group: "Mangafire", + title: title + } + ]); + } + + Object.keys(chapters).forEach(lang => { + chapters[lang].reverse(); + }); + + return chapters; + } + + try { + const response = await fetch(`https://mangafire.to/ajax/read/${mangaId}/chapter/en?vrf=${vrf}`); + const data = await response.json(); + + if (data.status === 200 && data.result && data.result.html) { + const chapters = parseChapters(data.result.html); + console.log(JSON.stringify(chapters)); + return chapters; + } else { + console.error("Invalid response from server"); + return null; + } + } catch (error) { + console.error("Error fetching chapters:" + error); + return null; + } +} + +async function getChapterImages(ID) { + vrf = generate_vrf(`chapter@${ID}`); + + try { + const response = await fetch(`https://mangafire.to/ajax/read/chapter/${ID}?vrf=${vrf}`); + const data = await response.json(); + + if (data.status === 200 && data.result && data.result.images) { + const images = data.result.images.map(img => img[0]); + console.log(JSON.stringify(images)); + return images; + } else { + console.error("Invalid response from server"); + return null; + } + + } catch (error) { + console.error("Error fetching chapters:" + error); + return null; + } +} + +// Credits to someone, idgaf! + +function b64encode(data) { + const keystr = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + function atobLookup(chr) { + const index = keystr.indexOf(chr); + return index < 0 ? undefined : index; + } + + data = `${data}`; + data = data.replace(/[ \t\n\f\r]/g, ""); + if (data.length % 4 === 0) { + data = data.replace(/==?$/, ""); + } + if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) { + return null; + } + let output = ""; + let buffer = 0; + let accumulatedBits = 0; + for (let i = 0; i < data.length; i++) { + buffer <<= 6; + buffer |= atobLookup(data[i]); + accumulatedBits += 6; + if (accumulatedBits === 24) { + output += String.fromCharCode((buffer & 0xff0000) >> 16); + output += String.fromCharCode((buffer & 0xff00) >> 8); + output += String.fromCharCode(buffer & 0xff); + buffer = accumulatedBits = 0; + } + } + if (accumulatedBits === 12) { + buffer >>= 4; + output += String.fromCharCode(buffer); + } else if (accumulatedBits === 18) { + buffer >>= 2; + output += String.fromCharCode((buffer & 0xff00) >> 8); + output += String.fromCharCode(buffer & 0xff); + } + return output; + } + +function b64decode(s) { + const keystr = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + function btoaLookup(index) { + if (index >= 0 && index < 64) { + return keystr[index]; + } + + return undefined; + } + + let i; + s = `${s}`; + for (i = 0; i < s.length; i++) { + if (s.charCodeAt(i) > 255) { + return null; + } + } + let out = ""; + for (i = 0; i < s.length; i += 3) { + const groupsOfSix = [undefined, undefined, undefined, undefined]; + groupsOfSix[0] = s.charCodeAt(i) >> 2; + groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; + if (s.length > i + 1) { + groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4; + groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2; + } + if (s.length > i + 2) { + groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; + groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; + } + for (let j = 0; j < groupsOfSix.length; j++) { + if (typeof groupsOfSix[j] === "undefined") { + out += "="; + } else { + out += btoaLookup(groupsOfSix[j]); + } + } + } + return out; + } + +const toBytes = (str) => Array.from(str, (c) => c.charCodeAt(0) & 0xff); +const fromBytes = (bytes) => + bytes.map((b) => String.fromCharCode(b & 0xff)).join(""); + +function rc4Bytes(key, input) { + const s = Array.from({ length: 256 }, (_, i) => i); + let j = 0; + + for (let i = 0; i < 256; i++) { + j = (j + s[i] + key.charCodeAt(i % key.length)) & 0xff; + [s[i], s[j]] = [s[j], s[i]]; + } + + const out = new Array(input.length); + let i = 0; + j = 0; + for (let y = 0; y < input.length; y++) { + i = (i + 1) & 0xff; + j = (j + s[i]) & 0xff; + [s[i], s[j]] = [s[j], s[i]]; + const k = s[(s[i] + s[j]) & 0xff]; + out[y] = (input[y] ^ k) & 0xff; + } + return out; + } + +function transform(input, initSeedBytes, prefixKeyBytes, prefixLen, schedule) { + const out = []; + for (let i = 0; i < input.length; i++) { + if (i < prefixLen) out.push(prefixKeyBytes[i]); + + out.push( + schedule[i % 10]((input[i] ^ initSeedBytes[i % 32]) & 0xff) & 0xff + ); + } + return out; + } + +const add8 = (n) => (c) => (c + n) & 0xff; +const sub8 = (n) => (c) => (c - n + 256) & 0xff; +const xor8 = (n) => (c) => (c ^ n) & 0xff; +const rotl8 = (n) => (c) => ((c << n) | (c >>> (8 - n))) & 0xff; + +function base64UrlEncodeBytes(bytes) { + const std = b64decode(fromBytes(bytes)); + return std.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); +} + +function bytesFromBase64(b64) { + return toBytes(b64encode(b64)); +} + +const rotr8 = (n) => (c) => ((c >>> n) | (c << (8 - n))) & 0xff; + +function generate_vrf(input) { + const schedule0 = [ + sub8(223), + rotr8(4), + rotr8(4), + add8(234), + rotr8(7), + rotr8(2), + rotr8(7), + sub8(223), + rotr8(7), + rotr8(6), + ]; + + const schedule1 = [ + add8(19), + rotr8(7), + add8(19), + rotr8(6), + add8(19), + rotr8(1), + add8(19), + rotr8(6), + rotr8(7), + rotr8(4), + ]; + + const schedule2 = [ + sub8(223), + rotr8(1), + add8(19), + sub8(223), + rotl8(2), + sub8(223), + add8(19), + rotl8(1), + rotl8(2), + rotl8(1), + ]; + + const schedule3 = [ + add8(19), + rotl8(1), + rotl8(1), + rotr8(1), + add8(234), + rotl8(1), + sub8(223), + rotl8(6), + rotl8(4), + rotl8(1), + ]; + + const schedule4 = [ + rotr8(1), + rotl8(1), + rotl8(6), + rotr8(1), + rotl8(2), + rotr8(4), + rotl8(1), + rotl8(1), + sub8(223), + rotl8(2), + ]; + + const CONST = { + "rc4Keys": [ + "FgxyJUQDPUGSzwbAq/ToWn4/e8jYzvabE+dLMb1XU1o=", + "CQx3CLwswJAnM1VxOqX+y+f3eUns03ulxv8Z+0gUyik=", + "fAS+otFLkKsKAJzu3yU+rGOlbbFVq+u+LaS6+s1eCJs=", + "Oy45fQVK9kq9019+VysXVlz1F9S1YwYKgXyzGlZrijo=", + "aoDIdXezm2l3HrcnQdkPJTDT8+W6mcl2/02ewBHfPzg=", + ], + "seeds32": [ + "yH6MXnMEcDVWO/9a6P9W92BAh1eRLVFxFlWTHUqQ474=", + "RK7y4dZ0azs9Uqz+bbFB46Bx2K9EHg74ndxknY9uknA=", + "rqr9HeTQOg8TlFiIGZpJaxcvAaKHwMwrkqojJCpcvoc=", + "/4GPpmZXYpn5RpkP7FC/dt8SXz7W30nUZTe8wb+3xmU=", + "wsSGSBXKWA9q1oDJpjtJddVxH+evCfL5SO9HZnUDFU8=", + ], + "prefixKeys": [ + "l9PavRg=", + "Ml2v7ag1Jg==", + "i/Va0UxrbMo=", + "WFjKAHGEkQM=", + "5Rr27rWd", + ], + }; + + // Stage 0: normalize to URI-encoded bytes + let bytes = toBytes(encodeURIComponent(input)); + + // RC4 + bytes = rc4Bytes(b64encode(CONST.rc4Keys[0]), bytes); + const prefixKey0 = bytesFromBase64(CONST.prefixKeys[0]); + bytes = transform( + bytes, + bytesFromBase64(CONST.seeds32[0]), + prefixKey0, + prefixKey0.length, + schedule0 + ); + + bytes = rc4Bytes(b64encode(CONST.rc4Keys[1]), bytes); + const prefixKey1 = bytesFromBase64(CONST.prefixKeys[1]); + bytes = transform( + bytes, + bytesFromBase64(CONST.seeds32[1]), + prefixKey1, + prefixKey1.length, + schedule1 + ); + + bytes = rc4Bytes(b64encode(CONST.rc4Keys[2]), bytes); + const prefixKey2 = bytesFromBase64(CONST.prefixKeys[2]); + bytes = transform( + bytes, + bytesFromBase64(CONST.seeds32[2]), + prefixKey2, + prefixKey2.length, + schedule2 + ); + + bytes = rc4Bytes(b64encode(CONST.rc4Keys[3]), bytes); + const prefixKey3 = bytesFromBase64(CONST.prefixKeys[3]); + bytes = transform( + bytes, + bytesFromBase64(CONST.seeds32[3]), + prefixKey3, + prefixKey3.length, + schedule3 + ); + + bytes = rc4Bytes(b64encode(CONST.rc4Keys[4]), bytes); + const prefixKey4 = bytesFromBase64(CONST.prefixKeys[4]); + bytes = transform( + bytes, + bytesFromBase64(CONST.seeds32[4]), + prefixKey4, + prefixKey4.length, + schedule4 + ); + + return base64UrlEncodeBytes(bytes); +} \ No newline at end of file