Cache Goodreads data and slow down bulk requests
This commit is contained in:
1 parent
af73bb5b26
commit
7ef3a29ece
6 files changed
+162
-13
No files matched your search
@@ -1,3 +1,5 @@
|
||||
import time
|
||||
|
||||
from rich.markup import escape
|
||||
from textual import on, work
|
||||
from textual.app import ComposeResult
|
||||
@@ -6,11 +8,14 @@ from textual.containers import Horizontal, VerticalScroll
|
||||
from textual.widgets import DataTable, Static
|
||||
|
||||
import books
|
||||
import goodreads
|
||||
import goodreads_series
|
||||
from tui.content_pane import ContentPane
|
||||
from tui.dialogs import ConfirmDialog, FormDialog, FormField
|
||||
from tui.series_picker import SeriesPicker
|
||||
|
||||
REQUEST_DELAY_SECONDS = 3
|
||||
|
||||
|
||||
def book_details(book: dict) -> str:
|
||||
rows = [
|
||||
@@ -40,6 +45,7 @@ class BooksPane(ContentPane):
|
||||
Binding("d", "delete", "Delete"),
|
||||
Binding("u", "refresh", "Refresh"),
|
||||
Binding("U", "refresh_all", "Refresh all"),
|
||||
Binding("C", "cache_covers", "Cache covers"),
|
||||
Binding("r", "reload", "Reload"),
|
||||
]
|
||||
|
||||
@@ -151,19 +157,48 @@ class BooksPane(ContentPane):
|
||||
self.notify("Refreshing every book from Goodreads...")
|
||||
self.refresh_every_book()
|
||||
|
||||
def report_blocked(self, error: Exception):
|
||||
self.app.call_from_thread(self.notify, str(error), title="Stopped", severity="error", timeout=15)
|
||||
|
||||
@work(thread=True, exclusive=True, group="refresh")
|
||||
def refresh_every_book(self):
|
||||
failures = 0
|
||||
refreshed = 0
|
||||
all_books = books.load_books()
|
||||
for book in all_books:
|
||||
for index, book in enumerate(all_books):
|
||||
if index:
|
||||
time.sleep(REQUEST_DELAY_SECONDS)
|
||||
try:
|
||||
books.refresh_book(book["goodreadsId"])
|
||||
except goodreads.BlockedError as error:
|
||||
self.report_blocked(error)
|
||||
break
|
||||
except (OSError, ValueError) as error:
|
||||
failures += 1
|
||||
self.app.call_from_thread(self.notify, f"{book['title']}: {error}", title="Skipped", severity="warning")
|
||||
continue
|
||||
refreshed += 1
|
||||
self.app.call_from_thread(lambda: self.reload(self.selected_key()))
|
||||
summary = f"Refreshed {len(all_books) - failures} of {len(all_books)} books"
|
||||
self.app.call_from_thread(self.notify, summary)
|
||||
self.app.call_from_thread(self.notify, f"Refreshed {refreshed} of {len(all_books)} books")
|
||||
|
||||
def action_cache_covers(self):
|
||||
missing = books.books_missing_local_covers()
|
||||
if not missing:
|
||||
self.notify("Every cover is already saved in static/covers/books")
|
||||
return
|
||||
self.notify(f"Saving {len(missing)} covers...")
|
||||
self.cache_missing_covers(missing)
|
||||
|
||||
@work(thread=True, exclusive=True, group="covers")
|
||||
def cache_missing_covers(self, missing: list[dict]):
|
||||
saved = 0
|
||||
for book in missing:
|
||||
try:
|
||||
books.cache_book_cover(book["goodreadsId"])
|
||||
except (OSError, ValueError) as error:
|
||||
self.app.call_from_thread(self.notify, f"{book['title']}: {error}", title="Skipped", severity="warning")
|
||||
continue
|
||||
saved += 1
|
||||
self.app.call_from_thread(lambda: self.reload(self.selected_key()))
|
||||
self.app.call_from_thread(self.notify, f"Saved {saved} of {len(missing)} covers")
|
||||
|
||||
def action_add_series(self):
|
||||
fields = [FormField("link", "Goodreads series link", placeholder="https://www.goodreads.com/series/92037-wings-of-fire", required=True)]
|
||||
@@ -185,12 +220,21 @@ class BooksPane(ContentPane):
|
||||
@work(thread=True, exclusive=True, group="series")
|
||||
def add_series_books(self, series: dict, choices: dict[str, str]):
|
||||
added = 0
|
||||
already_listed = set(books.library_shelves())
|
||||
fetched_any = False
|
||||
for entry in series["books"]:
|
||||
shelf = choices.get(entry["goodreadsId"])
|
||||
if not shelf:
|
||||
continue
|
||||
needs_fetch = entry["goodreadsId"] not in already_listed
|
||||
if needs_fetch and fetched_any:
|
||||
time.sleep(REQUEST_DELAY_SECONDS)
|
||||
fetched_any = fetched_any or needs_fetch
|
||||
try:
|
||||
book = books.add_series_book(series, entry, shelf)
|
||||
except goodreads.BlockedError as error:
|
||||
self.report_blocked(error)
|
||||
break
|
||||
except (OSError, ValueError) as error:
|
||||
self.app.call_from_thread(self.notify, f"{entry['title']}: {error}", title="Skipped", severity="warning")
|
||||
continue
|
||||
|
||||
Reference in new issue
Block a user