johnfactotum / foliate-js

Render e-books in the browser
https://johnfactotum.github.io/foliate-js/reader.html
MIT License
420 stars 60 forks source link

feat: support comic image zip #7

Closed bxb100 closed 1 year ago

bxb100 commented 1 year ago

support comic zip like this (application/zip type and all entries is image type):

download link

johnfactotum commented 1 year ago

So basically what I mean is something like

diff --git a/reader.js b/reader.js
index 304a049..8b4aa16 100644
--- a/reader.js
+++ b/reader.js
@@ -47,16 +47,9 @@ const makeDirectoryLoader = async entry => {
     return { loadText, loadBlob, getSize }
 }

-const isCBZ = ({ name, type }) =>
-    type === 'application/vnd.comicbook+zip' || name.endsWith('.cbz')
-
 const isFB2 = ({ name, type }) =>
     type === 'application/x-fictionbook+xml' || name.endsWith('.fb2')

-const isFBZ = ({ name, type }) =>
-    type === 'application/x-zip-compressed-fb2'
-    || name.endsWith('.fb2.zip') || name.endsWith('.fbz')
-
 const getView = async (file, emit) => {
     let book
     if (file.isDirectory) {
@@ -67,18 +60,20 @@ const getView = async (file, emit) => {
     else if (!file.size) throw new Error('File not found')
     else if (await isZip(file)) {
         const loader = await makeZipLoader(file)
-        if (isCBZ(file)) {
-            const { makeComicBook } = await import('./comic-book.js')
-            book = makeComicBook(loader, file)
-        } else if (isFBZ(file)) {
-            const { makeFB2 } = await import('./fb2.js')
-            const { entries } = loader
-            const entry = entries.find(entry => entry.filename.endsWith('.fb2'))
-            const blob = await loader.loadBlob((entry ?? entries[0]).filename)
-            book = await makeFB2(blob)
-        } else {
+        if ((await loader.loadText('mimetype')) === 'application/epub+zip') {
             const { EPUB } = await import('./epub.js')
             book = await new EPUB(loader).init()
+        } else {
+            const { entries } = loader
+            const fb2 = entries.find(entry => entry.filename.endsWith('.fb2'))
+            if (fb2 || loader.entries.length === 1) {
+                const { makeFB2 } = await import('./fb2.js')
+                const blob = await loader.loadBlob((fb2 ?? entries[0]).filename)
+                book = await makeFB2(blob)
+            } else {
+                const { makeComicBook } = await import('./comic-book.js')
+                book = makeComicBook(loader, file)
+            }
         }
     } else {
         const { isMOBI, MOBI } = await import('./mobi.js')

It does fail in the edge case where the comic book has only one page. But arguably those would be rarer than FBZ files that aren't read with correct encoding, which would cause the filename to end in something other than .fb2.

bxb100 commented 1 year ago

I get you.

johnfactotum commented 1 year ago

On the other hand, though, thinking a bit more about it, perhaps it's not unreasonable to depend on the file extension, after all. For example, one could have a sort of polyglot zip file that is simultaneous a CBZ and FBZ (or indeed even an EPUB), in which case the file extension could be the only thing that decides the type.

The situation is a bit different from Mobipocket and Kindle files, where there isn't a clear correspondence between the various file extensions and the formats, and really all those formats should be considered more like a single type.

In short, I think the current behavior it's okay. It should expect CBZs to use .cbz, and FBZ to use .fbz or fb2.zip. The only case this doesn't work is if you give it a file or blob without file extension. But this should be solved by either renaming the file or, in the case of #3, set the file name on the blob when opening it.

bxb100 commented 1 year ago

I agree with you, but in my case, the file extension is .zip and it should open with comic-book.js

so I minor revision to first check if is epub then check fbz, and default open it as comic-book

johnfactotum commented 1 year ago

Well, I am inclined to close this, as the reader works fine for what it is, which is mainly for demoing and testing the library. Also, the fact that there's no one true way of determining the file type is precisely why I decided to put the part in reader.js, not view.js, so the application can very easily be in control of how the book is loaded. For most applications, I expect that the content type would probably be known in advance (e.g. metadata stored in the app's library database) or detected in other ways (e.g. for a native app, methods provided by the desktop environment).

bxb100 commented 1 year ago

that sounds reasonable, closed