diff --git a/app/Http/Controllers/Admin/AdminBookController.php b/app/Http/Controllers/Admin/AdminBookController.php index ab8737894..db684fd1b 100644 --- a/app/Http/Controllers/Admin/AdminBookController.php +++ b/app/Http/Controllers/Admin/AdminBookController.php @@ -4,7 +4,7 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\BasePageController; use App\Models\BookInfo; -use Blacklight\Books; +use App\Services\BookService; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Carbon; @@ -32,7 +32,7 @@ class AdminBookController extends BasePageController public function edit(Request $request): View|RedirectResponse { $this->setAdminPrefs(); - $book = new Books; + $bookService = new BookService; $meta_title = $title = 'Book Edit'; @@ -41,7 +41,7 @@ class AdminBookController extends BasePageController if ($request->has('id')) { $id = $request->input('id'); - $b = $book->getBookInfo($id); + $b = $bookService->getBookInfo($id); if (! $b) { abort(404); @@ -64,7 +64,7 @@ class AdminBookController extends BasePageController ? ($b['publishdate'] ?? null) : Carbon::parse($request->input('publishdate'))->timestamp; - $book->update( + $bookService->update( $id, $request->input('title'), $request->input('asin'), @@ -78,7 +78,7 @@ class AdminBookController extends BasePageController return redirect()->route('admin.book-list')->with('success', 'Book updated successfully'); case 'view': default: - return view('admin.books.edit', compact('book', 'title', 'meta_title'))->with('book', $b); + return view('admin.books.edit', compact('title', 'meta_title'))->with('book', $b); } } diff --git a/app/Http/Controllers/BooksController.php b/app/Http/Controllers/BooksController.php index 7dcf48187..f1443c12f 100644 --- a/app/Http/Controllers/BooksController.php +++ b/app/Http/Controllers/BooksController.php @@ -3,7 +3,7 @@ namespace App\Http\Controllers; use App\Models\Category; -use Blacklight\Books; +use App\Services\BookService; use Illuminate\Http\Request; use Illuminate\Support\Arr; @@ -14,7 +14,7 @@ class BooksController extends BasePageController */ public function index(Request $request, string $id = '') { - $book = new Books(['Settings' => $this->settings]); + $bookService = new BookService; $boocats = Category::getChildren(Category::BOOKS_ROOT); @@ -38,13 +38,13 @@ class BooksController extends BasePageController $catarray = []; $catarray[] = $category; - $ordering = $book->getBookOrdering(); + $ordering = $bookService->getBookOrdering(); $orderby = $request->has('ob') && \in_array($request->input('ob'), $ordering, false) ? $request->input('ob') : ''; $books = []; $page = $request->has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1; $offset = ($page - 1) * config('nntmux.items_per_cover_page'); - $rslt = $book->getBookRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, $this->userdata->categoryexclusions); + $rslt = $bookService->getBookRange($page, $catarray, $offset, config('nntmux.items_per_cover_page'), $orderby, $this->userdata->categoryexclusions); $results = $this->paginate($rslt ?? [], $rslt[0]->_totalcount ?? 0, config('nntmux.items_per_cover_page'), $page, $request->url(), $request->query()); $maxwords = 50; foreach ($results as $result) { diff --git a/app/Http/Controllers/DetailsController.php b/app/Http/Controllers/DetailsController.php index 21eefd0cd..e47cc2d7c 100644 --- a/app/Http/Controllers/DetailsController.php +++ b/app/Http/Controllers/DetailsController.php @@ -14,8 +14,8 @@ use App\Models\UserDownload; use App\Models\Video; use App\Services\MovieService; use App\Services\Releases\ReleaseSearchService; +use App\Services\BookService; use Blacklight\AniDB; -use Blacklight\Books; use Blacklight\Console; use Blacklight\Games; use Blacklight\Music; @@ -119,7 +119,7 @@ class DetailsController extends BasePageController $book = ''; if ($data['bookinfo_id'] !== '') { - $book = (new Books)->getBookInfo($data['bookinfo_id']); + $book = (new BookService)->getBookInfo($data['bookinfo_id']); } $con = ''; diff --git a/app/Http/Resources/BookCollection.php b/app/Http/Resources/BookCollection.php new file mode 100644 index 000000000..930b345a5 --- /dev/null +++ b/app/Http/Resources/BookCollection.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'data' => $this->collection, + ]; + } +} + diff --git a/app/Http/Resources/BookResource.php b/app/Http/Resources/BookResource.php new file mode 100644 index 000000000..8e95f475d --- /dev/null +++ b/app/Http/Resources/BookResource.php @@ -0,0 +1,58 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'title' => $this->title, + 'author' => $this->author, + 'asin' => $this->asin, + 'isbn' => $this->isbn, + 'ean' => $this->ean, + 'url' => $this->url, + 'salesrank' => $this->salesrank, + 'publisher' => $this->publisher, + 'publishdate' => $this->publishdate, + 'pages' => $this->pages, + 'overview' => $this->overview, + 'genre' => $this->genre, + 'cover' => $this->cover ? true : false, + 'cover_url' => $this->getCoverUrl(), + 'created_at' => $this->created_at?->toIso8601String(), + 'updated_at' => $this->updated_at?->toIso8601String(), + ]; + } + + /** + * Get the cover image URL. + */ + protected function getCoverUrl(): ?string + { + if (! $this->cover) { + return null; + } + + $coverPath = storage_path('covers/book/'.$this->id.'.jpg'); + if (file_exists($coverPath)) { + return url('/covers/book/'.$this->id.'.jpg'); + } + + return null; + } +} + diff --git a/app/Models/BookInfo.php b/app/Models/BookInfo.php index c27d2ef56..c54338ecd 100644 --- a/app/Models/BookInfo.php +++ b/app/Models/BookInfo.php @@ -3,6 +3,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; use Laravel\Scout\Searchable; /** @@ -79,4 +80,40 @@ class BookInfo extends Model 'title' => $this->title, ]; } + + /** + * Get the releases associated with this book. + */ + public function releases(): HasMany + { + return $this->hasMany(Release::class, 'bookinfo_id'); + } + + /** + * Get the cover image path. + */ + public function getCoverPath(): string + { + return storage_path('covers/book/'.$this->id.'.jpg'); + } + + /** + * Check if cover image exists. + */ + public function hasCoverImage(): bool + { + return file_exists($this->getCoverPath()); + } + + /** + * Get the cover URL. + */ + public function getCoverUrl(): ?string + { + if (! $this->cover || ! $this->hasCoverImage()) { + return null; + } + + return url('/covers/book/'.$this->id.'.jpg'); + } } diff --git a/Blacklight/Books.php b/app/Services/BookService.php old mode 100755 new mode 100644 similarity index 92% rename from Blacklight/Books.php rename to app/Services/BookService.php index b00885bac..d2e35ab23 --- a/Blacklight/Books.php +++ b/app/Services/BookService.php @@ -1,48 +1,37 @@ echooutput = config('nntmux.echocli'); - $this->colorCli = new ColorCLI; $this->pubkey = Settings::settingValue('amazonpubkey'); @@ -75,16 +61,22 @@ class Books } /** - * @return Model|null|static + * Get book info by ID. */ - public function getBookInfo($id) + public function getBookInfo(?int $id): ?Model { + if ($id === null) { + return null; + } + return BookInfo::query()->where('id', $id)->first(); } + /** + * Get book info by name using full-text search. + */ public function getBookInfoByName(string $title): ?Model { - // only used to get a count of words $searchWords = ''; $title = preg_replace(['/( - | -|\(.+\)|\(|\))/', '/[^\w ]+/'], [' ', ''], $title); $title = trim(trim(preg_replace('/\s\s+/i', ' ', $title))); @@ -100,7 +92,10 @@ class Books return BookInfo::search($searchWords)->first(); } - public function getBookRange($page, $cat, $start, $num, $orderBy, array $excludedCats = []): array + /** + * Get book range with pagination. + */ + public function getBookRange(int $page, array $cat, int $start, int $num, string $orderBy, array $excludedCats = []): array { $page = max(1, $page); $start = max(0, $start); @@ -188,7 +183,10 @@ class Books return $return; } - public function getBookOrder($orderBy): array + /** + * Get book order array. + */ + public function getBookOrder(string $orderBy): array { $order = $orderBy === '' ? 'r.postdate' : $orderBy; $orderArr = explode('_', $order); @@ -206,6 +204,9 @@ class Books return [$orderfield, $ordersort]; } + /** + * Get book ordering options. + */ public function getBookOrdering(): array { return [ @@ -226,11 +227,17 @@ class Books ]; } + /** + * Get browse by options. + */ public function getBrowseByOptions(): array { return ['author' => 'author', 'title' => 'title']; } + /** + * Get browse by SQL clause. + */ public function getBrowseBy(): string { $browseby = ' '; @@ -244,6 +251,30 @@ class Books return $browseby; } + /** + * Update book by ID. + */ + public function update( + int $id, + string $title, + ?string $asin, + ?string $url, + ?string $author, + ?string $publisher, + $publishdate, + int $cover + ): bool { + return BookInfo::query()->where('id', $id)->update([ + 'title' => $title, + 'asin' => $asin, + 'url' => $url, + 'author' => $author, + 'publisher' => $publisher, + 'publishdate' => $publishdate, + 'cover' => $cover, + ]) > 0; + } + /** * Process book releases, 1 category at a time. * @@ -283,6 +314,8 @@ class Books } /** + * Process book releases helper. + * * @throws \Exception */ protected function processBookReleasesHelper($res, $categoryID): void @@ -349,6 +382,8 @@ class Books } /** + * Parse release title. + * * @return bool|string */ public function parseTitle($release_name, $releaseID, $releasetype) @@ -401,6 +436,8 @@ class Books } /** + * Update book info from external sources. + * * @return false|int|string * * @throws \Exception @@ -493,6 +530,8 @@ class Books } /** + * Fetch book properties from iTunes. + * * @return array|bool */ public function fetchItunesBookProperties(string $bookInfo) @@ -533,3 +572,4 @@ class Books return $book; } } + diff --git a/app/Services/BooksProcessor.php b/app/Services/BooksProcessor.php index c0737830b..4a3d64094 100644 --- a/app/Services/BooksProcessor.php +++ b/app/Services/BooksProcessor.php @@ -3,7 +3,6 @@ namespace App\Services; use App\Models\Settings; -use Blacklight\Books; class BooksProcessor { @@ -17,7 +16,7 @@ class BooksProcessor public function process(string $groupID = '', string $guidChar = ''): void { if ((int) Settings::settingValue('lookupbooks') !== 0) { - (new Books)->processBookReleases($groupID, $guidChar); + (new BookService)->processBookReleases($groupID, $guidChar); } } }