Fuente M440, modo sin conexión (servidor sin internet y app sin servidor), cola del dispositivo en Colas y arreglo del reescalado de tiras altas

- M440 (m440.in, español): búsqueda, populares, actualizados paginados, ficha, capítulos, descargas y seguimiento. La lista de capítulos viene cifrada: la clave se saca de un script ofuscado del sitio ejecutándolo en un proceso aislado con permisos mínimos (m440-key.mjs) y se renueva sola.
- Servidor sin internet (connectivity.js): se detecta en segundos; las fuentes fallan al instante, la portada muestra lo descargado en el servidor con buscador, portadas locales de respaldo, descargas y comprobaciones en pausa y reanudación automática al volver la conexión.
- App sin conexión con el servidor: el service worker usa la copia guardada si el servidor no contesta en 3 s, las peticiones comprueban si el servidor sigue vivo en vez de quedarse colgadas, aviso arriba e inicio con lo guardado en el dispositivo; al volver, se reconecta sola y envía el progreso pendiente.
- Colas: sección «En este dispositivo» con el guardado en curso en directo y cancelación (sin dejar capítulos a medias); también visible sin servidor.
- Imágenes: el proxy y el guardado en el dispositivo reintentan con tiempo límite cuando el CDN de la fuente falla.
- Reescalado HD: las páginas que superan 16383 px al reescalar (tiras de webtoon) se reducen al límite de WebP en vez de hacer fallar el capítulo.
- Entidades HTML con nombre en las sinopsis; pestañas de fuentes cortas en móvil; portadas rotas con icono de respaldo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 09:52:19 -06:00
co-authored by Claude Opus 5.5
parent d3e196a2f3
commit 86999b5725
11 changed files with 712 additions and 78 deletions
+55
View File
@@ -0,0 +1,55 @@
// Extrae la clave con la que M440 cifra su lista de capítulos. La clave está repartida en trozos dentro de
// un script ofuscado (js/ads2.js) que llama a CryptoJS.AES.decrypt(datos, clave, …). En vez de
// desofuscarlo, se ejecuta en un contexto vm vacío con un CryptoJS falso que solo anota la clave.
//
// Se lanza como proceso aparte con el modelo de permisos de Node (sin acceso a archivos salvo este, sin
// procesos hijo ni workers) y con tiempo límite. Dentro del vm no se pasa ningún objeto del proceso
// (todo se define con código dentro del contexto) y la generación de código desde strings está
// desactivada, así que el script no puede alcanzar process ni require.
// Entrada: el script por stdin. Salida: {"key": "..."} por stdout.
import vm from 'node:vm'
let src = ''
process.stdin.setEncoding('utf8')
for await (const chunk of process.stdin) {
src += chunk
if (src.length > 2e6) process.exit(2)
}
const ctx = vm.createContext({}, { codeGeneration: { strings: false, wasm: false } })
vm.runInContext(`
var __key = null;
var window = globalThis, self = globalThis;
var UsaPoncho = '{"ct":"AAAA","iv":"00","s":"00"}';
function mk() {
return new Proxy(function () {}, {
get: function (t, k) {
if (k === Symbol.toPrimitive) return function () { return '' };
if (k === 'toString') return function () { return '[]' };
return mk();
},
apply: function () { return mk() },
construct: function () { return mk() },
});
}
var document = mk(), $ = mk(), jQuery = $, localStorage = mk(), sessionStorage = mk();
var navigator = { userAgent: 'Mozilla/5.0', language: 'es' }, location = { href: 'https://m440.in/', hostname: 'm440.in' };
var setTimeout = function () {}, setInterval = function () {}, clearTimeout = function () {}, clearInterval = function () {};
var console = { log: function () {}, warn: function () {}, error: function () {}, info: function () {}, debug: function () {}, trace: function () {}, table: function () {}, exception: function () {} };
var id = function (s) { return s };
var CryptoJS = {
AES: {
decrypt: function (c, k) { if (__key === null) __key = String(k); return { toString: function () { return '"[]"' } } },
encrypt: mk(),
},
enc: { Utf8: { parse: id, stringify: id }, Hex: { parse: id, stringify: id }, Base64: { parse: id, stringify: id } },
lib: { CipherParams: { create: id }, WordArray: { create: id } },
format: { OpenSSL: {} },
};
`, ctx)
try {
vm.runInContext(src, ctx, { timeout: 5000 })
} catch {}
const key = vm.runInContext('typeof __key === "string" ? __key : ""', ctx)
process.stdout.write(JSON.stringify({ key: typeof key === 'string' ? key.slice(0, 200) : '' }))
process.exit(0)