fix(ebook-reader): load SVG images and fix progress update ID
- Add SVG image support: process <image xlink:href=...> elements in addition to HTML <img> tags for cover pages and embedded images - Fix progress update: use 'id' from API response instead of 'media_item_id' which the backend never returns for this endpoint - Update reader context interfaces to include currentPage field - Add debug logging for all resource keys to help troubleshoot image loading issues in future epubs
This commit is contained in:
@@ -28,7 +28,11 @@ interface MangaReader {
|
|||||||
readingDirection: "rtl" | "vertical";
|
readingDirection: "rtl" | "vertical";
|
||||||
}
|
}
|
||||||
|
|
||||||
type CurrentReader = UniversalReader | PDFReader | ComicReader | MangaReader;
|
export type CurrentReader =
|
||||||
|
| UniversalReader
|
||||||
|
| PDFReader
|
||||||
|
| ComicReader
|
||||||
|
| MangaReader;
|
||||||
|
|
||||||
export interface ReaderContext {
|
export interface ReaderContext {
|
||||||
getState: () => {
|
getState: () => {
|
||||||
|
|||||||
@@ -279,6 +279,7 @@ export async function renderSpineItem() {
|
|||||||
if (!container || !spineItem) return;
|
if (!container || !spineItem) return;
|
||||||
// Get the actual content from resources using the href
|
// Get the actual content from resources using the href
|
||||||
const resources = state.currentReader.cif.resources;
|
const resources = state.currentReader.cif.resources;
|
||||||
|
console.log("ALL resource keys:", Array.from(resources.keys()));
|
||||||
const contentBlob = resources?.get(spineItem.content);
|
const contentBlob = resources?.get(spineItem.content);
|
||||||
if (!contentBlob) {
|
if (!contentBlob) {
|
||||||
// Fallback: try to fetch directly if not in resources
|
// Fallback: try to fetch directly if not in resources
|
||||||
@@ -326,16 +327,11 @@ export async function renderSpineItem() {
|
|||||||
// Rewrite image src paths to use blob URLs from resources
|
// Rewrite image src paths to use blob URLs from resources
|
||||||
const modifiedContent = rewriteImageUrls(contentText, resources);
|
const modifiedContent = rewriteImageUrls(contentText, resources);
|
||||||
|
|
||||||
const images = modifiedContent.match(/<img[^>]+>/g);
|
const htmlImages = modifiedContent.match(/<img[^>]+>/g);
|
||||||
console.log("Found images in content:", images);
|
const svgImages =
|
||||||
// Test resource lookup
|
modifiedContent.match(/<image[^>]+xlink:href="([^"]+)"[^>]*>/gi) || [];
|
||||||
console.log("Trying to find image:", {
|
const allImages = [...htmlImages, ...svgImages];
|
||||||
direct: resources.get("image/1.png"),
|
console.log("Found images in content:", allImages);
|
||||||
withoutFolder: resources.get("1.png"),
|
|
||||||
allImageKeys: Array.from(resources.keys()).filter(
|
|
||||||
(k: string) => k.includes("image") || k.includes("png"),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Parse HTML and process images BEFORE setting innerHTML
|
// Parse HTML and process images BEFORE setting innerHTML
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
@@ -354,6 +350,20 @@ export async function renderSpineItem() {
|
|||||||
img.setAttribute("src", blobUrl);
|
img.setAttribute("src", blobUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process SVG <image> elements with xlink:href
|
||||||
|
const svgImgElements = Array.from(doc.querySelectorAll("image"));
|
||||||
|
for (const img of svgImgElements) {
|
||||||
|
const src = img.getAttribute("xlink:href");
|
||||||
|
if (!src) continue;
|
||||||
|
|
||||||
|
let blob = findImageInResources(resources, src);
|
||||||
|
if (blob) {
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
img.setAttribute("xlink:href", blobUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
container.innerHTML = doc.body.innerHTML;
|
container.innerHTML = doc.body.innerHTML;
|
||||||
// Apply reader styling
|
// Apply reader styling
|
||||||
applyReaderTheme();
|
applyReaderTheme();
|
||||||
@@ -498,7 +508,7 @@ function sendProgressUpdate() {
|
|||||||
if (!state.currentReader || !state.readerMetadata) return;
|
if (!state.currentReader || !state.readerMetadata) return;
|
||||||
|
|
||||||
const mediaItemId =
|
const mediaItemId =
|
||||||
state.readerMetadata.media_item_id ||
|
state.readerMetadata.id ||
|
||||||
document.body.dataset.mediaItemId ||
|
document.body.dataset.mediaItemId ||
|
||||||
window.location.pathname.split("/").pop();
|
window.location.pathname.split("/").pop();
|
||||||
|
|
||||||
@@ -541,7 +551,7 @@ function sendProgressUpdate() {
|
|||||||
const percentage = totalPages > 0 ? (currentPage / totalPages) * 100 : 0;
|
const percentage = totalPages > 0 ? (currentPage / totalPages) * 100 : 0;
|
||||||
|
|
||||||
updateReadingProgress(
|
updateReadingProgress(
|
||||||
state.readerMetadata.media_item_id,
|
state.readerMetadata.id,
|
||||||
{
|
{
|
||||||
current_page: currentPage,
|
current_page: currentPage,
|
||||||
total_pages: totalPages,
|
total_pages: totalPages,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ReaderMetadata, ReaderState } from "./reader-context";
|
import type { ReaderState } from "./reader-context";
|
||||||
|
|
||||||
type CurrentReader = import("./reader-context").CurrentReader;
|
type CurrentReader = import("./reader-context").CurrentReader;
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -72,7 +72,7 @@ interface ParserCapabilities {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
interface ReaderMetadata {
|
interface ReaderMetadata {
|
||||||
media_item_id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
author: string;
|
author: string;
|
||||||
cover_image_path: string;
|
cover_image_path: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user