+ {!downloadedBook && acquisitionLinks.length === 0 && streamLinks.length === 0 && (
+ {_('No downloadable format available')}
+ )}
+
{(acquisitionLinks.length > 0 || streamLinks.length > 0) && (
{acquisitionLinks.map(({ rel, links }) => {
diff --git a/apps/readest-app/src/app/opds/page.tsx b/apps/readest-app/src/app/opds/page.tsx
index 1822da35..dae5db59 100644
--- a/apps/readest-app/src/app/opds/page.tsx
+++ b/apps/readest-app/src/app/opds/page.tsx
@@ -329,7 +329,16 @@ export default function BrowserPage() {
}
}
} else {
- const feed = JSON.parse(text);
+ // Defense-in-depth: a non-feed response (e.g. an image returned for a
+ // link that isn't actually a feed) is neither XML nor JSON. Surface a
+ // clear message instead of letting a raw JSON.parse SyntaxError leak
+ // into the error view (readest issue #4599).
+ let feed;
+ try {
+ feed = JSON.parse(text);
+ } catch {
+ throw new Error(_('Content is neither valid XML nor JSON'));
+ }
const newState = {
feed,
baseURL: responseURL,
diff --git a/packages/foliate-js b/packages/foliate-js
index 1309db7e..03913b15 160000
--- a/packages/foliate-js
+++ b/packages/foliate-js
@@ -1 +1 @@
-Subproject commit 1309db7e8ce35d89f23e9da741b2faf3aa59b19c
+Subproject commit 03913b157520814c8cfeb623aa413a24e5ed1df1