refactor: switch to a class approach

This commit is contained in:
2025-11-17 17:17:12 +01:00
parent a33e2997aa
commit 9b317e20cb

269
main.py
View File

@@ -11,167 +11,170 @@ except ImportError:
sys.exit(1) sys.exit(1)
def login_to_audible(): class Auditui:
auth_file = Path.home() / ".config" / "auditui" / "auth.json" def __init__(self):
auth_file.parent.mkdir(parents=True, exist_ok=True) self.auth = None
if auth_file.exists():
try:
auth = audible.Authenticator.from_file(str(auth_file))
print("Loaded existing authentication.")
return auth
except Exception as e:
print(f"Failed to load existing auth: {e}")
print("Please re-authenticate.")
print("Please authenticate with your Audible account.")
print("You will need to provide:")
print(" - Your Audible email/username")
print(" - Your password")
print(" - Your marketplace locale (e.g., 'US', 'UK', 'DE', 'FR')")
email = input("Email: ")
password = getpass("Password: ")
marketplace = input(
"Marketplace locale (default: US): ").strip().upper() or "US"
try:
auth = audible.Authenticator.from_login(
username=email,
password=password,
locale=marketplace
)
def login_to_audible(self):
auth_file = Path.home() / ".config" / "auditui" / "auth.json"
auth_file.parent.mkdir(parents=True, exist_ok=True) auth_file.parent.mkdir(parents=True, exist_ok=True)
auth.to_file(str(auth_file))
print("Authentication successful! Credentials saved.")
return auth
except Exception as e:
print(f"Authentication failed: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if auth_file.exists():
try:
self.auth = audible.Authenticator.from_file(str(auth_file))
print("Loaded existing authentication.")
return
except Exception as e:
print(f"Failed to load existing auth: {e}")
print("Please re-authenticate.")
def format_runtime(minutes): print("Please authenticate with your Audible account.")
if minutes is None or minutes == 0: print("You will need to provide:")
return "Unknown length" print(" - Your Audible email/username")
print(" - Your password")
print(" - Your marketplace locale (e.g., 'US', 'UK', 'DE', 'FR')")
minutes = int(minutes) email = input("Email: ")
if minutes < 60: password = getpass("Password: ")
return f"{minutes} minutes" marketplace = input(
"Marketplace locale (default: US): ").strip().upper() or "US"
hours = minutes // 60 try:
mins = minutes % 60 self.auth = audible.Authenticator.from_login(
if mins == 0: username=email,
return f"{hours} hour{'s' if hours != 1 else ''}" password=password,
return f"{hours} hour{'s' if hours != 1 else ''} {mins} minute{'s' if mins != 1 else ''}" locale=marketplace
def list_library(auth):
client = audible.Client(auth=auth)
try:
print("\nFetching your library...")
all_items = []
page = 1
page_size = 50
while True:
library = client.get(
path="library",
num_results=page_size,
page=page,
response_groups="contributors,media,product_attrs,product_desc,product_details,rating"
) )
items = library.get("items", []) auth_file.parent.mkdir(parents=True, exist_ok=True)
self.auth.to_file(str(auth_file))
print("Authentication successful! Credentials saved.")
except Exception as e:
print(f"Authentication failed: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if not items: @staticmethod
break def format_runtime(minutes):
if minutes is None or minutes == 0:
return "Unknown length"
all_items.extend(items) minutes = int(minutes)
print(f"Fetched page {page} ({len(items)} items)...", end="\r") if minutes < 60:
return f"{minutes} minutes"
if len(items) < page_size: hours = minutes // 60
break mins = minutes % 60
if mins == 0:
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 ''}"
page += 1 def list_library(self):
client = audible.Client(auth=self.auth)
print(f"\nFetched {len(all_items)} books total.\n") try:
print("\nFetching your library...")
if not all_items: all_items = []
print("Your library is empty.") page = 1
return page_size = 50
print("-" * 80) while True:
library = client.get(
path="library",
num_results=page_size,
page=page,
response_groups="contributors,media,product_attrs,product_desc,product_details,rating"
)
for idx, item in enumerate(all_items, 1): items = library.get("items", [])
product = item.get("product", {})
title = product.get("title") if not items:
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 break
minutes = None all_items.extend(items)
if isinstance(runtime, dict): print(f"Fetched page {page} ({len(items)} items)...", end="\r")
if "min" in runtime:
minutes = int(runtime.get("min", 0))
elif isinstance(runtime, (int, float)):
minutes = int(runtime)
runtime_str = format_runtime(minutes) if len(items) < page_size:
break
asin = product.get("asin") or item.get("asin", "") page += 1
print(f"{idx}. {title}") print(f"\nFetched {len(all_items)} books total.\n")
if author_names:
print(f" Author: {author_names}")
print(f" Length: {runtime_str}")
if asin:
print(f" ASIN: {asin}")
print() if not all_items:
print("Your library is empty.")
return
print("-" * 80) print("-" * 80)
print(f"Total: {len(all_items)} books")
except Exception as e: for idx, item in enumerate(all_items, 1):
print(f"Error fetching library: {e}") product = item.get("product", {})
import traceback
traceback.print_exc() 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 = self.format_runtime(minutes)
asin = product.get("asin") or item.get("asin", "")
print(f"{idx}. {title}")
if author_names:
print(f" Author: {author_names}")
print(f" Length: {runtime_str}")
if asin:
print(f" ASIN: {asin}")
print()
print("-" * 80)
print(f"Total: {len(all_items)} books")
except Exception as e:
print(f"Error fetching library: {e}")
import traceback
traceback.print_exc()
def main(): def main():
auth = login_to_audible() client = Auditui()
list_library(auth) client.login_to_audible()
client.list_library()
if __name__ == "__main__": if __name__ == "__main__":