Skip to content

Commit a25b443

Browse files
committed
Added support for .cbr, .cbt and .cb7 using nika-begiashvili/libarchivejs .
1 parent 052123b commit a25b443

File tree

5 files changed

+57
-5
lines changed

5 files changed

+57
-5
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Library for rendering e-books in the browser.
44

55
Features:
6-
- Supports EPUB, MOBI, KF8 (AZW3), FB2, CBZ, PDF (experimental; requires PDF.js)
6+
- Supports EPUB, MOBI, KF8 (AZW3), FB2, CBZ, CBR, CBT, CB7, PDF (experimental; requires PDF.js)
77
- Add support for other formats yourself by implementing the book interface
88
- Pure JavaScript
99
- Small and modular
@@ -32,7 +32,7 @@ This project uses native ES modules. There's no build step, and you can import t
3232
There are mainly three kinds of modules:
3333

3434
- Modules that parse and load books, implementing the "book" interface
35-
- `comic-book.js`, for comic book archives (CBZ)
35+
- `comic-book.js`, for comic book archives (CBZ, CBR, CBT, CB7)
3636
- `epub.js` and `epubcfi.js`, for EPUB
3737
- `fb2.js`, for FictionBook 2
3838
- `mobi.js`, for both Mobipocket files and KF8 (commonly known as AZW3) files
@@ -139,7 +139,7 @@ Note that KF8 files can contain fonts that are zlib-compressed. They need to be
139139

140140
There is a proof-of-concept, highly experimental adapter for [PDF.js](https://mozilla.github.io/pdf.js/), with which you can show PDFs using the same fixed-layout renderer for EPUBs.
141141

142-
CBZs are similarly handled like fixed-layout EPUBs.
142+
CBZs and other comic book formats are similarly handled like fixed-layout EPUBs.
143143

144144
### The Renderers
145145

vendor/libarchive.js

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/libarchive.wasm

979 KB
Binary file not shown.

vendor/worker-bundle.js

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

view.js

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@ const isZip = async file => {
1010
return arr[0] === 0x50 && arr[1] === 0x4b && arr[2] === 0x03 && arr[3] === 0x04
1111
}
1212

13+
const isRar = async file => {
14+
const arr = new Uint8Array(await file.slice(0, 6).arrayBuffer())
15+
return arr[0] === 0x52 && arr[1] === 0x61 && arr[2] === 0x72 && arr[3] === 0x21 && arr[4] === 0x1A && arr[5] === 0x07
16+
}
17+
18+
const isTar = async file => {
19+
let arr = new Uint8Array(await file.slice(257, 262).arrayBuffer());
20+
if (arr[0] === 0x75 && arr[1] === 0x73 && arr[2] === 0x74 && arr[3] === 0x61 && arr[4] === 0x72) { return true; } //tar
21+
arr = new Uint8Array(await file.slice(0, 6).arrayBuffer());
22+
return arr[0] === 0x1F && arr[1] === 0x8B //tar.gz
23+
|| arr[0] === 0xFD && arr[1] === 0x37 && arr[2] === 0x7A && arr[3] === 0x58 && arr[4] === 0x5A && arr[5] === 0x00; //tar.xz
24+
}
25+
26+
const is7zip = async file => {
27+
const arr = new Uint8Array(await file.slice(0, 6).arrayBuffer())
28+
return arr[0] === 0x37 && arr[1] === 0x7A && arr[2] === 0xBC && arr[3] === 0xAF && arr[4] === 0x27 && arr[5] === 0x1C
29+
}
30+
1331
const isPDF = async file => {
1432
const arr = new Uint8Array(await file.slice(0, 5).arrayBuffer())
1533
return arr[0] === 0x25
@@ -18,7 +36,7 @@ const isPDF = async file => {
1836
}
1937

2038
const isCBZ = ({ name, type }) =>
21-
type === 'application/vnd.comicbook+zip' || name.endsWith('.cbz')
39+
type === 'application/x-cbz' || type === 'application/vnd.comicbook+zip' || name.endsWith('.cbz')
2240

2341
const isFB2 = ({ name, type }) =>
2442
type === 'application/x-fictionbook+xml' || name.endsWith('.fb2')
@@ -42,6 +60,23 @@ const makeZipLoader = async file => {
4260
return { entries, loadText, loadBlob, getSize }
4361
}
4462

63+
const makeGenericArchiveLoader = async file => {
64+
const { Archive } = (await import('./vendor/libarchive.js'))
65+
Archive.init({ workerUrl: './modules/ebookplayer/foliate/vendor/worker-bundle.js' })
66+
const archive = await Archive.open(file)
67+
const entries = (await archive.getFilesArray())
68+
.map(entry => ({filename: entry.file.name, file: entry.file}))
69+
const map = new Map(entries.map(entry => [entry.file.name, entry]))
70+
const load = f => (name, ...args) =>
71+
map.has(name) ? f(map.get(name), ...args) : null
72+
const loadText = load(entry => entry.file.extract().text())
73+
const loadBlob = load((entry, type) => !type
74+
? entry.file.extract()
75+
: entry.file.extract().then(data => data.arrayBuffer()).then(ab => new Blob([ab], {type})))
76+
const getSize = name => map.get(name)?.size ?? 0;
77+
return { entries, loadText, loadBlob, getSize }
78+
}
79+
4580
const getFileEntries = async entry => entry.isFile ? entry
4681
: (await Promise.all(Array.from(
4782
await new Promise((resolve, reject) => entry.createReader()
@@ -73,7 +108,7 @@ const fetchFile = async url => {
73108
const res = await fetch(url)
74109
if (!res.ok) throw new ResponseError(
75110
`${res.status} ${res.statusText}`, { cause: res })
76-
return new File([await res.blob()], new URL(res.url).pathname)
111+
return new File([await res.blob()], new URL(res.url).pathname, {type: res.headers.get("content-type") || ""})
77112
}
78113

79114
export const makeBook = async file => {
@@ -85,6 +120,11 @@ export const makeBook = async file => {
85120
book = await new EPUB(loader).init()
86121
}
87122
else if (!file.size) throw new NotFoundError('File not found')
123+
else if (await isRar(file) || await isTar(file) || await is7zip(file)) {
124+
const loader = await makeGenericArchiveLoader(file);
125+
const { makeComicBook } = await import('./comic-book.js')
126+
book = makeComicBook(loader, file);
127+
}
88128
else if (await isZip(file)) {
89129
const loader = await makeZipLoader(file)
90130
if (isCBZ(file)) {

0 commit comments

Comments
 (0)