Files
mangabaka-seeder/src/core/model.ts
T

45 lines
2.0 KiB
TypeScript

export type CoverRole = "poster" | "square" | "banner" | "foreground" | "other";
export type ImageType = "volume" | "volume_back" | "chapter" | "season" | "banner" | "audiobook" | "other";
export type ContentRating = "safe" | "suggestive" | "erotica" | "pornographic";
export const CONTENT_RATINGS: readonly ContentRating[] = ["safe", "suggestive", "erotica", "pornographic"];
export type Shape = "tall" | "portrait" | "square" | "wide" | "other";
export type ImageExt = "jpg" | "png" | "gif" | "webp";
/** Wayback history: one snapshot per this many months (spec §4.3). */
export type Granularity = 1 | 3 | 6 | 12;
export const GRANULARITIES: readonly Granularity[] = [1, 3, 6, 12];
export interface SourceSeries {
source: string; // Source.id
externalId: string; // e.g. Webtoons title_no "95"
title: string;
language: string; // MangaBaka language code, e.g. "en", "zh-Hant", or "unknown"
canonicalUrl: string;
archiveUrls: string[]; // URL prefixes (no scheme) to look up in Wayback
mature: boolean;
flags: Record<string, boolean>; // e.g. { canvas: true }
}
export interface CoverCandidate {
url: string; // normalised, hotlinkable
role: CoverRole;
current: boolean; // shown on the live page now
seenAt?: string; // earliest archive timestamp (YYYYMMDDhhmmss) if historical
lastSeenAt?: string; // latest archive timestamp after merging
originalUrl?: string; // as written in the page (absolute), before normalisation
archivedUrl?: string; // Wayback copy of the image itself
}
export interface InspectedCover {
candidate: CoverCandidate;
fetchedFrom: string; // URL that served the bytes (candidate.url, or archivedUrl)
live: boolean; // true if fetchedFrom === candidate.url
width: number;
height: number;
sha256: string; // lowercase hex
contentType: string; // image/jpeg | image/png | image/gif | image/webp
ext: ImageExt;
bytes: Uint8Array;
shape: Shape;
type: ImageType; // suggested MangaBaka type
}