fix(reader): remove the dead converted-PDF fallback for chapter books
Browse filesvalidFallback only accepted PDF objects in the deleted vomebook/Reader-Assets
dataset, while every current sidecar fallback is a CHM-native EPUB in the
reader-assets-v2 bucket, so the redirect to ext=pdf never ran. Remove
ReaderFallback from assetFields and from the backend record and reader-resolve
fields, the fallback reader parameter, validFallback and the chapter-load
redirect; a load failure keeps showing the classified error.
- backend/record_metadata.py +9 -12
- static/app.js +1 -1
- static/reader.js +1 -4
- static/reader/core/reader-contract.js +0 -3
- static/reader/core/reader-source.mjs +0 -16
- static/reader/formats/reader-format-controller.mjs +0 -16
- tests/test_fastapi_endpoints.py +2 -2
- tests/test_reader_backend.py +2 -1
- tests/test_reader_regression.js +1 -1
backend/record_metadata.py
CHANGED
|
@@ -120,17 +120,14 @@ def reader_asset_fields(asset: dict, original_extension: str) -> dict:
|
|
| 120 |
if asset.get("b") == "vomebook/pdf-pages-v2" or asset.get("ob") == "vomebook/pdf-pages-v2" else f"{READER_ASSETS_BASE}{ocr_path}"
|
| 121 |
)
|
| 122 |
fields["ocr_mode"] = str(asset.get("om") or "")
|
| 123 |
-
|
| 124 |
-
if asset.get(
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
+ quote(asset[key], safe="/"))
|
| 132 |
-
else:
|
| 133 |
-
fields[field] = f"{READER_ASSETS_BASE}{asset[key]}"
|
| 134 |
return fields
|
| 135 |
|
| 136 |
|
|
@@ -149,7 +146,7 @@ def enrich_reader_record(record: dict, asset: dict) -> dict:
|
|
| 149 |
"extension": direct_modes[direct_extension][0]}
|
| 150 |
if fields:
|
| 151 |
for source, target in (("url", "ReaderLink"), ("extension", "ReaderExtension"),
|
| 152 |
-
("chapter_manifest", "ReaderChapterManifest"),
|
| 153 |
("ocr_manifest", "ReaderOcrManifest"), ("ocr_mode", "ReaderOcrMode"),
|
| 154 |
("pdf_document", "ReaderPdfDocument")):
|
| 155 |
if source in fields:
|
|
|
|
| 120 |
if asset.get("b") == "vomebook/pdf-pages-v2" or asset.get("ob") == "vomebook/pdf-pages-v2" else f"{READER_ASSETS_BASE}{ocr_path}"
|
| 121 |
)
|
| 122 |
fields["ocr_mode"] = str(asset.get("om") or "")
|
| 123 |
+
if asset.get("c"):
|
| 124 |
+
if asset.get("cb") == "vomebook/reader-assets-v2":
|
| 125 |
+
fields["chapter_manifest"] = ("https://voiceofml-search.hf.space/api/reader-bucket-resource?path="
|
| 126 |
+
+ quote(asset["c"], safe="/"))
|
| 127 |
+
elif asset.get("cb") == "vomebook/pdf-pages-v2":
|
| 128 |
+
fields["chapter_manifest"] = "/api/reader-bucket-resource?path=" + quote(asset["c"], safe="/")
|
| 129 |
+
else:
|
| 130 |
+
fields["chapter_manifest"] = f"{READER_ASSETS_BASE}{asset['c']}"
|
|
|
|
|
|
|
|
|
|
| 131 |
return fields
|
| 132 |
|
| 133 |
|
|
|
|
| 146 |
"extension": direct_modes[direct_extension][0]}
|
| 147 |
if fields:
|
| 148 |
for source, target in (("url", "ReaderLink"), ("extension", "ReaderExtension"),
|
| 149 |
+
("chapter_manifest", "ReaderChapterManifest"),
|
| 150 |
("ocr_manifest", "ReaderOcrManifest"), ("ocr_mode", "ReaderOcrMode"),
|
| 151 |
("pdf_document", "ReaderPdfDocument")):
|
| 152 |
if source in fields:
|
static/app.js
CHANGED
|
@@ -2417,7 +2417,7 @@ function getReaderLink(rec, returnUrl = location.href, cacheMetadata = true) {
|
|
| 2417 |
readerRecord.OcrUrl = `/txt/${encodeRecordPath(VoiceOfMLReader.txtRelativePath(relPath))}`;
|
| 2418 |
}
|
| 2419 |
const readerUrl = VoiceOfMLReader.readerUrl(readerRecord, "/static/reader.html");
|
| 2420 |
-
if (cacheMetadata) try { const readerId = new URL(readerUrl, location.origin).searchParams.get("id"); if (readerId) { const sourceData = { url: readerRecord.ReaderLink || readerRecord.Link, download: readerRecord.Link, title: readerRecord.File, extension: readerRecord.ReaderExtension || readerRecord.Extension, original_extension: readerRecord.Extension, repo: String(readerRecord.Repo || "").split("/").pop(), folder: readerRecord.Folder, chapter_manifest: readerRecord.ReaderChapterManifest || "",
|
| 2421 |
return readerUrl;
|
| 2422 |
}
|
| 2423 |
|
|
|
|
| 2417 |
readerRecord.OcrUrl = `/txt/${encodeRecordPath(VoiceOfMLReader.txtRelativePath(relPath))}`;
|
| 2418 |
}
|
| 2419 |
const readerUrl = VoiceOfMLReader.readerUrl(readerRecord, "/static/reader.html");
|
| 2420 |
+
if (cacheMetadata) try { const readerId = new URL(readerUrl, location.origin).searchParams.get("id"); if (readerId) { const sourceData = { url: readerRecord.ReaderLink || readerRecord.Link, download: readerRecord.Link, title: readerRecord.File, extension: readerRecord.ReaderExtension || readerRecord.Extension, original_extension: readerRecord.Extension, repo: String(readerRecord.Repo || "").split("/").pop(), folder: readerRecord.Folder, chapter_manifest: readerRecord.ReaderChapterManifest || "", pdf_document: readerRecord.ReaderPdfDocument || "" }; cacheReaderMetadata(readerId, sourceData, true); } } catch (_) {}
|
| 2421 |
return readerUrl;
|
| 2422 |
}
|
| 2423 |
|
static/reader.js
CHANGED
|
@@ -154,7 +154,6 @@ const readerContext = {
|
|
| 154 |
get epubSeekTarget() { return epubSeekTarget; }, set epubSeekTarget(value) { epubSeekTarget = value; },
|
| 155 |
get extension() { return extension; }, set extension(value) { extension = value; },
|
| 156 |
get fail() { return fail; }, set fail(value) { fail = value; },
|
| 157 |
-
get fallbackUrl() { return fallbackUrl; }, set fallbackUrl(value) { fallbackUrl = value; },
|
| 158 |
get fetchReaderResponse() { return fetchReaderResponse; }, set fetchReaderResponse(value) { fetchReaderResponse = value; },
|
| 159 |
get fetchReaderUrl() { return fetchReaderUrl; }, set fetchReaderUrl(value) { fetchReaderUrl = value; },
|
| 160 |
get filterPanel() { return filterPanel; }, set filterPanel(value) { filterPanel = value; },
|
|
@@ -428,7 +427,6 @@ const readerContext = {
|
|
| 428 |
get updateSearchState() { return updateSearchState; }, set updateSearchState(value) { updateSearchState = value; },
|
| 429 |
get updateTocCurrentMark() { return updateTocCurrentMark; }, set updateTocCurrentMark(value) { updateTocCurrentMark = value; },
|
| 430 |
get v3ResourceUrl() { return v3ResourceUrl; }, set v3ResourceUrl(value) { v3ResourceUrl = value; },
|
| 431 |
-
get validFallback() { return validFallback; }, set validFallback(value) { validFallback = value; },
|
| 432 |
get validOcr() { return validOcr; }, set validOcr(value) { validOcr = value; },
|
| 433 |
get validSource() { return validSource; }, set validSource(value) { validSource = value; },
|
| 434 |
get validatePageMap() { return validatePageMap; },
|
|
@@ -572,7 +570,7 @@ let {
|
|
| 572 |
let {
|
| 573 |
readerContentUrl, readerDownloadApiUrl, readerDownloadUrl, startReaderDownload,
|
| 574 |
normalizeSourceUrl, resolveReaderId, applyReaderMetadata, applyReaderPathMetadata,
|
| 575 |
-
validSource, validOcr,
|
| 576 |
trustedReaderAssetManifestUrl, trustedChapterUrl, trustedChapterReferenceUrl, trustedChapterResourceUrl
|
| 577 |
} = createReaderSource(readerContext);
|
| 578 |
|
|
@@ -795,7 +793,6 @@ const documentState = readerRuntime.state.document,
|
|
| 795 |
|
| 796 |
readerBindings.initializeDocumentTitle();
|
| 797 |
|
| 798 |
-
let fallbackUrl = params.get("fallback") || "";
|
| 799 |
const ocrUrl = params.get("ocr") || "";
|
| 800 |
let ocrManifestUrl = params.get("ocr_manifest") || "";
|
| 801 |
let returnUrl = params.get("return") || "";
|
|
|
|
| 154 |
get epubSeekTarget() { return epubSeekTarget; }, set epubSeekTarget(value) { epubSeekTarget = value; },
|
| 155 |
get extension() { return extension; }, set extension(value) { extension = value; },
|
| 156 |
get fail() { return fail; }, set fail(value) { fail = value; },
|
|
|
|
| 157 |
get fetchReaderResponse() { return fetchReaderResponse; }, set fetchReaderResponse(value) { fetchReaderResponse = value; },
|
| 158 |
get fetchReaderUrl() { return fetchReaderUrl; }, set fetchReaderUrl(value) { fetchReaderUrl = value; },
|
| 159 |
get filterPanel() { return filterPanel; }, set filterPanel(value) { filterPanel = value; },
|
|
|
|
| 427 |
get updateSearchState() { return updateSearchState; }, set updateSearchState(value) { updateSearchState = value; },
|
| 428 |
get updateTocCurrentMark() { return updateTocCurrentMark; }, set updateTocCurrentMark(value) { updateTocCurrentMark = value; },
|
| 429 |
get v3ResourceUrl() { return v3ResourceUrl; }, set v3ResourceUrl(value) { v3ResourceUrl = value; },
|
|
|
|
| 430 |
get validOcr() { return validOcr; }, set validOcr(value) { validOcr = value; },
|
| 431 |
get validSource() { return validSource; }, set validSource(value) { validSource = value; },
|
| 432 |
get validatePageMap() { return validatePageMap; },
|
|
|
|
| 570 |
let {
|
| 571 |
readerContentUrl, readerDownloadApiUrl, readerDownloadUrl, startReaderDownload,
|
| 572 |
normalizeSourceUrl, resolveReaderId, applyReaderMetadata, applyReaderPathMetadata,
|
| 573 |
+
validSource, validOcr, readerAssetObjectFamily,
|
| 574 |
trustedReaderAssetManifestUrl, trustedChapterUrl, trustedChapterReferenceUrl, trustedChapterResourceUrl
|
| 575 |
} = createReaderSource(readerContext);
|
| 576 |
|
|
|
|
| 793 |
|
| 794 |
readerBindings.initializeDocumentTitle();
|
| 795 |
|
|
|
|
| 796 |
const ocrUrl = params.get("ocr") || "";
|
| 797 |
let ocrManifestUrl = params.get("ocr_manifest") || "";
|
| 798 |
let returnUrl = params.get("return") || "";
|
static/reader/core/reader-contract.js
CHANGED
|
@@ -432,7 +432,6 @@
|
|
| 432 |
? `${bucketBase}?path=${encodeURIComponent(asset.c)}`
|
| 433 |
: assetBase + asset.c
|
| 434 |
: "",
|
| 435 |
-
ReaderFallback: asset.f ? assetBase + asset.f : "",
|
| 436 |
ReaderOcrManifest: ocrUrl || (ocrPath.endsWith("/ocr-manifest.json") ? assetBase + ocrPath : ""),
|
| 437 |
ReaderOcrMode: String(asset.om || ""),
|
| 438 |
ReaderPdfDocument: asset.pd && ["vomebook/reader-assets-v2", "vomebook/pdf-pages-v2"].includes(asset.pdb) &&
|
|
@@ -466,8 +465,6 @@
|
|
| 466 |
params.set("ocr", record.OcrUrl || record.ocrUrl);
|
| 467 |
if (!shortId && (record.ReaderOcrManifest || record.readerOcrManifest))
|
| 468 |
params.set("ocr_manifest", record.ReaderOcrManifest || record.readerOcrManifest);
|
| 469 |
-
if (!shortId && (record.ReaderFallback || record.readerFallback))
|
| 470 |
-
params.set("fallback", record.ReaderFallback || record.readerFallback);
|
| 471 |
if (!shortId && record.ReaderPdfDocument) params.set("pdf_document", record.ReaderPdfDocument);
|
| 472 |
if (
|
| 473 |
!shortId &&
|
|
|
|
| 432 |
? `${bucketBase}?path=${encodeURIComponent(asset.c)}`
|
| 433 |
: assetBase + asset.c
|
| 434 |
: "",
|
|
|
|
| 435 |
ReaderOcrManifest: ocrUrl || (ocrPath.endsWith("/ocr-manifest.json") ? assetBase + ocrPath : ""),
|
| 436 |
ReaderOcrMode: String(asset.om || ""),
|
| 437 |
ReaderPdfDocument: asset.pd && ["vomebook/reader-assets-v2", "vomebook/pdf-pages-v2"].includes(asset.pdb) &&
|
|
|
|
| 465 |
params.set("ocr", record.OcrUrl || record.ocrUrl);
|
| 466 |
if (!shortId && (record.ReaderOcrManifest || record.readerOcrManifest))
|
| 467 |
params.set("ocr_manifest", record.ReaderOcrManifest || record.readerOcrManifest);
|
|
|
|
|
|
|
| 468 |
if (!shortId && record.ReaderPdfDocument) params.set("pdf_document", record.ReaderPdfDocument);
|
| 469 |
if (
|
| 470 |
!shortId &&
|
static/reader/core/reader-source.mjs
CHANGED
|
@@ -180,21 +180,6 @@ export function createReaderSource(readerContext) {
|
|
| 180 |
}
|
| 181 |
}
|
| 182 |
|
| 183 |
-
function validFallback(raw) {
|
| 184 |
-
try {
|
| 185 |
-
const url = new URL(raw);
|
| 186 |
-
return (
|
| 187 |
-
url.protocol === "https:" &&
|
| 188 |
-
["huggingface.co", "hf-mirror.com"].includes(url.hostname) &&
|
| 189 |
-
/\/datasets\/vomebook\/Reader-Assets\/resolve\/[^/]+\/objects\/[0-9a-f]{2}\/[0-9a-f]{64}\/(?:linearized\.pdf|(?:[a-z0-9-]+\/)?document\.pdf)$/.test(
|
| 190 |
-
url.pathname
|
| 191 |
-
)
|
| 192 |
-
);
|
| 193 |
-
} catch (_) {
|
| 194 |
-
return false;
|
| 195 |
-
}
|
| 196 |
-
}
|
| 197 |
-
|
| 198 |
function readerAssetObjectFamily(raw, base = location.href) {
|
| 199 |
try {
|
| 200 |
const url = new URL(raw, base);
|
|
@@ -330,7 +315,6 @@ export function createReaderSource(readerContext) {
|
|
| 330 |
applyReaderPathMetadata,
|
| 331 |
validSource,
|
| 332 |
validOcr,
|
| 333 |
-
validFallback,
|
| 334 |
readerAssetObjectFamily,
|
| 335 |
trustedReaderAssetManifestUrl,
|
| 336 |
trustedChapterUrl,
|
|
|
|
| 180 |
}
|
| 181 |
}
|
| 182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
function readerAssetObjectFamily(raw, base = location.href) {
|
| 184 |
try {
|
| 185 |
const url = new URL(raw, base);
|
|
|
|
| 315 |
applyReaderPathMetadata,
|
| 316 |
validSource,
|
| 317 |
validOcr,
|
|
|
|
| 318 |
readerAssetObjectFamily,
|
| 319 |
trustedReaderAssetManifestUrl,
|
| 320 |
trustedChapterUrl,
|
static/reader/formats/reader-format-controller.mjs
CHANGED
|
@@ -116,7 +116,6 @@ export function createReaderFormatController(readerContext) {
|
|
| 116 |
readerContext.contentUrl = readerContext.readerContentUrl(readerContext.sourceUrl);
|
| 117 |
readerContext.downloadUrl = resolved.download || readerContext.sourceUrl;
|
| 118 |
readerContext.chapterManifestUrl = resolved.chapter_manifest || "";
|
| 119 |
-
readerContext.fallbackUrl = resolved.fallback || "";
|
| 120 |
if (resolved.extension) readerContext.extension = String(resolved.extension).toLowerCase();
|
| 121 |
if (
|
| 122 |
readerContext.extension === "pdf" &&
|
|
@@ -231,21 +230,6 @@ export function createReaderFormatController(readerContext) {
|
|
| 231 |
} catch (error) {
|
| 232 |
if (readerContext.readerAbortController.signal.aborted || error?.name === "AbortError") return;
|
| 233 |
console.error(error);
|
| 234 |
-
if (
|
| 235 |
-
error?.code !== "READER_RESTORE" &&
|
| 236 |
-
readerContext.capability.mode === "epub-chapters" &&
|
| 237 |
-
readerContext.validFallback(readerContext.fallbackUrl)
|
| 238 |
-
) {
|
| 239 |
-
const target = new URL(location.href);
|
| 240 |
-
target.searchParams.set("url", readerContext.fallbackUrl);
|
| 241 |
-
target.searchParams.set("ext", "pdf");
|
| 242 |
-
target.searchParams.delete("chapter_manifest");
|
| 243 |
-
target.searchParams.delete("fallback");
|
| 244 |
-
if (readerContext.isReaderOverlayFrame())
|
| 245 |
-
window.parent.postMessage({ type: "voice-reader-open", url: target.href }, location.origin);
|
| 246 |
-
else location.replace(target.href);
|
| 247 |
-
return;
|
| 248 |
-
}
|
| 249 |
readerContext.fail(
|
| 250 |
error && error.message ? error.message : "原文件加载失败",
|
| 251 |
readerContext.classifyReaderError(error, readerContext.capability.mode === "foliate" ? "READER_CORRUPT" : "READER_PARSE")
|
|
|
|
| 116 |
readerContext.contentUrl = readerContext.readerContentUrl(readerContext.sourceUrl);
|
| 117 |
readerContext.downloadUrl = resolved.download || readerContext.sourceUrl;
|
| 118 |
readerContext.chapterManifestUrl = resolved.chapter_manifest || "";
|
|
|
|
| 119 |
if (resolved.extension) readerContext.extension = String(resolved.extension).toLowerCase();
|
| 120 |
if (
|
| 121 |
readerContext.extension === "pdf" &&
|
|
|
|
| 230 |
} catch (error) {
|
| 231 |
if (readerContext.readerAbortController.signal.aborted || error?.name === "AbortError") return;
|
| 232 |
console.error(error);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
readerContext.fail(
|
| 234 |
error && error.message ? error.message : "原文件加载失败",
|
| 235 |
readerContext.classifyReaderError(error, readerContext.capability.mode === "foliate" ? "READER_CORRUPT" : "READER_PARSE")
|
tests/test_fastapi_endpoints.py
CHANGED
|
@@ -601,7 +601,7 @@ class FastApiEndpointTests(unittest.TestCase):
|
|
| 601 |
self.assertEqual(response.status_code, 200)
|
| 602 |
self.assertEqual(result["ReaderExtension"], "epub-chapters")
|
| 603 |
self.assertTrue(result["ReaderChapterManifest"].endswith(chapter_manifest))
|
| 604 |
-
self.
|
| 605 |
self.assertEqual(result["DownloadLink"], original_link)
|
| 606 |
|
| 607 |
def test_reader_resolve_exposes_chapter_metadata_for_id_reader(self):
|
|
@@ -620,7 +620,7 @@ class FastApiEndpointTests(unittest.TestCase):
|
|
| 620 |
result = response.json()
|
| 621 |
self.assertEqual(result["extension"], "epub-chapters")
|
| 622 |
self.assertTrue(result["chapter_manifest"].endswith(chapter_manifest))
|
| 623 |
-
self.
|
| 624 |
|
| 625 |
def test_reader_static_assets_are_served(self):
|
| 626 |
for path in ("/static/reader.html", "/static/reader.js", "/static/reader.css", "/static/reader/core/reader-contract.js"):
|
|
|
|
| 601 |
self.assertEqual(response.status_code, 200)
|
| 602 |
self.assertEqual(result["ReaderExtension"], "epub-chapters")
|
| 603 |
self.assertTrue(result["ReaderChapterManifest"].endswith(chapter_manifest))
|
| 604 |
+
self.assertNotIn("ReaderFallback", result)
|
| 605 |
self.assertEqual(result["DownloadLink"], original_link)
|
| 606 |
|
| 607 |
def test_reader_resolve_exposes_chapter_metadata_for_id_reader(self):
|
|
|
|
| 620 |
result = response.json()
|
| 621 |
self.assertEqual(result["extension"], "epub-chapters")
|
| 622 |
self.assertTrue(result["chapter_manifest"].endswith(chapter_manifest))
|
| 623 |
+
self.assertNotIn("fallback", result)
|
| 624 |
|
| 625 |
def test_reader_static_assets_are_served(self):
|
| 626 |
for path in ("/static/reader.html", "/static/reader.js", "/static/reader.css", "/static/reader/core/reader-contract.js"):
|
tests/test_reader_backend.py
CHANGED
|
@@ -832,7 +832,8 @@ class ReaderBackendTests(unittest.IsolatedAsyncioTestCase):
|
|
| 832 |
self.assertEqual(resolved["extension"], "pdf-pages")
|
| 833 |
self.assertEqual(resolved["extension"], enriched["ReaderExtension"])
|
| 834 |
self.assertEqual(resolved["chapter_manifest"], enriched["ReaderChapterManifest"])
|
| 835 |
-
self.
|
|
|
|
| 836 |
|
| 837 |
|
| 838 |
if __name__ == "__main__":
|
|
|
|
| 832 |
self.assertEqual(resolved["extension"], "pdf-pages")
|
| 833 |
self.assertEqual(resolved["extension"], enriched["ReaderExtension"])
|
| 834 |
self.assertEqual(resolved["chapter_manifest"], enriched["ReaderChapterManifest"])
|
| 835 |
+
self.assertNotIn("fallback", resolved)
|
| 836 |
+
self.assertNotIn("ReaderFallback", enriched)
|
| 837 |
|
| 838 |
|
| 839 |
if __name__ == "__main__":
|
tests/test_reader_regression.js
CHANGED
|
@@ -138,7 +138,7 @@ const convertedParams = new URL(`https://example.test${convertedUrl}`).searchPar
|
|
| 138 |
assert.strictEqual(convertedParams.get('ext'), 'epub')
|
| 139 |
assert.strictEqual(convertedParams.get('title'), '原始书名.mobi')
|
| 140 |
assert.strictEqual(convertedParams.get('download'), 'https://download.example.test/books/book.mobi')
|
| 141 |
-
assert.strictEqual(convertedParams.get('fallback'),
|
| 142 |
assert.strictEqual(convertedParams.get('chapter_manifest'), 'https://huggingface.co/datasets/vomebook/Reader-Assets/resolve/main/chapters.json')
|
| 143 |
const pageAssetUrl = sandbox.self.VoiceOfMLReader.readerUrl({
|
| 144 |
ReaderLink: 'https://huggingface.co/datasets/vomebook/Reader-Assets/resolve/main/objects/aa/' + 'a'.repeat(64) + '/1234567890abcdef/page-manifest.json',
|
|
|
|
| 138 |
assert.strictEqual(convertedParams.get('ext'), 'epub')
|
| 139 |
assert.strictEqual(convertedParams.get('title'), '原始书名.mobi')
|
| 140 |
assert.strictEqual(convertedParams.get('download'), 'https://download.example.test/books/book.mobi')
|
| 141 |
+
assert.strictEqual(convertedParams.get('fallback'), null)
|
| 142 |
assert.strictEqual(convertedParams.get('chapter_manifest'), 'https://huggingface.co/datasets/vomebook/Reader-Assets/resolve/main/chapters.json')
|
| 143 |
const pageAssetUrl = sandbox.self.VoiceOfMLReader.readerUrl({
|
| 144 |
ReaderLink: 'https://huggingface.co/datasets/vomebook/Reader-Assets/resolve/main/objects/aa/' + 'a'.repeat(64) + '/1234567890abcdef/page-manifest.json',
|