From 782f5c15d69741f7d6b023e7e3be09d9300ede3c Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Mon, 20 Oct 2025 17:02:34 -0400 Subject: [PATCH] [books] Calc stats and dont die when title not found --- vrobbler/apps/books/models.py | 2 +- vrobbler/apps/scrobbles/models.py | 43 ++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/vrobbler/apps/books/models.py b/vrobbler/apps/books/models.py index b8b34f4..714bcd7 100644 --- a/vrobbler/apps/books/models.py +++ b/vrobbler/apps/books/models.py @@ -290,7 +290,7 @@ class Book(LongPlayScrobblableMixin): return book def save_image_from_url(self, url: str, force_update: bool = False): - if not self.cover or (force_update and url): + if url and (not self.cover or force_update): r = requests.get(url) if r.status_code == 200: fname = f"{self.title}_{self.uuid}.jpg" diff --git a/vrobbler/apps/scrobbles/models.py b/vrobbler/apps/scrobbles/models.py index bd473cd..0ec0968 100644 --- a/vrobbler/apps/scrobbles/models.py +++ b/vrobbler/apps/scrobbles/models.py @@ -1181,6 +1181,7 @@ class Scrobble(TimeStampedModel): for page in page_list: if not page.get("end_ts", None): page["end_ts"] = int(timezone.now().timestamp()) + page["duration"] = page["end_ts"] - page.get("start_ts") page_list.append( BookPageLogData( @@ -1198,7 +1199,7 @@ class Scrobble(TimeStampedModel): scrobble_data.pop("playback_status") if read_log_page: - scrobble_data["log"] = BookLogData(page_data=BookPageLogData(page_number=read_log_page, start_ts=int(timezone.now().timestamp()))) + scrobble_data["log"] = BookLogData(page_data=[BookPageLogData(page_number=read_log_page, start_ts=int(timezone.now().timestamp()))]) logger.info( f"[scrobbling] creating new scrobble", @@ -1392,6 +1393,9 @@ class Scrobble(TimeStampedModel): if class_name in LONG_PLAY_MEDIA.values(): self.finish_long_play() + if class_name == "Book": + self.calculate_reading_stats() + logger.info( f"[scrobbling] stopped", extra={ @@ -1487,3 +1491,40 @@ class Scrobble(TimeStampedModel): beyond_completion = False return beyond_completion + + def calculate_reading_stats(self, commit=True): + # --- Sort safely by numeric page_number --- + def safe_page_number(entry): + try: + return int(entry.get("page_number", 0)) + except (ValueError, TypeError): + return float("inf") # push invalid entries to the end + + page_data = self.log.get("page_data") + + if not page_data: + logger.warning("No page data found to calculate") + return + + if isinstance(page_data, dict): + logger.warning("Page data is dict, migrate koreader data") + return + + page_data.sort(key=safe_page_number) + + # --- Extract valid numeric page numbers --- + valid_pages = [] + for page in page_data: + try: + valid_pages.append(int(page["page_number"])) + except (ValueError, TypeError): + continue + + # --- Compute stats --- + if valid_pages: + self.log["page_start"] = min(valid_pages) + self.log["page_end"] = max(valid_pages) + self.log["pages_read"] = len(set(valid_pages)) + + if commit: + self.save(update_fields=["log"])