[mangafire] export 'VRF' functions

This commit is contained in:
Mike Fährmann
2026-02-02 11:45:12 +01:00
parent 5c8bea3cc0
commit 39fb51dade
2 changed files with 132 additions and 122 deletions

View File

@@ -11,7 +11,6 @@
from .common import ChapterExtractor, MangaExtractor from .common import ChapterExtractor, MangaExtractor
from .. import text, exception from .. import text, exception
from ..cache import memcache from ..cache import memcache
import binascii
BASE_PATTERN = r"(?:https?://)?(?:www\.)?mangafire\.to" BASE_PATTERN = r"(?:https?://)?(?:www\.)?mangafire\.to"
@@ -53,7 +52,8 @@ class MangafireChapterExtractor(MangafireBase, ChapterExtractor):
def images(self, page): def images(self, page):
url = f"{self.root}/ajax/read/{self.type}/{self.chapter_id}" url = f"{self.root}/ajax/read/{self.type}/{self.chapter_id}"
params = {"vrf": generate_VRF(f"{self.type}@{self.chapter_id}")} params = {"vrf": self.utils("vrf").generate(
f"{self.type}@{self.chapter_id}")}
headers = {"x-requested-with": "XMLHttpRequest"} headers = {"x-requested-with": "XMLHttpRequest"}
data = self.request_json(url, params=params, headers=headers) data = self.request_json(url, params=params, headers=headers)
@@ -131,7 +131,7 @@ def _manga_info(self, manga_path, page=None):
def _manga_chapters(self, manga_info): def _manga_chapters(self, manga_info):
manga_id, type, lang = manga_info manga_id, type, lang = manga_info
url = f"{self.root}/ajax/read/{manga_id}/{type}/{lang}" url = f"{self.root}/ajax/read/{manga_id}/{type}/{lang}"
params = {"vrf": generate_VRF(f"{manga_id}@{type}@{lang}")} params = {"vrf": self.utils("vrf").generate(f"{manga_id}@{type}@{lang}")}
headers = {"x-requested-with": "XMLHttpRequest"} headers = {"x-requested-with": "XMLHttpRequest"}
data = self.request_json(url, params=params, headers=headers) data = self.request_json(url, params=params, headers=headers)
@@ -169,122 +169,3 @@ def _chapter_info(info):
"title" : text.unescape(text.extr(info, 'title="', '"')), "title" : text.unescape(text.extr(info, 'title="', '"')),
"lang" : lang, "lang" : lang,
} }
###############################################################################
# VRF generation utils
#
# adapted from dazedcat19/FMD2
# https://github.com/dazedcat19/FMD2/blob/master/lua/modules/MangaFire.lua
def generate_VRF(input):
input = text.quote(input).encode()
for key_b64, seed_b64, prefix_b64, schedule in (
(key_l, seed_A, prefix_O, schedule_c),
(key_g, seed_V, prefix_v, schedule_y),
(key_B, seed_N, prefix_L, schedule_b),
(key_m, seed_P, prefix_p, schedule_j),
(key_F, seed_k, prefix_W, schedule_e),
):
input = transform(
rc4(binascii.a2b_base64(key_b64), input),
binascii.a2b_base64(seed_b64),
binascii.a2b_base64(prefix_b64),
schedule,
)
return binascii.b2a_base64(bytes(input), newline=False).rstrip(
b"=").replace(b"+", b"-").replace(b"/", b"_")
def transform(input, seed, prefix, schedule):
prefix_len = len(prefix)
out = []
for idx, c in enumerate(input):
if idx < prefix_len:
out.append(prefix[idx] or 0)
out.append(schedule[idx % 10]((c ^ seed[idx % 32]) & 255) & 255)
return out
def rc4(key, input):
lkey = len(key)
j = 0
s = list(range(256))
for i in range(256):
j = (j + s[i] + key[i % lkey]) & 255
s[i], s[j] = s[j], s[i]
out = []
i = j = 0
for c in input:
i = (i + 1) & 255
j = (j + s[i]) & 255
s[i], s[j] = s[j], s[i]
k = s[(s[i] + s[j]) & 255]
out.append(c ^ k)
return out
def add8(n):
return lambda c: (c + n) & 255
def sub8(n):
return lambda c: (c - n + 256) & 255
def xor8(n):
return lambda c: (c ^ n) & 255
def rotl8(n):
return lambda c: ((c << n) | (c >> (8 - n))) & 255
def rotr8(n):
return lambda c: ((c >> n) | (c << (8 - n))) & 255
schedule_c = (
sub8(223), rotr8(4), rotr8(4), add8(234), rotr8(7),
rotr8(2), rotr8(7), sub8(223), rotr8(7), rotr8(6),
)
schedule_y = (
add8(19), rotr8(7), add8(19), rotr8(6), add8(19),
rotr8(1), add8(19), rotr8(6), rotr8(7), rotr8(4),
)
schedule_b = (
sub8(223), rotr8(1), add8(19), sub8(223), rotl8(2),
sub8(223), add8(19), rotl8(1), rotl8(2), rotl8(1),
)
schedule_j = (
add8(19), rotl8(1), rotl8(1), rotr8(1), add8(234),
rotl8(1), sub8(223), rotl8(6), rotl8(4), rotl8(1),
)
schedule_e = (
rotr8(1), rotl8(1), rotl8(6), rotr8(1), rotl8(2),
rotr8(4), rotl8(1), rotl8(1), sub8(223), rotl8(2),
)
key_l = "FgxyJUQDPUGSzwbAq/ToWn4/e8jYzvabE+dLMb1XU1o="
key_g = "CQx3CLwswJAnM1VxOqX+y+f3eUns03ulxv8Z+0gUyik="
key_B = "fAS+otFLkKsKAJzu3yU+rGOlbbFVq+u+LaS6+s1eCJs="
key_m = "Oy45fQVK9kq9019+VysXVlz1F9S1YwYKgXyzGlZrijo="
key_F = "aoDIdXezm2l3HrcnQdkPJTDT8+W6mcl2/02ewBHfPzg="
seed_A = "yH6MXnMEcDVWO/9a6P9W92BAh1eRLVFxFlWTHUqQ474="
seed_V = "RK7y4dZ0azs9Uqz+bbFB46Bx2K9EHg74ndxknY9uknA="
seed_N = "rqr9HeTQOg8TlFiIGZpJaxcvAaKHwMwrkqojJCpcvoc="
seed_P = "/4GPpmZXYpn5RpkP7FC/dt8SXz7W30nUZTe8wb+3xmU="
seed_k = "wsSGSBXKWA9q1oDJpjtJddVxH+evCfL5SO9HZnUDFU8="
prefix_O = "l9PavRg="
prefix_v = "Ml2v7ag1Jg=="
prefix_L = "i/Va0UxrbMo="
prefix_p = "WFjKAHGEkQM="
prefix_W = "5Rr27rWd"

View File

@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Mike Fährmann
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
"""VRF generation utils
adapted from dazedcat19/FMD2
https://github.com/dazedcat19/FMD2/blob/master/lua/modules/MangaFire.lua
"""
from ... import text
import binascii
def generate(input):
input = text.quote(input).encode()
for key_b64, seed_b64, prefix_b64, schedule in (
(key_l, seed_A, prefix_O, schedule_c),
(key_g, seed_V, prefix_v, schedule_y),
(key_B, seed_N, prefix_L, schedule_b),
(key_m, seed_P, prefix_p, schedule_j),
(key_F, seed_k, prefix_W, schedule_e),
):
input = transform(
rc4(binascii.a2b_base64(key_b64), input),
binascii.a2b_base64(seed_b64),
binascii.a2b_base64(prefix_b64),
schedule,
)
return binascii.b2a_base64(bytes(input), newline=False).rstrip(
b"=").replace(b"+", b"-").replace(b"/", b"_")
def transform(input, seed, prefix, schedule):
prefix_len = len(prefix)
out = []
for idx, c in enumerate(input):
if idx < prefix_len:
out.append(prefix[idx] or 0)
out.append(schedule[idx % 10]((c ^ seed[idx % 32]) & 255) & 255)
return out
def rc4(key, input):
lkey = len(key)
j = 0
s = list(range(256))
for i in range(256):
j = (j + s[i] + key[i % lkey]) & 255
s[i], s[j] = s[j], s[i]
out = []
i = j = 0
for c in input:
i = (i + 1) & 255
j = (j + s[i]) & 255
s[i], s[j] = s[j], s[i]
k = s[(s[i] + s[j]) & 255]
out.append(c ^ k)
return out
def add8(n):
return lambda c: (c + n) & 255
def sub8(n):
return lambda c: (c - n + 256) & 255
def xor8(n):
return lambda c: (c ^ n) & 255
def rotl8(n):
return lambda c: ((c << n) | (c >> (8 - n))) & 255
def rotr8(n):
return lambda c: ((c >> n) | (c << (8 - n))) & 255
schedule_c = (
sub8(223), rotr8(4), rotr8(4), add8(234), rotr8(7),
rotr8(2), rotr8(7), sub8(223), rotr8(7), rotr8(6),
)
schedule_y = (
add8(19), rotr8(7), add8(19), rotr8(6), add8(19),
rotr8(1), add8(19), rotr8(6), rotr8(7), rotr8(4),
)
schedule_b = (
sub8(223), rotr8(1), add8(19), sub8(223), rotl8(2),
sub8(223), add8(19), rotl8(1), rotl8(2), rotl8(1),
)
schedule_j = (
add8(19), rotl8(1), rotl8(1), rotr8(1), add8(234),
rotl8(1), sub8(223), rotl8(6), rotl8(4), rotl8(1),
)
schedule_e = (
rotr8(1), rotl8(1), rotl8(6), rotr8(1), rotl8(2),
rotr8(4), rotl8(1), rotl8(1), sub8(223), rotl8(2),
)
key_l = "FgxyJUQDPUGSzwbAq/ToWn4/e8jYzvabE+dLMb1XU1o="
key_g = "CQx3CLwswJAnM1VxOqX+y+f3eUns03ulxv8Z+0gUyik="
key_B = "fAS+otFLkKsKAJzu3yU+rGOlbbFVq+u+LaS6+s1eCJs="
key_m = "Oy45fQVK9kq9019+VysXVlz1F9S1YwYKgXyzGlZrijo="
key_F = "aoDIdXezm2l3HrcnQdkPJTDT8+W6mcl2/02ewBHfPzg="
seed_A = "yH6MXnMEcDVWO/9a6P9W92BAh1eRLVFxFlWTHUqQ474="
seed_V = "RK7y4dZ0azs9Uqz+bbFB46Bx2K9EHg74ndxknY9uknA="
seed_N = "rqr9HeTQOg8TlFiIGZpJaxcvAaKHwMwrkqojJCpcvoc="
seed_P = "/4GPpmZXYpn5RpkP7FC/dt8SXz7W30nUZTe8wb+3xmU="
seed_k = "wsSGSBXKWA9q1oDJpjtJddVxH+evCfL5SO9HZnUDFU8="
prefix_O = "l9PavRg="
prefix_v = "Ml2v7ag1Jg=="
prefix_L = "i/Va0UxrbMo="
prefix_p = "WFjKAHGEkQM="
prefix_W = "5Rr27rWd"