Files
auditui/stats.py

142 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""Stats playground for Audible TUI - get your listening stats"""
import logging
from datetime import date
from getpass import getpass
from pathlib import Path
import audible
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger = logging.getLogger(__name__)
logging.getLogger("audible").setLevel(logging.WARNING)
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("httpcore").setLevel(logging.WARNING)
class AudibleStats:
"""Class to handle Audible authentication and stats retrieval."""
def __init__(self) -> None:
"""Initialize the stats handler with authentication."""
self.auth: audible.Authenticator | None = None
self.client: audible.Client | None = None
self.home = Path.home()
def authenticate(self) -> None:
"""Authenticate with Audible and store auth and client."""
auth_path = self.home / ".config" / "auditui" / "auth.json"
auth_path.parent.mkdir(parents=True, exist_ok=True)
if auth_path.exists():
try:
self.auth = audible.Authenticator.from_file(str(auth_path))
self.client = audible.Client(auth=self.auth)
return
except Exception:
logger.info(
"Failed to load existing auth. Re-authenticating.\n")
email = input("Email: ")
password = getpass("Password: ")
marketplace = (
input("Marketplace locale (default: US): ").strip().upper() or "US"
)
self.auth = audible.Authenticator.from_login(
username=email, password=password, locale=marketplace
)
self.auth.to_file(str(auth_path))
self.client = audible.Client(auth=self.auth)
def get_signup_year(self) -> int:
"""Get signup year by checking activity in each month, each year."""
current_year = date.today().year
start_year = 1995
try:
stats = self.client.get(
"1.0/stats/aggregates",
monthly_listening_interval_duration="12",
monthly_listening_interval_start_date=f"{current_year}-01",
store="Audible",
)
monthly_stats = stats.get("aggregated_monthly_listening_stats", [])
if not monthly_stats or not any(
stat.get("aggregated_sum", 0) > 0 for stat in monthly_stats
):
logger.warning("Could not determine signup year")
return 0
except Exception:
logger.warning("Could not determine signup year")
return 0
left, right = start_year, current_year
earliest_year = current_year
while left <= right:
middle = (left + right) // 2
try:
stats = self.client.get(
"1.0/stats/aggregates",
monthly_listening_interval_duration="12",
monthly_listening_interval_start_date=f"{middle}-01",
store="Audible",
)
monthly_stats = stats.get(
"aggregated_monthly_listening_stats", [])
has_activity = bool(
monthly_stats
and any(stat.get("aggregated_sum", 0) > 0 for stat in monthly_stats)
)
except Exception:
has_activity = False
if has_activity:
earliest_year = middle
right = middle - 1
else:
left = middle + 1
return earliest_year
def get_current_month_listening_time(self) -> tuple[int, int, int]:
"""Get total listening time for the current month as (hours, minutes, seconds)."""
try:
stats = self.client.get(
"1.0/stats/aggregates",
monthly_listening_interval_duration="1",
monthly_listening_interval_start_date=date.today().strftime("%Y-%m"),
store="Audible",
)
monthly_stats = stats.get("aggregated_monthly_listening_stats", [])
if not monthly_stats:
return (0, 0, 0)
total_milliseconds = sum(
stat.get("aggregated_sum", 0) for stat in monthly_stats
)
total_seconds = int(total_milliseconds // 1000)
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return (hours, minutes, seconds)
except Exception as e:
logger.warning(f"Could not get current month listening time: {e}")
return (0, 0, 0)
def main() -> None:
"""Main entry point."""
worker = AudibleStats()
worker.authenticate()
print(worker.get_signup_year())
hours, minutes, seconds = worker.get_current_month_listening_time()
print(f"Total listening time this month: {hours}h {minutes}m {seconds}s")
if __name__ == "__main__":
main()