mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
108 lines
3.8 KiB
TypeScript
108 lines
3.8 KiB
TypeScript
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { createReadStream } from 'fs';
|
|
import { readdir, stat } from 'fs/promises';
|
|
import { basename, join } from 'path';
|
|
|
|
import { BookRepository } from './book.repository';
|
|
import { BookCardDto } from './dto/book-card.dto';
|
|
import { BookDetailDto } from './dto/book-detail.dto';
|
|
import { GetBooksDto } from './dto/get-books.dto';
|
|
|
|
@Injectable()
|
|
export class BookService {
|
|
private readonly booksPath: string;
|
|
|
|
constructor(
|
|
private readonly bookRepo: BookRepository,
|
|
private readonly config: ConfigService,
|
|
) {
|
|
this.booksPath = this.config.get<string>('storage.booksPath')!;
|
|
}
|
|
|
|
async getCards(dto: GetBooksDto): Promise<{ items: BookCardDto[]; total: number; page: number; size: number }> {
|
|
const { libraryId, page = 0, size = 50, search } = dto;
|
|
const { rows, authorRows, fileRows, total } = await this.bookRepo.findCards(libraryId, { page, size, search });
|
|
|
|
const authorsByBook = new Map<number, string[]>();
|
|
for (const row of authorRows) {
|
|
const list = authorsByBook.get(row.bookId) ?? [];
|
|
list.push(row.name);
|
|
authorsByBook.set(row.bookId, list);
|
|
}
|
|
|
|
const filesByBook = new Map<number, { id: number; format: string | null; role: string }[]>();
|
|
for (const row of fileRows) {
|
|
const list = filesByBook.get(row.bookId) ?? [];
|
|
list.push({ id: row.id, format: row.format, role: row.role });
|
|
filesByBook.set(row.bookId, list);
|
|
}
|
|
|
|
const items: BookCardDto[] = rows.map((row) => ({
|
|
id: row.id,
|
|
status: row.status,
|
|
title: row.title ?? basename(row.folderPath),
|
|
seriesName: row.seriesName ?? null,
|
|
seriesIndex: row.seriesIndex ?? null,
|
|
authors: authorsByBook.get(row.id) ?? [],
|
|
files: filesByBook.get(row.id) ?? [],
|
|
}));
|
|
|
|
return { items, total, page, size };
|
|
}
|
|
|
|
async getCoverPath(id: number): Promise<string | null> {
|
|
const dir = join(this.booksPath, 'covers', String(id));
|
|
try {
|
|
const files = await readdir(dir);
|
|
const cover = files.find((f) => f.startsWith('cover.'));
|
|
return cover ? join(dir, cover) : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async getFileStream(fileId: number): Promise<{ stream: ReturnType<typeof createReadStream>; size: number; format: string }> {
|
|
const file = await this.bookRepo.findFileById(fileId);
|
|
if (!file) throw new NotFoundException(`No file with id ${fileId}`);
|
|
const { size } = await stat(file.absolutePath);
|
|
return { stream: createReadStream(file.absolutePath), size, format: file.format ?? 'unknown' };
|
|
}
|
|
|
|
async getProgress(fileId: number) {
|
|
return this.bookRepo.findProgress(fileId);
|
|
}
|
|
|
|
async saveProgress(fileId: number, cfi: string | null | undefined, pageNumber: number | null | undefined, percentage: number) {
|
|
await this.bookRepo.upsertProgress(fileId, cfi ?? null, pageNumber ?? null, percentage);
|
|
}
|
|
|
|
async getDetail(id: number): Promise<BookDetailDto> {
|
|
const result = await this.bookRepo.findById(id);
|
|
if (!result) throw new NotFoundException(`Book ${id} not found`);
|
|
|
|
const { book, authorRows, tagRows, fileRows } = result;
|
|
const meta = book.book_metadata;
|
|
|
|
return {
|
|
id: book.books.id,
|
|
status: book.books.status,
|
|
folderPath: book.books.folderPath,
|
|
title: meta?.title ?? null,
|
|
subtitle: meta?.subtitle ?? null,
|
|
description: meta?.description ?? null,
|
|
isbn10: meta?.isbn10 ?? null,
|
|
isbn13: meta?.isbn13 ?? null,
|
|
publisher: meta?.publisher ?? null,
|
|
publishedYear: meta?.publishedYear ?? null,
|
|
language: meta?.language ?? null,
|
|
pageCount: meta?.pageCount ?? null,
|
|
seriesName: meta?.seriesName ?? null,
|
|
seriesIndex: meta?.seriesIndex ?? null,
|
|
authors: authorRows,
|
|
tags: tagRows.map((t) => t.name),
|
|
files: fileRows,
|
|
};
|
|
}
|
|
}
|