feat: add a method to list unfinished books with percentage and duration

This commit is contained in:
2025-11-17 20:28:24 +01:00
parent 1b2c451eaa
commit 02ea6baa0d

215
main.py
View File

@@ -58,20 +58,111 @@ class Auditui:
sys.exit(1) sys.exit(1)
@staticmethod @staticmethod
def format_runtime(minutes): def format_duration(value, unit='minutes', default_none=None):
if minutes is None or minutes == 0: if value is None or value <= 0:
return "Unknown length" return default_none
minutes = int(minutes) if unit == 'seconds':
if minutes < 60: total_minutes = int(value) // 60
return f"{minutes} minutes" else:
total_minutes = int(value)
hours = minutes // 60 if total_minutes < 60:
mins = minutes % 60 return f"{total_minutes} minute{'s' if total_minutes != 1 else ''}"
hours = total_minutes // 60
mins = total_minutes % 60
if mins == 0: if mins == 0:
return f"{hours} hour{'s' if hours != 1 else ''}" return f"{hours} hour{'s' if hours != 1 else ''}"
return f"{hours} hour{'s' if hours != 1 else ''} {mins} minute{'s' if mins != 1 else ''}" return f"{hours} hour{'s' if hours != 1 else ''} {mins} minute{'s' if mins != 1 else ''}"
@staticmethod
def _display_items(items):
if not items:
print("No books found.")
return
print("-" * 80)
for idx, item in enumerate(items, 1):
product = item.get("product", {})
title = product.get("title")
if not title:
title = item.get("title")
if not title:
title = product.get("asin", "Unknown Title")
authors = product.get("authors", [])
if not authors:
authors = product.get("contributors", [])
if not authors and "authors" in item:
authors = item.get("authors", [])
author_names = ", ".join([a.get("name", "")
for a in authors if isinstance(a, dict)])
runtime = None
runtime_fields = [
"runtime_length_min",
"runtime_length",
"vLength",
"length",
"duration"
]
for field in runtime_fields:
runtime = product.get(field)
if runtime is None:
runtime = item.get(field)
if runtime is not None:
break
minutes = None
if isinstance(runtime, dict):
if "min" in runtime:
minutes = int(runtime.get("min", 0))
elif isinstance(runtime, (int, float)):
minutes = int(runtime)
runtime_str = Auditui.format_duration(
minutes, unit='minutes', default_none="Unknown length")
asin = product.get("asin") or item.get("asin", "")
percent_complete = item.get("percent_complete")
listening_status = item.get("listening_status", {})
if isinstance(listening_status, dict):
if percent_complete is None:
percent_complete = listening_status.get("percent_complete")
time_remaining_seconds = listening_status.get(
"time_remaining_seconds")
else:
time_remaining_seconds = None
print(f"{idx}. {title}")
if author_names:
print(f" Author: {author_names}")
print(f" Length: {runtime_str}")
if percent_complete is not None and percent_complete > 0:
percent_str = f"{percent_complete:.1f}%"
print(f" Progress: {percent_str} read")
if time_remaining_seconds:
time_remaining_str = Auditui.format_duration(
time_remaining_seconds, unit='seconds')
if time_remaining_str:
print(f" Time remaining: {time_remaining_str}")
if asin:
print(f" ASIN: {asin}")
print()
print("-" * 80)
print(f"Total: {len(items)} books")
def list_library(self): def list_library(self):
client = audible.Client(auth=self.auth) client = audible.Client(auth=self.auth)
@@ -109,63 +200,76 @@ class Auditui:
print("Your library is empty.") print("Your library is empty.")
return return
print("-" * 80) self._display_items(all_items)
for idx, item in enumerate(all_items, 1): except Exception as e:
product = item.get("product", {}) print(f"Error fetching library: {e}")
title = product.get("title") def list_unfinished(self):
if not title: client = audible.Client(auth=self.auth)
title = item.get("title")
if not title:
title = product.get("asin", "Unknown Title")
authors = product.get("authors", []) try:
if not authors: print("\nFetching your library...")
authors = product.get("contributors", [])
if not authors and "authors" in item:
authors = item.get("authors", [])
author_names = ", ".join([a.get("name", "")
for a in authors if isinstance(a, dict)])
runtime = None all_items = []
runtime_fields = [ page = 1
"runtime_length_min", page_size = 50
"runtime_length",
"vLength",
"length",
"duration"
]
for field in runtime_fields: while True:
runtime = product.get(field) library = client.get(
if runtime is None: path="library",
runtime = item.get(field) num_results=page_size,
if runtime is not None: page=page,
break response_groups="contributors,media,product_attrs,product_desc,product_details,rating,is_finished,listening_status,percent_complete"
)
minutes = None items = library.get("items", [])
if isinstance(runtime, dict):
if "min" in runtime:
minutes = int(runtime.get("min", 0))
elif isinstance(runtime, (int, float)):
minutes = int(runtime)
runtime_str = self.format_runtime(minutes) if not items:
break
asin = product.get("asin") or item.get("asin", "") all_items.extend(items)
print(f"Fetched page {page} ({len(items)} items)...", end="\r")
print(f"{idx}. {title}") if len(items) < page_size:
if author_names: break
print(f" Author: {author_names}")
print(f" Length: {runtime_str}")
if asin:
print(f" ASIN: {asin}")
print() page += 1
print("-" * 80) print(f"\nFetched {len(all_items)} books total.\n")
print(f"Total: {len(all_items)} books")
unfinished_items = []
finished_count = 0
for item in all_items:
is_finished_flag = item.get("is_finished")
percent_complete = item.get("percent_complete")
listening_status = item.get("listening_status")
if isinstance(listening_status, dict):
is_finished_flag = is_finished_flag or listening_status.get(
"is_finished", False)
percent_complete = percent_complete if percent_complete is not None else listening_status.get(
"percent_complete", 0)
is_finished = False
if is_finished_flag is True:
is_finished = True
elif isinstance(percent_complete, (int, float)) and percent_complete >= 100:
is_finished = True
if is_finished:
finished_count += 1
else:
unfinished_items.append(item)
print(
f"Found {len(unfinished_items)} unfinished books (filtered out {finished_count} finished books).\n")
if not unfinished_items:
print("No unfinished books found.")
return
self._display_items(unfinished_items)
except Exception as e: except Exception as e:
print(f"Error fetching library: {e}") print(f"Error fetching library: {e}")
@@ -174,7 +278,8 @@ class Auditui:
def main(): def main():
client = Auditui() client = Auditui()
client.login_to_audible() client.login_to_audible()
client.list_library() # client.list_library()
client.list_unfinished()
if __name__ == "__main__": if __name__ == "__main__":