mirror of
https://github.com/john-okeefe/foliate-js.git
synced 2026-09-09 19:39:13 -04:00
Implement intelligent panel detection for manga/comics using a three-tier fallback system that automatically selects the best detection method: - Tier 1: OpenCV edge detection (fast, accurate for clear panel borders) - Tier 2: COCO-SSD ML detection (handles irregular layouts) - Tier 3: Grid-based detection (lightweight, always works) The system automatically falls back through tiers if higher tiers fail or if CSP blocks 'unsafe-eval' required by ML libraries. Changes: - panel-detection/detector.js: Modified to use dynamic script loading * Calls loadMLLibraries() on first use for lazy loading * Checks if ML libraries loaded successfully before using them * Falls back to grid detection if CSP blocks eval or libraries fail * Uses globalThis.cv/tf/cocoSsd for UMD/global library access - panel-detection/load-scripts.js: Created dynamic script loader * Dynamically injects <script> tags when panel detection enabled * Checks CSP compatibility with canUseEval() function * Loads libraries in correct order: TensorFlow → OpenCV → COCO-SSD * Falls back to grid if ML libraries fail to load * Uses Promise-based API for clean async loading This library-native approach keeps all functionality within foliate-js without requiring changes to reader.html or consumer applications.
75 lines
2.0 KiB
JavaScript
75 lines
2.0 KiB
JavaScript
// panel-detection/load-scripts.js
|
|
// Dynamically loads ML/CV libraries as script tags
|
|
const loadedScripts = new Set();
|
|
export async function loadMLLibraries() {
|
|
// Check if already loaded
|
|
if (globalThis.cv && globalThis.tf && globalThis.cocoSsd) {
|
|
return { loaded: true, method: "cached" };
|
|
}
|
|
|
|
// Check CSP compatibility
|
|
if (!canUseEval()) {
|
|
console.warn("Panel detection requires CSP with unsafe-eval");
|
|
return { loaded: false, reason: "csp-blocked" };
|
|
}
|
|
|
|
const scripts = [
|
|
{ name: "TensorFlow", src: "./vendor/tfjs/tf.min.js", global: "tf" },
|
|
{ name: "OpenCV", src: "./vendor/opencv/opencv.js", global: "cv" },
|
|
{
|
|
name: "COCO-SSD",
|
|
src: "./vendor/coco-ssd/coco-ssd.min.js",
|
|
global: "cocoSsd",
|
|
},
|
|
];
|
|
|
|
try {
|
|
for (const { name, src, global: globalName } of scripts) {
|
|
if (globalThis[globalName]) continue; // Already loaded
|
|
|
|
await loadScript(src);
|
|
loadedScripts.add(src);
|
|
|
|
// Verify global was set
|
|
if (!globalThis[globalName]) {
|
|
throw new Error(`${name} failed to load (global not set)`);
|
|
}
|
|
}
|
|
|
|
return { loaded: true, method: "dynamic" };
|
|
} catch (e) {
|
|
console.warn("Failed to load ML libraries:", e);
|
|
return { loaded: false, reason: e.message };
|
|
}
|
|
}
|
|
function loadScript(src) {
|
|
return new Promise((resolve, reject) => {
|
|
const script = document.createElement("script");
|
|
script.src = src;
|
|
script.onload = () => resolve();
|
|
script.onerror = () => reject(new Error(`Failed to load: ${src}`));
|
|
document.head.appendChild(script);
|
|
});
|
|
}
|
|
function canUseEval() {
|
|
// Try to detect if CSP allows eval
|
|
try {
|
|
const test = new Function("return true")();
|
|
return test === true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
export function getLibrariesStatus() {
|
|
return {
|
|
opencv: !!globalThis.cv,
|
|
tensorflow: !!globalThis.tf,
|
|
cocoSsd: !!globalThis.cocoSsd,
|
|
};
|
|
}
|
|
export function clearScripts() {
|
|
// Note: We don't remove script tags as they can't be unloaded
|
|
// This is for future cleanup if needed
|
|
loadedScripts.clear();
|
|
}
|