feat: add progressive EPUB chapter reader
Browse files
app.py
CHANGED
|
@@ -120,7 +120,13 @@ def decode_reader_assets(raw: bytes) -> dict:
|
|
| 120 |
mode = entry.get("m")
|
| 121 |
if mode not in ("p", "e", "d", "h", "a", "v") or not isinstance(path, str) or not re.fullmatch(r"objects/[0-9a-f]{2}/[0-9a-f]{64}/(?:[a-z0-9-]+/)?(?:document\.pdf|book\.epub|document\.docx|document\.html|audio\.mp3|video\.mp4)", path):
|
| 122 |
continue
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
else:
|
| 125 |
clean[key] = {"s": 4}
|
| 126 |
return {"v": 1, "f": clean}
|
|
@@ -391,7 +397,7 @@ def validate_voiceofml_source_url(url: str) -> str:
|
|
| 391 |
|
| 392 |
|
| 393 |
READER_ASSET_SOURCE_RE = re.compile(
|
| 394 |
-
r"^/datasets/vomebook/Reader-Assets/resolve/main/objects/[0-9a-f]{2}/[0-9a-f]{64}/(?:[a-z0-9-]+/)?(?:document\.pdf|book\.epub|document\.docx|document\.html|audio\.mp3|video\.mp4)$"
|
| 395 |
)
|
| 396 |
VOICEOFML_READER_SOURCE_RE = re.compile(r"^/datasets/VoiceOfML/[A-Za-z0-9._-]+/(?:resolve|raw)/main/.+$")
|
| 397 |
|
|
@@ -1418,8 +1424,10 @@ async def api_random_reader(repo: Optional[str] = Query(default=None)):
|
|
| 1418 |
rec["Path"] = rec.get("Path") or build_record_path_url(rec)
|
| 1419 |
asset = converted.get(index)
|
| 1420 |
if asset:
|
| 1421 |
-
rec["ReaderLink"] = f"https://huggingface.co/datasets/vomebook/Reader-Assets/resolve/main/{asset['p']}"
|
| 1422 |
-
rec["ReaderExtension"] = {"p": "pdf", "e": "epub", "d": "docx", "h": "html", "a": "audio", "v": "video"}[asset["m"]]
|
|
|
|
|
|
|
| 1423 |
rec["DownloadLink"] = rec["Link"]
|
| 1424 |
return JSONResponse(rec)
|
| 1425 |
|
|
|
|
| 120 |
mode = entry.get("m")
|
| 121 |
if mode not in ("p", "e", "d", "h", "a", "v") or not isinstance(path, str) or not re.fullmatch(r"objects/[0-9a-f]{2}/[0-9a-f]{64}/(?:[a-z0-9-]+/)?(?:document\.pdf|book\.epub|document\.docx|document\.html|audio\.mp3|video\.mp4)", path):
|
| 122 |
continue
|
| 123 |
+
chapter_manifest = entry.get("c")
|
| 124 |
+
fallback = entry.get("f")
|
| 125 |
+
if chapter_manifest is not None and (mode != "e" or not isinstance(chapter_manifest, str) or not re.fullmatch(r"objects/[0-9a-f]{2}/[0-9a-f]{64}/[a-z0-9-]+/chapter-manifest\.json", chapter_manifest)):
|
| 126 |
+
continue
|
| 127 |
+
if fallback is not None and (not isinstance(fallback, str) or not re.fullmatch(r"objects/[0-9a-f]{2}/[0-9a-f]{64}/[a-z0-9-]+/document\.pdf", fallback)):
|
| 128 |
+
continue
|
| 129 |
+
clean[key] = {"s": 2, "m": mode, "p": path, **({"c": chapter_manifest} if chapter_manifest else {}), **({"f": fallback} if fallback else {})}
|
| 130 |
else:
|
| 131 |
clean[key] = {"s": 4}
|
| 132 |
return {"v": 1, "f": clean}
|
|
|
|
| 397 |
|
| 398 |
|
| 399 |
READER_ASSET_SOURCE_RE = re.compile(
|
| 400 |
+
r"^/datasets/vomebook/Reader-Assets/resolve/main/objects/[0-9a-f]{2}/[0-9a-f]{64}/(?:[a-z0-9-]+/)?(?:chapter-manifest\.json|document\.pdf|book\.epub|document\.docx|document\.html|audio\.mp3|video\.mp4)$"
|
| 401 |
)
|
| 402 |
VOICEOFML_READER_SOURCE_RE = re.compile(r"^/datasets/VoiceOfML/[A-Za-z0-9._-]+/(?:resolve|raw)/main/.+$")
|
| 403 |
|
|
|
|
| 1424 |
rec["Path"] = rec.get("Path") or build_record_path_url(rec)
|
| 1425 |
asset = converted.get(index)
|
| 1426 |
if asset:
|
| 1427 |
+
rec["ReaderLink"] = f"https://huggingface.co/datasets/vomebook/Reader-Assets/resolve/main/{asset.get('c') or asset['p']}"
|
| 1428 |
+
rec["ReaderExtension"] = "epub-chapters" if asset.get("c") else {"p": "pdf", "e": "epub", "d": "docx", "h": "html", "a": "audio", "v": "video"}[asset["m"]]
|
| 1429 |
+
if asset.get("f"):
|
| 1430 |
+
rec["ReaderFallback"] = f"https://huggingface.co/datasets/vomebook/Reader-Assets/resolve/main/{asset['f']}"
|
| 1431 |
rec["DownloadLink"] = rec["Link"]
|
| 1432 |
return JSONResponse(rec)
|
| 1433 |
|