Skip to content

Commit

Permalink
add in book fetching
Browse files Browse the repository at this point in the history
  • Loading branch information
bradcypert committed Dec 26, 2024
1 parent fac3e4c commit fbc876d
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 53 deletions.
83 changes: 63 additions & 20 deletions src/books/book-service.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,74 @@
import { Database } from "../../database.ts";
import { dustService } from "../../main.ts";
import { Author, AuthorWithId } from "./author.ts";
import { BookCrawler } from "./book-crawler.ts";
import type { Book } from "./book.ts";
import { addAuthorIfNotExists, addBookIfNotExists } from "./data.ts";
import type { Book, BookWithId } from "./book.ts";
import {
addAuthorIfNotExists,
addBookIfNotExists,
getAllAuthors,
getAllBooks,
getAuthorById,
getAuthorByName,
getBook,
} from "./data.ts";
import { FSWalker } from "./fs/fs-walker.ts";

export class BookService {
async populateBooksDB(dirs: Array<string>) {
const fsWalker = new FSWalker(dirs, { supportedFiletypes: ["pdf"] });
const crawler = new BookCrawler(fsWalker);
const books = await crawler.crawlForBooks();
const groupedByAuthor = books.reduce((acc, cur) => {
if (!acc.get(cur.author)) {
acc.set(cur.author, []);
}
acc.get(cur.author)?.push(cur);
return acc;
}, new Map<string, Array<Omit<Book, "author">>>());

async populateBooksDB(dirs: Array<string>) {
const fsWalker = new FSWalker(dirs, {supportedFiletypes: ['pdf']});
const crawler = new BookCrawler(fsWalker);
const books = await crawler.crawlForBooks();
const groupedByAuthor = books.reduce((acc, cur) => {
if (!acc.get(cur.author)) {
acc.set(cur.author, [])
}
acc.get(cur.author)?.push(cur);
return acc;
}, new Map<string, Array<Omit<Book, "author">>>());
for (const [author, books] of groupedByAuthor.entries()) {
const _author = await addAuthorIfNotExists(dustService.database, author);
for (const book of books) {
await addBookIfNotExists(dustService.database, {
name: book.name,
filepath: book.filepath,
author: _author.id,
});
}
}
}

async getBookById(
database: Database,
id: string
): Promise<Omit<BookWithId, "author"> & { author: Author }> {
const book = await getBook(database, id);
const author = await getAuthorById(database, book.author);

for (const [author, books] of groupedByAuthor.entries()) {
const _author = await addAuthorIfNotExists(dustService.database, author);
for (const book of books) {
await addBookIfNotExists(dustService.database, {name: book.name, filepath: book.filepath, author: _author.id});
}
}
return {
...book,
author: author,
};
}

async getBooks(
database: Database
): Promise<(Omit<BookWithId, "author"> & { author: Author | undefined })[]> {
const books = await getAllBooks(database);
const authors = await getAllAuthors(database);
const authorsById = new Map<number, AuthorWithId>();
for (const author of authors) {
authorsById.set(author.id, author);
}

return books.map((book) => {
return {
...book,
author: authorsById.get(book.author),
};
});
}
}

export const bookService: BookService = new BookService();
export const bookService: BookService = new BookService();
53 changes: 47 additions & 6 deletions src/books/data.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Database } from "../../database.ts";
import type { AuthorWithId } from "./author.ts";
import type { Book } from "./book.ts";
import type { Book, BookWithId } from "./book.ts";

export const migrate = (database: Database) => {
return database.migrate([
Expand All @@ -15,17 +15,33 @@ export const migrate = (database: Database) => {
]);
}

export const getAllBooks = (database: Database) => {
return database.execute(`
export const getAllBooks = async (database: Database): Promise<BookWithId[]> => {
const resp = await database.execute(`
SELECT rowid, * FROM books;
`);

return resp.rows.map((r) => {
return {
id: r['rowid'] as number,
name: r['name'] as string,
filepath: r['file_path'] as string,
author: r['author'] as number,
};
})
}

export const getBook = (database: Database, id: string) => {
return database.execute({
export const getBook = async (database: Database, id: string): Promise<BookWithId> => {
const resp = await database.execute({
sql: "SELECT rowid, * FROM books where rowId = $id;",
args: { id }
})
});

return {
id: resp.rows[0]['rowid'] as number,
name: resp.rows[0]['name'] as string,
author: resp.rows[0]['author'] as number,
filepath: resp.rows[0]['file_path'] as string,
}
}

export const getBookByName = (database: Database, name: string) => {
Expand All @@ -47,13 +63,38 @@ export const addBookIfNotExists = async (database: Database, book: Book) => {
});
}

export const getAuthorById = async (database: Database, id: number): Promise<AuthorWithId> => {
const resp = await database.execute({
sql: "SELECT rowid, * FROM authors where rowId = $id",
args: {id}
});
return {
id: resp.rows[0]['rowid'] as number,
name: resp.rows[0]['name'] as string,
}
}

export const getAuthorByName = (database: Database, name: string) => {
return database.execute({
sql: "SELECT rowid, * FROM authors WHERE name = $name",
args: {name: name}
})
}

export const getAllAuthors = async (database: Database): Promise<AuthorWithId[]> => {
const resp = await database.execute({
sql: "SELECT rowid, * FROM authors",
args: {name: name}
});

return resp.rows.map((r) => {
return {
id: r['rowid'] as number,
name: r['name'] as string,
}
})
}

// TODO: Need to ensure author exists when we put the book in the DB.
export const addAuthorIfNotExists = async (database: Database, authorName: string): Promise<AuthorWithId> => {
const existing = await getAuthorByName(database, authorName);
Expand Down
77 changes: 50 additions & 27 deletions src/books/routes.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,52 @@
import type { Router } from "@oak/oak";
import { Status, type Router } from "@oak/oak";
import { bookService } from "./book-service.ts";
import { dustService } from "../../main.ts";

export const registerRoutes = (router: Router) => {
router.get("/books/", async (ctx) => {
ctx.response.body = {
books: [],
};
});
router.get("/books/:id", (ctx) => {
ctx.response.body = {
book: {
id: ctx.params.id
}
}
});
router.get("/books/:id/progress", (ctx) => {
ctx.response.body = {
book: {
id: ctx.params.id,
},
progress: {
page: 0,
}
};
});
router.put("/books/:id/progress", (ctx) => {
ctx.response.status = 200;
});
}
router.get("/books/", async (ctx) => {
const books = await bookService.getBooks(dustService.database);
console.log(books);
ctx.response.body = {
books: books,
};
});
router.get("/books/:id", async (ctx) => {
try {
const book = await bookService.getBookById(
dustService.database,
ctx.params.id
);
console.log(book);
ctx.response.status = Status.OK;
ctx.response.body = {
book: book,
};
ctx.response.type = "json";

return;
} catch (e) {
console.error(e);
}
});
router.get("/books/:id/stream", async (ctx) => {
const book = await bookService.getBookById(
dustService.database,
ctx.params.id
);
const file = await Deno.open(book.filepath, { read: true });
ctx.response.body = file;
});
router.get("/books/:id/progress", (ctx) => {
ctx.response.body = {
book: {
id: ctx.params.id,
},
progress: {
page: 0,
},
};
});
router.put("/books/:id/progress", (ctx) => {
ctx.response.status = 200;
});
};

0 comments on commit fbc876d

Please sign in to comment.