Download static/reader/formats/reader-format-controller.mjs from VoiceOfML/Search: direct link, hf CLI and curl.
- Browser
- Download file 14.5 kB
-
https://huggingface.co/spaces/VoiceOfML/Search/resolve/main/static/reader/formats/reader-format-controller.mjs
- Command line
-
hf download hf://spaces/VoiceOfML/Search/static/reader/formats/reader-format-controller.mjs
-
curl -L -o reader-format-controller.mjs https://huggingface.co/spaces/VoiceOfML/Search/resolve/main/static/reader/formats/reader-format-controller.mjs
14.5 kB
| export function createReaderFormatController(readerContext) { | |
| function disposeFormatResources(mode) { | |
| if (mode === "epub-chapters") { | |
| readerContext.chapterManifestCleanup?.(); | |
| readerContext.chapterManifestCleanup = null; | |
| } | |
| if (mode === "swf") { | |
| readerContext.swfPlayer?.remove(); | |
| readerContext.swfPlayer = null; | |
| } | |
| if (["pdf", "pdf-pages", "image-pages"].includes(mode)) { | |
| readerContext.pdfDocument = null; | |
| readerContext.pdfHybridUrl = ""; | |
| readerContext.pdfHybridDocumentPromise = null; | |
| readerContext.pdfHybridSearchUnavailable = false; | |
| readerContext.pdfPageManifest = null; | |
| readerContext.pdfOcrManifest = null; | |
| readerContext.pdfOcrManifestPromise = null; | |
| readerContext.pdfV3Repository?.dispose(); readerContext.pdfTextLayerRepository?.dispose(); | |
| for (const shell of readerContext.pdfManifestShells) shell?._v3PreviewRelease?.(); | |
| readerContext.pdfV3Repository = readerContext.pdfTextLayerRepository = null; | |
| readerContext.pdfV3Manifest = readerContext.pdfV3Map = null; | |
| readerContext.pdfTextContentCache.clear(); | |
| readerContext.pdfTextContentPromises.clear(); | |
| readerContext.pdfTextContentCacheBytes = 0; | |
| readerContext.pdfOcrPageCache.clear(); | |
| readerContext.pdfOcrPageCacheBytes = 0; | |
| readerContext.clearPdfManifestPrefetches(); | |
| readerContext.pdfBookSearchClient?.dispose(); | |
| readerContext.pdfBookSearchClient = null; | |
| readerContext.pdfShellWindow?.dispose(); | |
| readerContext.pdfFirstPagePreload?.removeAttribute("src"); | |
| readerContext.pdfFirstPagePreload = null; | |
| for (const canvas of readerContext.content.querySelectorAll(".reader-page canvas")) { | |
| canvas.width = 0; | |
| canvas.height = 0; | |
| } | |
| for (const image of readerContext.content.querySelectorAll(".reader-page img")) image.removeAttribute("src"); | |
| } | |
| if (mode === "foliate") { | |
| readerContext.foliateSectionObserver?.disconnect(); | |
| readerContext.foliateSectionObserver = null; | |
| readerContext.foliateSectionVirtualizer?.dispose(); | |
| readerContext.foliateChapterRepository?.dispose(); | |
| readerContext.foliateSectionVirtualizer = null; | |
| readerContext.foliateChapterRepository = null; | |
| readerContext.foliateSectionLoader = null; | |
| readerContext.foliateSectionSettler = null; | |
| try { | |
| const closing = readerContext.epubRendition?.close?.(); | |
| closing?.catch?.(() => {}); | |
| } catch (_) {} | |
| readerContext.epubRendition = null; | |
| readerContext.epubBook = null; | |
| } | |
| if (mode === "html") { | |
| readerContext.htmlFrame?.remove(); | |
| readerContext.htmlFrame = null; | |
| readerContext.content.classList.remove("reader-content--spreadsheet"); | |
| } | |
| if (["audio", "video"].includes(mode) && readerContext.mediaElement) { | |
| readerContext.mediaElement.pause(); | |
| readerContext.mediaElement.removeAttribute("src"); | |
| readerContext.mediaElement.load(); | |
| readerContext.mediaElement = null; | |
| } | |
| } | |
| function registerReaderFormatAdapters() { | |
| const formats = { | |
| "pdf-pages": [ | |
| () => { | |
| return readerContext.fetchReaderResponse(); | |
| }, | |
| readerContext.renderPdfPages | |
| ], | |
| "image-pages": [ | |
| () => readerContext.fetchReaderResponse(), | |
| readerContext.renderImagePages | |
| ], | |
| pdf: [readerContext.loadPdfDocument, readerContext.renderPdf], | |
| image: [readerContext.loadImageDocument, readerContext.renderImageDocument], | |
| text: [readerContext.loadTextDocument, readerContext.renderTextDocument], | |
| markdown: [readerContext.loadMarkdownDocument, readerContext.renderMarkdownDocument], | |
| html: [readerContext.loadHtmlDocument, readerContext.renderHtml], | |
| "epub-chapters": [readerContext.loadChapterManifestDocument, readerContext.renderChapterManifest], | |
| audio: [readerContext.loadMediaDocument, () => readerContext.renderMedia("audio")], | |
| video: [readerContext.loadMediaDocument, () => readerContext.renderMedia("video")], | |
| swf: [readerContext.loadSwfDocument, readerContext.renderSwf], | |
| foliate: [readerContext.loadMediaDocument, readerContext.renderFoliate] | |
| }; | |
| for (const [mode, [open, render]] of Object.entries(formats)) | |
| readerContext.formatAdapters.register(mode, { | |
| open, | |
| render, | |
| restore: (entry, generation) => readerContext.restoreFormat(mode, entry, generation), | |
| dispose: () => readerContext.disposeFormatResources(mode) | |
| }); | |
| } | |
| async function resolveReaderSource() { | |
| if (!readerContext.readerId || readerContext.sourceUrl) return; | |
| readerContext.setReaderStage("resolve"); | |
| let resolved = readerContext.cachedReaderData; | |
| if (!resolved?.url) { | |
| try { | |
| resolved = await readerContext.resolveReaderId(readerContext.readerId); | |
| } catch (error) { | |
| readerContext.assertReaderActive(); | |
| if (!readerContext.localReaderData?.url) throw error; | |
| resolved = readerContext.localReaderData; | |
| } | |
| } | |
| readerContext.assertReaderActive(); | |
| readerContext.sourceUrl = readerContext.normalizeSourceUrl(resolved.url || ""); | |
| readerContext.contentUrl = readerContext.readerContentUrl(readerContext.sourceUrl); | |
| readerContext.downloadUrl = resolved.download || readerContext.sourceUrl; | |
| readerContext.chapterManifestUrl = resolved.chapter_manifest || ""; | |
| readerContext.fallbackUrl = resolved.fallback || ""; | |
| if (resolved.extension) readerContext.extension = String(resolved.extension).toLowerCase(); | |
| if ( | |
| readerContext.extension === "pdf" && | |
| readerContext.sourceUrl.includes("/api/reader-bucket-resource?") && | |
| new URL(readerContext.sourceUrl).searchParams.get("path")?.endsWith("page-manifest.json") | |
| ) | |
| readerContext.extension = "pdf-pages"; | |
| readerContext.capability = readerContext.readerRuntime.negotiate(VoiceOfMLReader.capability(readerContext.extension)); | |
| if (readerContext.capability.mode !== "pdf" && !readerContext.pdfPrimarySource()) readerContext.releasePdfWorker?.(); | |
| readerContext.readerRuntime.update("source", { | |
| url: readerContext.sourceUrl, | |
| contentUrl: readerContext.contentUrl, | |
| downloadUrl: readerContext.downloadUrl, | |
| extension: readerContext.extension, | |
| metadata: resolved | |
| }); | |
| readerContext.applyReaderMetadata(resolved); | |
| } | |
| async function start() { | |
| const generation = readerContext.readerRuntime.currentGeneration("navigation"); | |
| // A PDF link already tells us which engine is needed. Start its import while | |
| // an ID-only Reader link is resolving its source URL. The promise is reused | |
| // by loadPdfDocument, so this does not create a second module request. | |
| if (readerContext.extension === "pdf" || /\.pdf(?:$|[?#])/i.test(readerContext.sourceUrl)) readerContext.preloadPdfEngine().catch(() => {}); | |
| readerContext.readerSourcePromise = readerContext.measurePdf(readerContext.resolveReaderSource(), "resolve"); | |
| await readerContext.readerSourcePromise; | |
| readerContext.assertReaderActive(); | |
| if (readerContext.readerId && /\/calibre-chm-epub-[^/]+\/document\.epub(?:$|\?)/i.test(readerContext.sourceUrl)) { | |
| readerContext.extension = "epub"; | |
| readerContext.readerRuntime.update("source", { extension: readerContext.extension }); | |
| readerContext.capability = readerContext.readerRuntime.negotiate(VoiceOfMLReader.capability(readerContext.extension)); | |
| readerContext.content.dataset.mode = readerContext.capability.mode; | |
| document.querySelector(".page-controls").hidden = true; | |
| } | |
| readerContext.syncCapabilityControls(); | |
| readerContext.downloadButton.href = readerContext.readerDownloadUrl(readerContext.documentState.title, readerContext.downloadUrl); | |
| if ( | |
| (!readerContext.validSource(readerContext.sourceUrl) && !readerContext.validSource(readerContext.chapterManifestUrl) && !readerContext.readerId) || | |
| readerContext.capability.readerMode === VoiceOfMLReader.ReaderMode.UNSUPPORTED | |
| ) | |
| return readerContext.fail("此文件暂不支持在线阅读,请下载原文件。", "READER_UNSUPPORTED"); | |
| readerContext.formatAdapters.activate(readerContext.capability.mode); | |
| readerContext.applyReaderMetadata(readerContext.resolvedReaderData); | |
| if (readerContext.validOcr(readerContext.ocrUrl)) { | |
| const ocr = document.querySelector("#ocr") || document.createElement("a"); | |
| ocr.id = "ocr"; | |
| ocr.className = "text-button"; | |
| ocr.target = "_blank"; | |
| ocr.rel = "noopener noreferrer"; | |
| ocr.textContent = "OCR"; | |
| if (!ocr.parentElement) document.querySelector(".reader-actions")?.prepend(ocr); | |
| ocr.href = readerContext.ocrUrl; | |
| ocr.hidden = false; | |
| } | |
| try { | |
| if (readerContext.capability.mode === "foliate") { | |
| readerContext.setReaderStage("foliate"); | |
| readerContext.foliateContinuous = true; | |
| const restorationPromise = readerContext.loadReaderRestoration(); | |
| readerContext.readerRestorationPromise = restorationPromise; | |
| await readerContext.awaitReader(readerContext.renderFoliate()); | |
| readerContext.assertReaderActive(); | |
| const restored = await readerContext.awaitReader(restorationPromise); | |
| await readerContext.applyInitialRestoration(restored, generation); | |
| readerContext.initialReaderTargetSection = null; | |
| readerContext.assertReaderActive(); | |
| if (!readerContext.setReaderPhase("ready")) return; | |
| readerContext.updateDocumentState({ restorationReady: !readerContext.restorationFailed }); | |
| readerContext.scheduleSave(); | |
| return; | |
| } | |
| readerContext.setReaderStage("prepare"); | |
| const restorationPromise = readerContext.loadReaderRestoration(); | |
| readerContext.readerRestorationPromise = restorationPromise; | |
| const prepareResources = new Set(readerContext.readerResources); | |
| try { | |
| const prepared = await readerContext.awaitReader(readerContext.formatAdapters.active.open()); | |
| readerContext.assertReaderActive(); | |
| await readerContext.formatAdapters.active.render(prepared); | |
| } catch (error) { | |
| readerContext.assertReaderActive(); | |
| const originalExtension = String(readerContext.resolvedReaderData?.original_extension || "").toLowerCase(); | |
| if (readerContext.capability.mode !== "image-pages" || !readerContext.validSource(readerContext.downloadUrl) || | |
| !["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(originalExtension)) throw error; | |
| for (const cleanup of [...readerContext.readerResources]) { | |
| if (prepareResources.has(cleanup)) continue; | |
| cleanup(); | |
| readerContext.readerResources.delete(cleanup); | |
| } | |
| readerContext.formatAdapters.dispose(); | |
| readerContext.content.replaceChildren(); | |
| readerContext.extension = originalExtension; | |
| readerContext.capability = readerContext.readerRuntime.negotiate(VoiceOfMLReader.capability(readerContext.extension)); | |
| readerContext.readerRuntime.update("source", { extension: readerContext.extension }); | |
| readerContext.updateDocumentState({ page: 1, pageCount: 0 }); | |
| readerContext.formatAdapters.activate(readerContext.capability.mode); | |
| readerContext.syncCapabilityControls(); | |
| await readerContext.renderImageDocument(await readerContext.loadImageDocument(readerContext.downloadUrl)); | |
| } | |
| readerContext.assertReaderActive(); | |
| readerContext.markReaderContentReady(); | |
| const restored = await readerContext.awaitReader(restorationPromise); | |
| await readerContext.applyInitialRestoration(restored, generation); | |
| readerContext.assertReaderActive(); | |
| if (!readerContext.setReaderPhase("ready")) return; | |
| readerContext.updateDocumentState({ restorationReady: !readerContext.restorationFailed }); | |
| readerContext.schedulePdfBookSearchPrefetch(); | |
| readerContext.updateProgressTools(); | |
| if (readerContext.fullSearchInput.value.trim()) readerContext.runFullSearch(); | |
| readerContext.scheduleSave(); | |
| } catch (error) { | |
| if (readerContext.readerAbortController.signal.aborted || error?.name === "AbortError") return; | |
| console.error(error); | |
| if ( | |
| error?.code !== "READER_RESTORE" && | |
| readerContext.capability.mode === "epub-chapters" && | |
| readerContext.validFallback(readerContext.fallbackUrl) | |
| ) { | |
| const target = new URL(location.href); | |
| target.searchParams.set("url", readerContext.fallbackUrl); | |
| target.searchParams.set("ext", "pdf"); | |
| target.searchParams.delete("chapter_manifest"); | |
| target.searchParams.delete("fallback"); | |
| if (readerContext.isReaderOverlayFrame()) | |
| window.parent.postMessage({ type: "voice-reader-open", url: target.href }, location.origin); | |
| else location.replace(target.href); | |
| return; | |
| } | |
| readerContext.fail( | |
| error && error.message ? error.message : "原文件加载失败", | |
| readerContext.classifyReaderError(error, readerContext.capability.mode === "foliate" ? "READER_CORRUPT" : "READER_PARSE") | |
| ); | |
| } | |
| } | |
| function syncCapabilityControls() { | |
| readerContext.content.dataset.mode = readerContext.capability.mode || "unsupported"; | |
| readerContext.pageInvertToggle.hidden = !["pdf", "pdf-pages"].includes(readerContext.capability.mode); | |
| document.querySelector(".page-controls").hidden = !readerContext.capability.features.pagination; | |
| document.querySelector(".zoom-controls").hidden = !readerContext.capability.features.zoom; | |
| readerContext.mediaTab.hidden = !readerContext.capability.features.media; | |
| readerContext.fullSearchButton.hidden = !readerContext.capability.features.search || (readerContext.capability.mode === "pdf-pages" && !readerContext.ocrManifestUrl && (!readerContext.pdfHybridUrl || readerContext.pdfHybridSearchUnavailable)); | |
| readerContext.bookmarkRibbon.hidden = !readerContext.capability.features.bookmarks; | |
| readerContext.readingProgressSummary.querySelector(".reader-progress-bookmark").hidden = | |
| !readerContext.capability.features.bookmarks; | |
| if ( | |
| (readerContext.panelState.selected === "media" && readerContext.mediaTab.hidden) || | |
| (readerContext.panelState.selected === "full-search" && readerContext.fullSearchButton.hidden) | |
| ) | |
| readerContext.selectPanel("toc"); | |
| } | |
| return { | |
| disposeFormatResources, | |
| registerReaderFormatAdapters, | |
| resolveReaderSource, | |
| start, | |
| syncCapabilityControls | |
| }; | |
| } | |