refactor: switch to a class approach
This commit is contained in:
269
main.py
269
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__":
|
||||
|
||||
Reference in New Issue
Block a user