Modo libro 3D, tomos con cubiertas reales y ocultar créditos
Libro 3D (app/public/book3d.js): lector WebGL2 con mesa de madera, cubierta, hojas que giran, cámara real y giroscopio, ahora con más recorrido alrededor del libro. Tomos (app/volumes.js): agrupa los capítulos en un solo libro con portada y contraportada reales. El reparto sale de Wikipedia, MangaDex, M440 y MangaUpdates, contrastados entre sí. Lo que ninguna fuente sitúa se estima, de modo que solo quedan sueltos los capítulos del final, y los fuera de orden se recolocan. Manhwa y manhua no tienen tomos. Ocultar créditos (app/credits.js): botón por obra en la ficha. Al leer por tomos oculta, sin borrarlas, la página cero y las dos últimas de cada capítulo si se repiten en otros capítulos de la obra. HANDOFF-LIBRO3D.md recoge el estado, las decisiones y las pruebas. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+27
-1
@@ -12,6 +12,7 @@
|
||||
// Todo se guarda en la base de datos, una vez por manga y para todos los usuarios: lo guardado se sirve sin
|
||||
// consultar nada y, si es antiguo, se actualiza en segundo plano (si una fuente falla, se queda lo que había).
|
||||
// Los mangas seguidos por alguien se mantienen siempre al día, aunque nadie abra su ficha.
|
||||
import { parseMuVolumes, workTitle } from './volumes.js'
|
||||
const DAY = 24 * 3600e3
|
||||
const INFO_TTL = v => (v?.found ? 30 * DAY : 3 * DAY)
|
||||
const SYNC_EVERY = 3600e3
|
||||
@@ -119,6 +120,31 @@ export function setupMeta({ db, md, sourceOf, fetchMangaFull, HttpError, json, n
|
||||
}, wait)
|
||||
}
|
||||
|
||||
// Las entradas de MangaDex con el título de la obra, en el orden en que las devuelve (primero la más parecida).
|
||||
function mdMatches(mangaId) {
|
||||
return stored(`mdworks:${mangaId}`, v => (v.length ? 30 * DAY : 3 * DAY), async () => {
|
||||
const { manga } = await fetchMangaFull(mangaId, 'es')
|
||||
const title = workTitle(manga.title)
|
||||
const r = await md('/manga', { title, limit: 10, 'contentRating[]': MD_RATINGS }, 6 * 3600e3)
|
||||
return (r?.data || []).filter(m => mdTitles(m).some(t => similar(t, title))).map(m => m.id)
|
||||
}, true)
|
||||
}
|
||||
|
||||
// Capítulo -> tomo según las publicaciones de MangaUpdates. Sirve para las obras licenciadas, que MangaDex deja sin capítulos.
|
||||
async function muVolumes(mangaId) {
|
||||
const l = await links(mangaId, true)
|
||||
if (!l.mu) return {}
|
||||
return stored(`mumap:${mangaId}`, v => (Object.keys(v).length ? 7 * DAY : 3 * DAY), async () => {
|
||||
const rows = []
|
||||
for (let page = 1; page <= 20; page++) {
|
||||
const r = await mu('/releases/search', { search: String(l.mu), search_type: 'series', perpage: 100, page })
|
||||
rows.push(...(r?.results || []).map(x => x.record))
|
||||
if (!r || page * 100 >= r.total_hits) break
|
||||
}
|
||||
return parseMuVolumes(rows)
|
||||
}, true)
|
||||
}
|
||||
|
||||
// ---------- Ficha ----------
|
||||
const AL_FIELDS = `id idMal siteUrl title{romaji english native} synonyms description(asHtml:true) format status
|
||||
startDate{year month day} endDate{year month day} chapters volumes countryOfOrigin isAdult
|
||||
@@ -287,5 +313,5 @@ export function setupMeta({ db, md, sourceOf, fetchMangaFull, HttpError, json, n
|
||||
return true
|
||||
}
|
||||
|
||||
return { route, links, info, cachedInfo, dates }
|
||||
return { route, links, mdMatches, muVolumes, similar, stored, info, cachedInfo, dates }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user