gadl/library.ts
2023-11-20 23:33:21 +09:00

111 lines
3 KiB
TypeScript

import fs from "node:fs/promises";
import { createWriteStream } from "node:fs";
import stream from "node:stream/promises";
import { Zip, ZipPassThrough } from "fflate";
import { Database } from "./database";
export type Book = {
id: number;
platform: "dmm-books" | "google-play-books";
readerUrl: string;
title: string;
authors: Array<string>;
};
export function createLibrary(db: Database) {
return {
async add(readerUrlOrBook: string | Book) {
const platform = "dmm-books";
if (typeof readerUrlOrBook === "string") {
await db.run(
`insert into books(platform_id, reader_url) values((select id from platforms where name = ?), ?)`,
platform,
readerUrlOrBook,
);
return;
}
await db.run(
`\
insert into books(
platform_id,
reader_url,
title,
authors)
values((select id from platforms where name = ?), ?, ?, ?)
on conflict(reader_url)
do update set title = excluded.title, authors = excluded.authors
`,
platform,
readerUrlOrBook.readerUrl,
readerUrlOrBook.title,
JSON.stringify(readerUrlOrBook.authors),
);
},
async delete(id: number) {
await db.run(`delete from books where id = ?`, id);
},
async get(id: number): Promise<Book | undefined> {
const row = await db.get(
`select books.id, platforms.name as platform, books.reader_url as readerUrl, books.title, books.authors from books left join platforms on books.platform_id = platforms.id where books.id = ?`,
id,
);
const book: Book | undefined = row && {
...row,
authors: JSON.parse(row.authors),
};
return book;
},
async getBooks(): Promise<Array<Book>> {
const rows = await db.all(
`select books.id, platforms.name as platform, books.reader_url as readerUrl, books.title, books.authors from books left join platforms on books.platform_id = platforms.id`,
);
const books: Array<Book> = rows.map((row) => ({
...row,
authors: JSON.parse(row.authors),
}));
return books;
},
async archive(dir: string) {
const bookDirs = await fs.readdir(dir, { withFileTypes: true });
for (const bookDir of bookDirs) {
if (!bookDir.isDirectory()) {
continue;
}
const path = `${bookDir.path}/${bookDir.name}`;
const out = createWriteStream(`${path}.cbz`);
const zip = new Zip(function cb(err, data, final) {
if (err) {
out.destroy(err);
return;
}
out[final ? "end" : "write"](data);
});
const files = await fs.readdir(path);
for (const file of files) {
const data = new ZipPassThrough(file);
zip.add(data);
const buffer = await fs.readFile(`${path}/${file}`);
data.push(buffer, true);
}
zip.end();
await stream.finished(out);
await fs.rm(path, { recursive: true });
}
},
};
}