From 9b317e20cbdfe833bf7fe2b7bf7215d563261a58 Mon Sep 17 00:00:00 2001 From: Kharec Date: Mon, 17 Nov 2025 17:17:12 +0100 Subject: [PATCH] refactor: switch to a class approach --- main.py | 269 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 136 insertions(+), 133 deletions(-) diff --git a/main.py b/main.py index bde7916..c0a8a17 100644 --- a/main.py +++ b/main.py @@ -11,167 +11,170 @@ except ImportError: sys.exit(1) -def login_to_audible(): - auth_file = Path.home() / ".config" / "auditui" / "auth.json" - auth_file.parent.mkdir(parents=True, exist_ok=True) - - 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 - ) +class Auditui: + def __init__(self): + self.auth = None + def login_to_audible(self): + auth_file = Path.home() / ".config" / "auditui" / "auth.json" 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): - if minutes is None or minutes == 0: - return "Unknown length" + 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')") - minutes = int(minutes) - if minutes < 60: - return f"{minutes} minutes" + email = input("Email: ") + password = getpass("Password: ") + marketplace = input( + "Marketplace locale (default: US): ").strip().upper() or "US" - hours = minutes // 60 - 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 ''}" - - -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" + try: + self.auth = audible.Authenticator.from_login( + username=email, + password=password, + locale=marketplace ) - 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: - break + @staticmethod + def format_runtime(minutes): + if minutes is None or minutes == 0: + return "Unknown length" - all_items.extend(items) - print(f"Fetched page {page} ({len(items)} items)...", end="\r") + minutes = int(minutes) + if minutes < 60: + return f"{minutes} minutes" - if len(items) < page_size: - break + hours = minutes // 60 + 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: - print("Your library is empty.") - return + all_items = [] + page = 1 + 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): - product = item.get("product", {}) + items = library.get("items", []) - 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: + if not items: 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) + all_items.extend(items) + print(f"Fetched page {page} ({len(items)} items)...", end="\r") - 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}") - if author_names: - print(f" Author: {author_names}") - print(f" Length: {runtime_str}") - if asin: - print(f" ASIN: {asin}") + print(f"\nFetched {len(all_items)} books total.\n") - print() + if not all_items: + print("Your library is empty.") + return - print("-" * 80) - print(f"Total: {len(all_items)} books") + print("-" * 80) - except Exception as e: - print(f"Error fetching library: {e}") - import traceback - traceback.print_exc() + for idx, item in enumerate(all_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 = 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(): - auth = login_to_audible() - list_library(auth) + client = Auditui() + client.login_to_audible() + client.list_library() if __name__ == "__main__":