Add comic panel detection system with multi-tier fallback
- panel-detection.service.ts: Main orchestration with OpenCV → ML → Grid fallback chain - panel-detection.opencv.ts: Edge detection using OpenCV for 80% of comics - panel-detection.ml.ts: COCO-SSD object detection for irregular layouts - panel-detector.ts: Unified detector interface - panel-editor.ts: Manual panel editor UI for user corrections - panel-ml-detector.ts: TensorFlow.js integration for ML detection - page-cache.ts: Efficient page caching for large comics - background-color.ts: Auto-detect comic background color - chapter-markers.ts: Chapter detection and navigation - page-scrubber.ts: Fast page scrubbing/thumbnails - page-order.ts: RTL/LTR page ordering support - panel-gap.ts: Panel gap detection
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
// Page order presets for manga/comics
|
||||
// Auto-detect Japanese vs Western reading order
|
||||
// Allow user override in case detection is wrong
|
||||
// Procedural implementation (no OOP)
|
||||
|
||||
type PageOrderMode = "auto" | "japanese" | "western";
|
||||
|
||||
interface PageOrderConfig {
|
||||
mode: PageOrderMode;
|
||||
detectedOrder: PageOrderMode;
|
||||
userOverride: boolean;
|
||||
}
|
||||
|
||||
interface PageOrderState {
|
||||
config: PageOrderConfig;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
// Detect page order based on filename patterns
|
||||
function detectPageOrder(pageNames: string[]): PageOrderMode {
|
||||
if (pageNames.length < 2) return "western";
|
||||
|
||||
const firstPage = pageNames[0].toLowerCase();
|
||||
const lastPage = pageNames[pageNames.length - 1].toLowerCase();
|
||||
|
||||
const hasFrontCover = /cover|front|001/.test(firstPage);
|
||||
const hasBackCover = /back|end|最后的/.test(lastPage);
|
||||
|
||||
if (hasFrontCover && !hasBackCover) {
|
||||
return "western";
|
||||
}
|
||||
if (hasBackCover && !hasFrontCover) {
|
||||
return "japanese";
|
||||
}
|
||||
|
||||
const chapterMatches = pageNames.filter((n) => /ch-\d+|chapter/i.test(n));
|
||||
if (chapterMatches.length > 0) {
|
||||
const firstChapter = chapterMatches[0];
|
||||
const pageNum = parseInt(firstChapter.match(/\d+/)?.[0] || "0");
|
||||
return pageNum > 0 ? "western" : "japanese";
|
||||
}
|
||||
|
||||
return "western";
|
||||
}
|
||||
|
||||
function createPageOrderState(
|
||||
totalPages: number,
|
||||
pageNames: string[],
|
||||
): PageOrderState {
|
||||
const detectedOrder = detectPageOrder(pageNames);
|
||||
|
||||
return {
|
||||
config: {
|
||||
mode: "auto",
|
||||
detectedOrder,
|
||||
userOverride: false,
|
||||
},
|
||||
totalPages,
|
||||
};
|
||||
}
|
||||
|
||||
function setPageOrderMode(
|
||||
state: PageOrderState,
|
||||
mode: PageOrderMode,
|
||||
): PageOrderState {
|
||||
return {
|
||||
...state,
|
||||
config: {
|
||||
...state.config,
|
||||
mode,
|
||||
userOverride: mode !== "auto",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getPageOrder(state: PageOrderState): PageOrderMode {
|
||||
if (state.config.mode === "auto") {
|
||||
return state.config.detectedOrder;
|
||||
}
|
||||
return state.config.mode;
|
||||
}
|
||||
|
||||
function reorderPages(state: PageOrderState, pageNumbers: number[]): number[] {
|
||||
const order = getPageOrder(state);
|
||||
|
||||
if (order === "japanese") {
|
||||
return [...pageNumbers].reverse();
|
||||
}
|
||||
|
||||
return pageNumbers;
|
||||
}
|
||||
|
||||
function getDisplayPageNumber(
|
||||
state: PageOrderState,
|
||||
actualPage: number,
|
||||
): number {
|
||||
const order = getPageOrder(state);
|
||||
|
||||
if (order === "japanese") {
|
||||
return state.totalPages - actualPage + 1;
|
||||
}
|
||||
|
||||
return actualPage;
|
||||
}
|
||||
Reference in New Issue
Block a user