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:
@@ -0,0 +1,53 @@
|
||||
// ¿Tiene el servidor salida a internet? Si las fuentes fallan por red, se comprueba contra varios sitios
|
||||
// a la vez; si ninguno responde, el servidor pasa a "sin conexión": las peticiones a las fuentes fallan
|
||||
// al instante (en vez de esperar timeouts y reintentos), las descargas y comprobaciones se pausan, y la
|
||||
// app trabaja solo con lo descargado. Mientras dure, se vuelve a comprobar cada poco y, al volver la
|
||||
// conexión, se reanuda todo solo.
|
||||
const PROBES = ['https://api.mangadex.org/ping', 'https://www.leercapitulo.co/', 'https://m440.in/', 'https://1.1.1.1/']
|
||||
const EVERY_ONLINE = 3 * 60e3
|
||||
const EVERY_OFFLINE = 20e3
|
||||
|
||||
export function setupConnectivity({ httpError }) {
|
||||
const state = { online: true, since: Date.now(), checkedAt: 0 }
|
||||
const onOnline = []
|
||||
let probing = null
|
||||
|
||||
// Cualquier respuesta HTTP (aunque sea un error) demuestra que hay internet.
|
||||
const probeOne = u => fetch(u, { method: 'HEAD', redirect: 'manual', signal: AbortSignal.timeout(6e3) }).then(() => true, () => false)
|
||||
function check() {
|
||||
probing ??= (async () => {
|
||||
const ok = (await Promise.all(PROBES.map(probeOne))).some(Boolean)
|
||||
state.checkedAt = Date.now()
|
||||
if (ok !== state.online) {
|
||||
state.online = ok
|
||||
state.since = Date.now()
|
||||
console.log(ok ? 'Conexión a internet recuperada: se reanudan descargas y comprobaciones.' : 'Sin conexión a internet: modo sin conexión (solo lo descargado).')
|
||||
if (ok) for (const f of onOnline) { try { f() } catch (e) { console.warn('Reconexión:', e.message) } }
|
||||
}
|
||||
return ok
|
||||
})().finally(() => { probing = null })
|
||||
return probing
|
||||
}
|
||||
// Una fuente falló por red (no por un error HTTP): comprobar si es la fuente o es internet.
|
||||
function failed() {
|
||||
if (state.online && Date.now() - state.checkedAt > 10e3) check()
|
||||
}
|
||||
// Para cortar al instante las peticiones a las fuentes mientras no hay internet.
|
||||
function guard() {
|
||||
if (!state.online) throw httpError(503, 'El servidor no tiene conexión a internet: solo está disponible lo descargado.')
|
||||
}
|
||||
|
||||
// Sin conexión: cada 20 s. En línea solo hace falta confirmar de vez en cuando (los fallos de red ya
|
||||
// disparan la comprobación).
|
||||
setInterval(() => {
|
||||
if (!state.online || Date.now() - state.checkedAt > EVERY_ONLINE) check().catch(() => {})
|
||||
}, EVERY_OFFLINE).unref()
|
||||
check().catch(() => {})
|
||||
|
||||
return {
|
||||
online: () => state.online,
|
||||
status: () => ({ online: state.online, since: state.since, checkedAt: state.checkedAt }),
|
||||
check, failed, guard,
|
||||
whenOnline: f => onOnline.push(f),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user