From f67930feb16a858e838de19667beee1f2b0515c9 Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Tue, 31 Mar 2026 23:35:14 +0800 Subject: [PATCH] fix(opds): fix Copyparty books showing as "Untitled" in mixed feeds, closes #3667 (#3705) --- .../src/__tests__/utils/opds-feed.test.ts | 159 ++++++++++++++++++ packages/foliate-js | 2 +- 2 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 apps/readest-app/src/__tests__/utils/opds-feed.test.ts diff --git a/apps/readest-app/src/__tests__/utils/opds-feed.test.ts b/apps/readest-app/src/__tests__/utils/opds-feed.test.ts new file mode 100644 index 00000000..803c9ec9 --- /dev/null +++ b/apps/readest-app/src/__tests__/utils/opds-feed.test.ts @@ -0,0 +1,159 @@ +import { describe, it, expect } from 'vitest'; +import { getFeed } from 'foliate-js/opds.js'; + +const MIME_XML = 'application/xml'; + +const parseXML = (xml: string): Document => { + return new DOMParser().parseFromString(xml, MIME_XML as DOMParserSupportedType); +}; + +describe('OPDS feed parsing', () => { + describe('getFeed with mixed navigation and publication entries (Copyparty)', () => { + // Reproduces https://github.com/readest/readest/issues/3667 + // Copyparty mixes folder entries (navigation) and book entries (publications) + // in the same feed. When a folder comes first, all items were classified as + // navigation, causing books to show as "Untitled". + const copypartyRootFeed = ` + + Readest-Test + + + Sub/ + + 2026-03-28T05:37:03Z + + + Children of the Fleet - Orson Scott Card.epub + 2025-11-02T17:50:21Z + + + + +`; + + it('should separate navigation and publication entries in mixed feeds', () => { + const doc = parseXML(copypartyRootFeed); + const feed = getFeed(doc); + + // Should have navigation items (the "Sub/" folder) + expect(feed.navigation).toBeDefined(); + expect(feed.navigation!).toHaveLength(1); + expect(feed.navigation![0].title).toBe('Sub/'); + + // Should have publication items (the book) + expect(feed.publications).toBeDefined(); + expect(feed.publications!).toHaveLength(1); + expect(feed.publications![0].metadata.title).toBe( + 'Children of the Fleet - Orson Scott Card.epub', + ); + }); + + it('should handle feeds with only publications (subfolder case)', () => { + const subFeed = ` + + + + Children of the Fleet - Orson Scott Card.epub + 2025-11-02T17:50:21Z + + + + Earth Afire - Orson Scott Card & Aaron Johnston.epub + 2025-11-02T17:50:22Z + + +`; + + const doc = parseXML(subFeed); + const feed = getFeed(doc); + + expect(feed.navigation).toBeUndefined(); + expect(feed.publications).toBeDefined(); + expect(feed.publications!).toHaveLength(2); + expect(feed.publications![0].metadata.title).toBe( + 'Children of the Fleet - Orson Scott Card.epub', + ); + expect(feed.publications![1].metadata.title).toBe( + 'Earth Afire - Orson Scott Card & Aaron Johnston.epub', + ); + }); + + it('should handle feeds with only navigation entries', () => { + const navFeed = ` + + Root + + Fiction/ + + 2026-03-28T05:37:03Z + + + Non-Fiction/ + + 2026-03-28T05:37:03Z + +`; + + const doc = parseXML(navFeed); + const feed = getFeed(doc); + + expect(feed.publications).toBeUndefined(); + expect(feed.navigation).toBeDefined(); + expect(feed.navigation!).toHaveLength(2); + expect(feed.navigation![0].title).toBe('Fiction/'); + expect(feed.navigation![1].title).toBe('Non-Fiction/'); + }); + + it('should handle navigation entry after publications', () => { + // Reverse order: publications first, then navigation + const reverseFeed = ` + + + Some Book.epub + 2025-11-02T17:50:21Z + + + + Sub/ + + 2026-03-28T05:37:03Z + +`; + + const doc = parseXML(reverseFeed); + const feed = getFeed(doc); + + expect(feed.publications).toBeDefined(); + expect(feed.publications!).toHaveLength(1); + expect(feed.publications![0].metadata.title).toBe('Some Book.epub'); + + expect(feed.navigation).toBeDefined(); + expect(feed.navigation!).toHaveLength(1); + expect(feed.navigation![0].title).toBe('Sub/'); + }); + }); +}); diff --git a/packages/foliate-js b/packages/foliate-js index c5c09a97..348a64a8 160000 --- a/packages/foliate-js +++ b/packages/foliate-js @@ -1 +1 @@ -Subproject commit c5c09a9762505e2caf83729c988181d92be3ce70 +Subproject commit 348a64a8f16d5c06d9da9f9f63940b2d95ab0989