@prose-reader/streamer 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/archives/createArchiveFromArrayBufferList.d.ts +10 -0
- package/dist/archives/createArchiveFromArrayBufferList.js +35 -0
- package/dist/archives/createArchiveFromJszip.d.ts +34 -0
- package/dist/archives/createArchiveFromJszip.js +26 -0
- package/dist/archives/createArchiveFromText.d.ts +7 -0
- package/dist/archives/createArchiveFromText.js +67 -0
- package/dist/archives/createArchiveFromUrls.d.ts +7 -0
- package/dist/archives/createArchiveFromUrls.js +29 -0
- package/dist/archives/getArchiveOpfInfo.d.ts +15 -0
- package/dist/archives/getArchiveOpfInfo.js +8 -0
- package/dist/archives/types.d.ts +20 -0
- package/dist/archives/types.js +1 -0
- package/dist/generators/manifest.d.ts +10 -0
- package/dist/generators/manifest.js +141 -0
- package/dist/generators/resources.d.ts +2 -0
- package/dist/generators/resources.js +95 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +5709 -0
- package/dist/index.js.LICENSE.txt +10 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/kobo.d.ts +6 -0
- package/dist/parsers/kobo.js +26 -0
- package/dist/parsers/nav.d.ts +5 -0
- package/dist/parsers/nav.js +121 -0
- package/dist/report.d.ts +13 -0
- package/dist/report.js +68 -0
- package/dist/streamer/src/archives/createArchiveFromJszip.d.ts +34 -0
- package/dist/streamer/src/generators/manifest.d.ts +10 -0
- package/dist/streamer/src/generators/resources.d.ts +2 -0
- package/dist/streamer/src/index.d.ts +10 -0
- package/dist/streamer/src/parsers/kobo.d.ts +6 -0
- package/dist/streamer/src/parsers/nav.d.ts +5 -0
- package/dist/utils/blobToBAse64.d.ts +1 -0
- package/dist/utils/blobToBAse64.js +19 -0
- package/dist/utils/sortByTitleComparator.d.ts +1 -0
- package/dist/utils/sortByTitleComparator.js +18 -0
- package/dist/utils/uri.d.ts +1 -0
- package/dist/utils/uri.js +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Archive } from "./types";
|
|
2
|
+
export declare const createArchiveFromArrayBufferList: (list: {
|
|
3
|
+
isDir: boolean;
|
|
4
|
+
name: string;
|
|
5
|
+
size: number;
|
|
6
|
+
data: () => Promise<ArrayBuffer>;
|
|
7
|
+
}[], { orderByAlpha, name }?: {
|
|
8
|
+
orderByAlpha?: boolean | undefined;
|
|
9
|
+
name?: string | undefined;
|
|
10
|
+
}) => Promise<Archive>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { sortByTitleComparator } from "../utils/sortByTitleComparator";
|
|
11
|
+
import { getUriBasename } from "../utils/uri";
|
|
12
|
+
export const createArchiveFromArrayBufferList = (list, { orderByAlpha, name } = {}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
let files = list;
|
|
14
|
+
if (orderByAlpha) {
|
|
15
|
+
files = files.sort((a, b) => sortByTitleComparator(a.name, b.name));
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
filename: name || ``,
|
|
19
|
+
files: files.map(file => ({
|
|
20
|
+
dir: file.isDir,
|
|
21
|
+
basename: getUriBasename(file.name),
|
|
22
|
+
uri: file.name,
|
|
23
|
+
blob: () => __awaiter(void 0, void 0, void 0, function* () { return new Blob([yield file.data()]); }),
|
|
24
|
+
string: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
const data = yield file.data();
|
|
26
|
+
return String.fromCharCode.apply(null, Array.from(new Uint16Array(data)));
|
|
27
|
+
}),
|
|
28
|
+
base64: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
+
// @todo not used for now, lets implement it later if needed
|
|
30
|
+
return ``;
|
|
31
|
+
}),
|
|
32
|
+
size: file.size
|
|
33
|
+
}))
|
|
34
|
+
};
|
|
35
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Archive, StreamResult } from "./types";
|
|
3
|
+
interface OutputByType {
|
|
4
|
+
base64: string;
|
|
5
|
+
string: string;
|
|
6
|
+
text: string;
|
|
7
|
+
binarystring: string;
|
|
8
|
+
array: number[];
|
|
9
|
+
uint8array: Uint8Array;
|
|
10
|
+
arraybuffer: ArrayBuffer;
|
|
11
|
+
blob: Blob;
|
|
12
|
+
nodebuffer: Buffer;
|
|
13
|
+
}
|
|
14
|
+
declare type OutputType = keyof OutputByType;
|
|
15
|
+
interface JSZipObject {
|
|
16
|
+
name: string;
|
|
17
|
+
dir: boolean;
|
|
18
|
+
date: Date;
|
|
19
|
+
comment: string;
|
|
20
|
+
unixPermissions: number | string | null;
|
|
21
|
+
dosPermissions: number | null;
|
|
22
|
+
async<T extends OutputType>(type: T): Promise<OutputByType[T]>;
|
|
23
|
+
internalStream?: (type?: `uint8array`) => StreamResult;
|
|
24
|
+
}
|
|
25
|
+
interface JSZip {
|
|
26
|
+
files: {
|
|
27
|
+
[key: string]: JSZipObject;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare const createArchiveFromJszip: (jszip: JSZip, { orderByAlpha, name }?: {
|
|
31
|
+
orderByAlpha?: boolean | undefined;
|
|
32
|
+
name?: string | undefined;
|
|
33
|
+
}) => Promise<Archive>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { sortByTitleComparator } from "../utils/sortByTitleComparator";
|
|
11
|
+
import { getUriBasename } from "../utils/uri";
|
|
12
|
+
export const createArchiveFromJszip = (jszip, { orderByAlpha, name } = {}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
let files = Object.values(jszip.files);
|
|
14
|
+
if (orderByAlpha) {
|
|
15
|
+
files = files.sort((a, b) => sortByTitleComparator(a.name, b.name));
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
filename: name || ``,
|
|
19
|
+
files: files.map(file => (Object.assign(Object.assign({ dir: file.dir, basename: getUriBasename(file.name), uri: file.name, blob: () => file.async(`blob`), string: () => file.async(`string`), base64: () => file.async(`base64`) }, file.internalStream && {
|
|
20
|
+
stream: file.internalStream
|
|
21
|
+
}), {
|
|
22
|
+
// this is private API
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
size: file._data.uncompressedSize })))
|
|
25
|
+
};
|
|
26
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { blobToBase64 } from "../utils/blobToBAse64";
|
|
11
|
+
import { getUriBasename } from "../utils/uri";
|
|
12
|
+
/**
|
|
13
|
+
* Useful to create archive from txt content
|
|
14
|
+
*/
|
|
15
|
+
export const createArchiveFromText = (content, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
const txtOpfContent = `
|
|
17
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
18
|
+
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="ja" prefix="rendition: http://www.idpf.org/vocab/rendition/#"
|
|
19
|
+
unique-identifier="ootuya-id">
|
|
20
|
+
<metadata xmlns:opf="http://www.idpf.org/2007/opf" xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
21
|
+
xmlns:dcterms="http://purl.org/dc/terms/">
|
|
22
|
+
<meta property="rendition:layout">reflowable</meta>
|
|
23
|
+
</metadata>
|
|
24
|
+
<manifest>
|
|
25
|
+
<item id="p01" href="p01.txt" media-type="text/plain"/>
|
|
26
|
+
</manifest>
|
|
27
|
+
<spine page-progression-direction="${(options === null || options === void 0 ? void 0 : options.direction) || `ltr`}">
|
|
28
|
+
<itemref idref="p01" />
|
|
29
|
+
</spine>
|
|
30
|
+
</package>
|
|
31
|
+
`;
|
|
32
|
+
const archive = {
|
|
33
|
+
filename: `content.txt`,
|
|
34
|
+
files: [{
|
|
35
|
+
dir: false,
|
|
36
|
+
basename: getUriBasename(`generated.opf`),
|
|
37
|
+
uri: `generated.opf`,
|
|
38
|
+
blob: () => __awaiter(void 0, void 0, void 0, function* () { return new Blob([txtOpfContent]); }),
|
|
39
|
+
string: () => __awaiter(void 0, void 0, void 0, function* () { return txtOpfContent; }),
|
|
40
|
+
base64: () => __awaiter(void 0, void 0, void 0, function* () { return btoa(txtOpfContent); }),
|
|
41
|
+
size: 0
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
dir: false,
|
|
45
|
+
basename: getUriBasename(`p01.txt`),
|
|
46
|
+
uri: `p01.txt`,
|
|
47
|
+
blob: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
+
if (typeof content === `string`)
|
|
49
|
+
return new Blob([content]);
|
|
50
|
+
return content;
|
|
51
|
+
}),
|
|
52
|
+
string: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
53
|
+
if (typeof content === `string`)
|
|
54
|
+
return content;
|
|
55
|
+
return content.text();
|
|
56
|
+
}),
|
|
57
|
+
base64: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
58
|
+
if (typeof content === `string`)
|
|
59
|
+
return btoa(content);
|
|
60
|
+
return blobToBase64(content);
|
|
61
|
+
}),
|
|
62
|
+
size: typeof content === `string` ? content.length : content.size,
|
|
63
|
+
encodingFormat: `text/plain`
|
|
64
|
+
}]
|
|
65
|
+
};
|
|
66
|
+
return archive;
|
|
67
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Archive } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* @important
|
|
4
|
+
* Make sure the urls are on the same origin or the cors header is set otherwise
|
|
5
|
+
* the resource cannot be consumed as it is on the web.
|
|
6
|
+
*/
|
|
7
|
+
export declare const createArchiveFromUrls: (urls: string[]) => Promise<Archive>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { getUriBasename } from "../utils/uri";
|
|
11
|
+
/**
|
|
12
|
+
* @important
|
|
13
|
+
* Make sure the urls are on the same origin or the cors header is set otherwise
|
|
14
|
+
* the resource cannot be consumed as it is on the web.
|
|
15
|
+
*/
|
|
16
|
+
export const createArchiveFromUrls = (urls) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
|
+
return {
|
|
18
|
+
filename: ``,
|
|
19
|
+
files: urls.map(url => ({
|
|
20
|
+
dir: false,
|
|
21
|
+
basename: getUriBasename(url),
|
|
22
|
+
uri: url,
|
|
23
|
+
size: 100 / urls.length,
|
|
24
|
+
base64: () => __awaiter(void 0, void 0, void 0, function* () { return ``; }),
|
|
25
|
+
blob: () => __awaiter(void 0, void 0, void 0, function* () { return new Blob(); }),
|
|
26
|
+
string: () => __awaiter(void 0, void 0, void 0, function* () { return ``; })
|
|
27
|
+
}))
|
|
28
|
+
};
|
|
29
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Archive } from "./types";
|
|
2
|
+
export declare const getArchiveOpfInfo: (archive: Archive) => {
|
|
3
|
+
data: {
|
|
4
|
+
dir: boolean;
|
|
5
|
+
basename: string;
|
|
6
|
+
uri: string;
|
|
7
|
+
blob: () => Promise<Blob>;
|
|
8
|
+
string: () => Promise<string>;
|
|
9
|
+
base64: () => Promise<string>;
|
|
10
|
+
stream?: (() => import("./types").StreamResult) | undefined;
|
|
11
|
+
size: number;
|
|
12
|
+
encodingFormat?: "text/plain" | undefined;
|
|
13
|
+
} | undefined;
|
|
14
|
+
basePath: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const getArchiveOpfInfo = (archive) => {
|
|
2
|
+
const filesAsArray = Object.values(archive.files).filter(file => !file.dir);
|
|
3
|
+
const file = filesAsArray.find(file => file.uri.endsWith(`.opf`));
|
|
4
|
+
return {
|
|
5
|
+
data: file,
|
|
6
|
+
basePath: (file === null || file === void 0 ? void 0 : file.uri.substring(0, file.uri.lastIndexOf(`/`))) || ``
|
|
7
|
+
};
|
|
8
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface StreamResult {
|
|
2
|
+
on(e: `data`, cb: (data: Uint8Array) => void): void;
|
|
3
|
+
on(e: `error`, cb: (error: Error) => void): void;
|
|
4
|
+
on(e: `end`, cb: () => void): void;
|
|
5
|
+
resume(): void;
|
|
6
|
+
}
|
|
7
|
+
export declare type Archive = {
|
|
8
|
+
filename: string;
|
|
9
|
+
files: {
|
|
10
|
+
dir: boolean;
|
|
11
|
+
basename: string;
|
|
12
|
+
uri: string;
|
|
13
|
+
blob: () => Promise<Blob>;
|
|
14
|
+
string: () => Promise<string>;
|
|
15
|
+
base64: () => Promise<string>;
|
|
16
|
+
stream?: () => StreamResult;
|
|
17
|
+
size: number;
|
|
18
|
+
encodingFormat?: undefined | `text/plain`;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import xmldoc from 'xmldoc';
|
|
2
|
+
import { Archive } from '..';
|
|
3
|
+
export declare const getManifestFromArchive: (archive: Archive, { baseUrl }?: {
|
|
4
|
+
baseUrl?: string | undefined;
|
|
5
|
+
}) => Promise<Response>;
|
|
6
|
+
export declare const getItemsFromDoc: (doc: xmldoc.XmlDocument) => {
|
|
7
|
+
href: string;
|
|
8
|
+
id: string;
|
|
9
|
+
mediaType: string | undefined;
|
|
10
|
+
}[];
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import xmldoc from 'xmldoc';
|
|
11
|
+
import { parseToc } from '../parsers/nav';
|
|
12
|
+
import { extractKoboInformationFromArchive } from '../parsers/kobo';
|
|
13
|
+
import { Report } from '../report';
|
|
14
|
+
import { getArchiveOpfInfo } from '..';
|
|
15
|
+
const generateManifestFromEpub = (archive, baseUrl) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
var _a;
|
|
17
|
+
const { data: opsFile, basePath: opfBasePath } = getArchiveOpfInfo(archive) || {};
|
|
18
|
+
const koboInformation = yield extractKoboInformationFromArchive(archive);
|
|
19
|
+
if (!opsFile) {
|
|
20
|
+
throw new Error(`No opf content`);
|
|
21
|
+
}
|
|
22
|
+
const data = yield opsFile.string();
|
|
23
|
+
Report.log(data, koboInformation);
|
|
24
|
+
const opfXmlDoc = new xmldoc.XmlDocument(data);
|
|
25
|
+
const toc = (yield parseToc(opfXmlDoc, archive, { baseUrl })) || [];
|
|
26
|
+
const metadataElm = opfXmlDoc.childNamed(`metadata`);
|
|
27
|
+
const manifestElm = opfXmlDoc.childNamed(`manifest`);
|
|
28
|
+
const spineElm = opfXmlDoc.childNamed(`spine`);
|
|
29
|
+
const guideElm = opfXmlDoc.childNamed(`guide`);
|
|
30
|
+
const titleElm = metadataElm === null || metadataElm === void 0 ? void 0 : metadataElm.childNamed(`dc:title`);
|
|
31
|
+
const metaElmChildren = (metadataElm === null || metadataElm === void 0 ? void 0 : metadataElm.childrenNamed(`meta`)) || [];
|
|
32
|
+
const metaElmWithRendition = metaElmChildren.find(meta => meta.attr.property === `rendition:layout`);
|
|
33
|
+
const metaElmWithRenditionFlow = metaElmChildren.find(meta => meta.attr.property === `rendition:flow`);
|
|
34
|
+
const metaElmWithRenditionSpread = metaElmChildren.find(meta => meta.attr.property === `rendition:spread`);
|
|
35
|
+
const publisherRenditionLayout = metaElmWithRendition === null || metaElmWithRendition === void 0 ? void 0 : metaElmWithRendition.val;
|
|
36
|
+
const publisherRenditionFlow = metaElmWithRenditionFlow === null || metaElmWithRenditionFlow === void 0 ? void 0 : metaElmWithRenditionFlow.val;
|
|
37
|
+
const renditionSpread = metaElmWithRenditionSpread === null || metaElmWithRenditionSpread === void 0 ? void 0 : metaElmWithRenditionSpread.val;
|
|
38
|
+
const title = (titleElm === null || titleElm === void 0 ? void 0 : titleElm.val) || ((_a = archive.files.find(({ dir }) => dir)) === null || _a === void 0 ? void 0 : _a.basename) || ``;
|
|
39
|
+
const pageProgressionDirection = spineElm === null || spineElm === void 0 ? void 0 : spineElm.attr[`page-progression-direction`];
|
|
40
|
+
const spineItemIds = spineElm === null || spineElm === void 0 ? void 0 : spineElm.childrenNamed(`itemref`).map((item) => item.attr.idref);
|
|
41
|
+
const manifestItemsFromSpine = (manifestElm === null || manifestElm === void 0 ? void 0 : manifestElm.childrenNamed(`item`).filter((item) => spineItemIds.includes(item.attr.id || ``))) || [];
|
|
42
|
+
const archiveSpineItems = archive.files.filter(file => {
|
|
43
|
+
return manifestItemsFromSpine.find(item => {
|
|
44
|
+
if (!opfBasePath)
|
|
45
|
+
return `${item.attr.href}` === file.uri;
|
|
46
|
+
return `${opfBasePath}/${item.attr.href}` === file.uri;
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
const totalSize = archiveSpineItems.reduce((size, file) => file.size + size, 1);
|
|
50
|
+
return {
|
|
51
|
+
filename: archive.filename,
|
|
52
|
+
nav: {
|
|
53
|
+
toc
|
|
54
|
+
},
|
|
55
|
+
renditionLayout: publisherRenditionLayout || koboInformation.renditionLayout || `reflowable`,
|
|
56
|
+
renditionFlow: publisherRenditionFlow || `auto`,
|
|
57
|
+
renditionSpread,
|
|
58
|
+
title,
|
|
59
|
+
readingDirection: pageProgressionDirection || `ltr`,
|
|
60
|
+
spineItems: (spineElm === null || spineElm === void 0 ? void 0 : spineElm.childrenNamed(`itemref`).map((itemrefElm) => {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
const manifestItem = manifestElm === null || manifestElm === void 0 ? void 0 : manifestElm.childrenNamed(`item`).find((item) => item.attr.id === (itemrefElm === null || itemrefElm === void 0 ? void 0 : itemrefElm.attr.idref));
|
|
63
|
+
const href = (manifestItem === null || manifestItem === void 0 ? void 0 : manifestItem.attr.href) || ``;
|
|
64
|
+
const properties = (((_a = itemrefElm === null || itemrefElm === void 0 ? void 0 : itemrefElm.attr.properties) === null || _a === void 0 ? void 0 : _a.split(` `)) || []);
|
|
65
|
+
const itemSize = ((_b = archive.files.find(file => file.uri.endsWith(href))) === null || _b === void 0 ? void 0 : _b.size) || 0;
|
|
66
|
+
return Object.assign(Object.assign({ id: (manifestItem === null || manifestItem === void 0 ? void 0 : manifestItem.attr.id) || ``, path: opfBasePath ? `${opfBasePath}/${manifestItem === null || manifestItem === void 0 ? void 0 : manifestItem.attr.href}` : `${manifestItem === null || manifestItem === void 0 ? void 0 : manifestItem.attr.href}`,
|
|
67
|
+
// href: opfBasePath ? `${baseUrl}/${opfBasePath}/${manifestItem?.attr['href']}` : `${baseUrl}/${manifestItem?.attr['href']}`,
|
|
68
|
+
href: opfBasePath ? `${baseUrl}/${opfBasePath}/${manifestItem === null || manifestItem === void 0 ? void 0 : manifestItem.attr.href}` : `${baseUrl}/${manifestItem === null || manifestItem === void 0 ? void 0 : manifestItem.attr.href}`, renditionLayout: publisherRenditionLayout || `reflowable` }, properties.find(property => property === `rendition:layout-reflowable`) && {
|
|
69
|
+
renditionLayout: `reflowable`
|
|
70
|
+
}), { progressionWeight: itemSize / totalSize, pageSpreadLeft: properties.some(property => property === `page-spread-left`) || undefined, pageSpreadRight: properties.some(property => property === `page-spread-right`) || undefined,
|
|
71
|
+
// size: itemSize
|
|
72
|
+
mediaType: manifestItem === null || manifestItem === void 0 ? void 0 : manifestItem.attr[`media-type`] });
|
|
73
|
+
})) || [],
|
|
74
|
+
items: getItemsFromDoc(opfXmlDoc),
|
|
75
|
+
guide: guideElm === null || guideElm === void 0 ? void 0 : guideElm.childrenNamed(`reference`).map(elm => {
|
|
76
|
+
return {
|
|
77
|
+
href: elm.attr.href || ``,
|
|
78
|
+
title: elm.attr.title || ``,
|
|
79
|
+
type: elm.attr.type
|
|
80
|
+
};
|
|
81
|
+
})
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Create a manifest from a generic archive.
|
|
86
|
+
* Will use direct
|
|
87
|
+
*/
|
|
88
|
+
const generateManifestFromArchive = (archive, baseUrl) => __awaiter(void 0, void 0, void 0, function* () {
|
|
89
|
+
var _b;
|
|
90
|
+
const files = Object.values(archive.files).filter(file => !file.dir);
|
|
91
|
+
return {
|
|
92
|
+
filename: archive.filename,
|
|
93
|
+
nav: {
|
|
94
|
+
toc: []
|
|
95
|
+
},
|
|
96
|
+
title: ((_b = archive.files.find(({ dir }) => dir)) === null || _b === void 0 ? void 0 : _b.basename.replace(/\/$/, ``)) || ``,
|
|
97
|
+
renditionLayout: `pre-paginated`,
|
|
98
|
+
renditionSpread: `auto`,
|
|
99
|
+
readingDirection: `ltr`,
|
|
100
|
+
spineItems: files.map((file) => ({
|
|
101
|
+
id: file.basename,
|
|
102
|
+
path: `${file.uri}`,
|
|
103
|
+
href: baseUrl ? `${baseUrl}/${file.uri}` : file.uri,
|
|
104
|
+
renditionLayout: `pre-paginated`,
|
|
105
|
+
progressionWeight: (1 / files.length),
|
|
106
|
+
pageSpreadLeft: undefined,
|
|
107
|
+
pageSpreadRight: undefined
|
|
108
|
+
})),
|
|
109
|
+
items: files.map((file) => ({
|
|
110
|
+
id: file.basename,
|
|
111
|
+
href: baseUrl ? `${baseUrl}/${file.uri}` : file.uri
|
|
112
|
+
}))
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
export const getManifestFromArchive = (archive, { baseUrl = `` } = {}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
116
|
+
var _c;
|
|
117
|
+
const { data: opsFile } = getArchiveOpfInfo(archive) || {};
|
|
118
|
+
try {
|
|
119
|
+
if (opsFile) {
|
|
120
|
+
const manifest = yield generateManifestFromEpub(archive, baseUrl);
|
|
121
|
+
const data = JSON.stringify(manifest);
|
|
122
|
+
return new Response(data, { status: 200 });
|
|
123
|
+
}
|
|
124
|
+
const manifest = yield generateManifestFromArchive(archive, baseUrl);
|
|
125
|
+
const data = JSON.stringify(manifest);
|
|
126
|
+
return new Response(data, { status: 200 });
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
Report.error(e);
|
|
130
|
+
return new Response((_c = e) === null || _c === void 0 ? void 0 : _c.message, { status: 500 });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
export const getItemsFromDoc = (doc) => {
|
|
134
|
+
var _a;
|
|
135
|
+
const manifestElm = doc.childNamed(`manifest`);
|
|
136
|
+
return ((_a = manifestElm === null || manifestElm === void 0 ? void 0 : manifestElm.childrenNamed(`item`)) === null || _a === void 0 ? void 0 : _a.map((el) => ({
|
|
137
|
+
href: el.attr.href || ``,
|
|
138
|
+
id: el.attr.id || ``,
|
|
139
|
+
mediaType: el.attr[`media-type`]
|
|
140
|
+
}))) || [];
|
|
141
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import xmldoc from 'xmldoc';
|
|
11
|
+
import { getArchiveOpfInfo } from '..';
|
|
12
|
+
import { getItemsFromDoc } from "./manifest";
|
|
13
|
+
export const getResourceFromArchive = (archive, resourcePath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
|
+
const file = Object.values(archive.files).find(file => file.uri === resourcePath);
|
|
15
|
+
const metadata = yield getMetadata(archive, resourcePath);
|
|
16
|
+
if (!file) {
|
|
17
|
+
throw new Error(`no file found`);
|
|
18
|
+
}
|
|
19
|
+
const blob = yield file.blob();
|
|
20
|
+
// if (file.stream) {
|
|
21
|
+
// const stream = file.stream()
|
|
22
|
+
// console.log(file, stream)
|
|
23
|
+
// stream.on(`data`, data => {
|
|
24
|
+
// console.log(`data`, data)
|
|
25
|
+
// })
|
|
26
|
+
// stream.on(`error`, data => {
|
|
27
|
+
// console.error(`error`, data)
|
|
28
|
+
// })
|
|
29
|
+
// stream.on(`end`, () => {
|
|
30
|
+
// console.log(`end`)
|
|
31
|
+
// })
|
|
32
|
+
// }
|
|
33
|
+
// const stream = file.stream!()
|
|
34
|
+
// const readableStream = new ReadableStream({
|
|
35
|
+
// start(controller) {
|
|
36
|
+
// function push() {
|
|
37
|
+
// stream.on(`data`, data => {
|
|
38
|
+
// controller.enqueue(data)
|
|
39
|
+
// })
|
|
40
|
+
// stream.on(`error`, data => {
|
|
41
|
+
// console.error(`error`, data)
|
|
42
|
+
// })
|
|
43
|
+
// stream.on(`end`, () => {
|
|
44
|
+
// controller.close()
|
|
45
|
+
// })
|
|
46
|
+
// stream.resume()
|
|
47
|
+
// }
|
|
48
|
+
// push();
|
|
49
|
+
// }
|
|
50
|
+
// })
|
|
51
|
+
const response = new Response(blob, {
|
|
52
|
+
headers: Object.assign(Object.assign(Object.assign({}, blob.type && {
|
|
53
|
+
'Content-Type': blob.type
|
|
54
|
+
}), file.encodingFormat && {
|
|
55
|
+
'Content-Type': file.encodingFormat
|
|
56
|
+
}), metadata.mediaType && {
|
|
57
|
+
'Content-Type': metadata.mediaType
|
|
58
|
+
}
|
|
59
|
+
// 'Cache-Control': `no-cache, no-store, no-transform`
|
|
60
|
+
)
|
|
61
|
+
});
|
|
62
|
+
return response;
|
|
63
|
+
});
|
|
64
|
+
const getMetadata = (archive, resourcePath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
const opfInfo = getArchiveOpfInfo(archive);
|
|
67
|
+
const data = yield ((_a = opfInfo.data) === null || _a === void 0 ? void 0 : _a.string());
|
|
68
|
+
if (data) {
|
|
69
|
+
const opfXmlDoc = new xmldoc.XmlDocument(data);
|
|
70
|
+
const items = getItemsFromDoc(opfXmlDoc);
|
|
71
|
+
return {
|
|
72
|
+
mediaType: (_b = items.find((item) => resourcePath.endsWith(item.href))) === null || _b === void 0 ? void 0 : _b.mediaType
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
mediaType: getContentTypeFromExtension(resourcePath)
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
const getContentTypeFromExtension = (uri) => {
|
|
80
|
+
if (uri.endsWith(`.css`)) {
|
|
81
|
+
return `text/css; charset=UTF-8`;
|
|
82
|
+
}
|
|
83
|
+
if (uri.endsWith(`.jpg`)) {
|
|
84
|
+
return `image/jpg`;
|
|
85
|
+
}
|
|
86
|
+
if (uri.endsWith(`.xhtml`)) {
|
|
87
|
+
return `application/xhtml+xml`;
|
|
88
|
+
}
|
|
89
|
+
if (uri.endsWith(`.mp4`)) {
|
|
90
|
+
return `video/mp4`;
|
|
91
|
+
}
|
|
92
|
+
if (uri.endsWith(`.svg`)) {
|
|
93
|
+
return `image/svg+xml`;
|
|
94
|
+
}
|
|
95
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { getResourceFromArchive } from './generators/resources';
|
|
2
|
+
export { getManifestFromArchive } from './generators/manifest';
|
|
3
|
+
export type { Manifest } from '@prose-reader/shared';
|
|
4
|
+
export { Report } from './report';
|
|
5
|
+
export { Archive } from './archives/types';
|
|
6
|
+
export { getArchiveOpfInfo } from './archives/getArchiveOpfInfo';
|
|
7
|
+
export { createArchiveFromUrls } from './archives/createArchiveFromUrls';
|
|
8
|
+
export { createArchiveFromText } from './archives/createArchiveFromText';
|
|
9
|
+
export { createArchiveFromJszip } from './archives/createArchiveFromJszip';
|
|
10
|
+
export { createArchiveFromArrayBufferList } from './archives/createArchiveFromArrayBufferList';
|