Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#
import os
import sys


sys.path.insert(0, os.path.abspath("../../src"))
import audible_cli

Expand Down
50 changes: 25 additions & 25 deletions plugin_cmds/cmd_decrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import operator
import pathlib
import re
import subprocess # noqa: S404
import subprocess
import tempfile
import typing as t
from enum import Enum
Expand Down Expand Up @@ -50,17 +50,17 @@ def is_supported_file(cls, value):


def _get_input_files(
files: t.Union[t.Tuple[str], t.List[str]],
files: tuple[str] | list[str],
recursive: bool = True
) -> t.List[pathlib.Path]:
) -> list[pathlib.Path]:
filenames = []
for filename in files:
# if the shell does not do filename globbing
expanded = list(glob(filename, recursive=recursive))

if (
len(expanded) == 0
and '*' not in filename
and "*" not in filename
and not SupportedFiles.is_supported_file(filename)
):
raise click.BadParameter("{filename}: file not found or supported.")
Expand All @@ -74,7 +74,7 @@ def _get_input_files(
return filenames


def recursive_lookup_dict(key: str, dictionary: t.Dict[str, t.Any]) -> t.Any:
def recursive_lookup_dict(key: str, dictionary: dict[str, t.Any]) -> t.Any:
if key in dictionary:
return dictionary[key]
for value in dictionary.values():
Expand All @@ -85,7 +85,7 @@ def recursive_lookup_dict(key: str, dictionary: t.Dict[str, t.Any]) -> t.Any:
continue
else:
return item

raise KeyError


Expand All @@ -104,12 +104,12 @@ def get_aaxc_credentials(voucher_file: pathlib.Path):


class ApiChapterInfo:
def __init__(self, content_metadata: t.Dict[str, t.Any]) -> None:
def __init__(self, content_metadata: dict[str, t.Any]) -> None:
chapter_info = self._parse(content_metadata)
self._chapter_info = chapter_info

@classmethod
def from_file(cls, file: t.Union[pathlib.Path, str]) -> "ApiChapterInfo":
def from_file(cls, file: pathlib.Path | str) -> "ApiChapterInfo":
file = pathlib.Path(file)
if not file.exists() or not file.is_file():
raise ChapterError(f"Chapter file {file} not found.")
Expand All @@ -118,7 +118,7 @@ def from_file(cls, file: t.Union[pathlib.Path, str]) -> "ApiChapterInfo":
return cls(content_json)

@staticmethod
def _parse(content_metadata: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]:
def _parse(content_metadata: dict[str, t.Any]) -> dict[str, t.Any]:
if "chapters" in content_metadata:
return content_metadata

Expand Down Expand Up @@ -167,17 +167,17 @@ def is_accurate(self):
def _separate_intro_outro(self, chapters):
echo("Separate Audible Brand Intro and Outro to own Chapter.")
chapters.sort(key=operator.itemgetter("start_offset_ms"))

first = chapters[0]
intro_dur_ms = self.get_intro_duration_ms()
first["start_offset_ms"] = intro_dur_ms
first["start_offset_sec"] = round(first["start_offset_ms"] / 1000)
first["length_ms"] -= intro_dur_ms

last = chapters[-1]
outro_dur_ms = self.get_outro_duration_ms()
last["length_ms"] -= outro_dur_ms

chapters.append(
{
"length_ms": intro_dur_ms,
Expand All @@ -197,13 +197,13 @@ def _separate_intro_outro(self, chapters):
}
)
chapters.sort(key=operator.itemgetter("start_offset_ms"))

return chapters

def _remove_intro_outro(self, chapters):
echo("Delete Audible Brand Intro and Outro.")
chapters.sort(key=operator.itemgetter("start_offset_ms"))

intro_dur_ms = self.get_intro_duration_ms()
outro_dur_ms = self.get_outro_duration_ms()

Expand All @@ -216,14 +216,14 @@ def _remove_intro_outro(self, chapters):

last = chapters[-1]
last["length_ms"] -= outro_dur_ms

return chapters

class FFMeta:
SECTION = re.compile(r"\[(?P<header>[^]]+)\]")
OPTION = re.compile(r"(?P<option>.*?)\s*(?:(?P<vi>=)\s*(?P<value>.*))?$")

def __init__(self, ffmeta_file: t.Union[str, pathlib.Path]) -> None:
def __init__(self, ffmeta_file: str | pathlib.Path) -> None:
self._ffmeta_raw = pathlib.Path(ffmeta_file).read_text("utf-8")
self._ffmeta_parsed = self._parse_ffmeta()

Expand Down Expand Up @@ -321,7 +321,7 @@ def update_chapters_from_chapter_info(
"title": chapter["title"],
}
self._ffmeta_parsed["CHAPTER"] = new_chapters

def get_start_end_without_intro_outro(
self,
chapter_info: ApiChapterInfo,
Expand Down Expand Up @@ -358,7 +358,7 @@ def __init__(
file: pathlib.Path,
target_dir: pathlib.Path,
tempdir: pathlib.Path,
activation_bytes: t.Optional[str],
activation_bytes: str | None,
overwrite: bool,
rebuild_chapters: bool,
force_rebuild_chapters: bool,
Expand All @@ -381,7 +381,7 @@ def __init__(
credentials = get_aaxc_credentials(voucher_filename)

self._source = file
self._credentials: t.Optional[t.Union[str, t.Tuple[str]]] = credentials
self._credentials: str | tuple[str] | None = credentials
self._target_dir = target_dir
self._tempdir = tempdir
self._overwrite = overwrite
Expand All @@ -390,8 +390,8 @@ def __init__(
self._skip_rebuild_chapters = skip_rebuild_chapters
self._separate_intro_outro = separate_intro_outro
self._remove_intro_outro = remove_intro_outro
self._api_chapter: t.Optional[ApiChapterInfo] = None
self._ffmeta: t.Optional[FFMeta] = None
self._api_chapter: ApiChapterInfo | None = None
self._ffmeta: FFMeta | None = None
self._is_rebuilded: bool = False

@property
Expand Down Expand Up @@ -429,9 +429,9 @@ def ffmeta(self) -> FFMeta:
credentials_cmd = [
"-activation_bytes",
self._credentials,
]
]
base_cmd.extend(credentials_cmd)

extract_cmd = [
"-i",
str(self._source),
Expand Down Expand Up @@ -484,7 +484,7 @@ def run(self):
credentials_cmd = [
"-activation_bytes",
self._credentials,
]
]
base_cmd.extend(credentials_cmd)

if self._rebuild_chapters:
Expand Down Expand Up @@ -616,7 +616,7 @@ def run(self):
def cli(
session,
files: str,
directory: t.Union[pathlib.Path, str],
directory: pathlib.Path | str,
all_: bool,
overwrite: bool,
rebuild_chapters: bool,
Expand Down
4 changes: 2 additions & 2 deletions plugin_cmds/cmd_get-annotations.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import click

from audible.exceptions import NotFoundError

from audible_cli.decorators import pass_client


@click.command("get-annotations")
@click.argument("asin")
@pass_client
async def cli(client, asin):
url = f"https://cde-ta-g7g.amazon.com/FionaCDEServiceEngine/sidecar"
url = "https://cde-ta-g7g.amazon.com/FionaCDEServiceEngine/sidecar"
params = {
"type": "AUDI",
"key": asin
Expand Down
10 changes: 5 additions & 5 deletions plugin_cmds/cmd_goodreads-transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
import pathlib

import click
from isbntools.app import isbn_from_words

from audible_cli.decorators import (
bunch_size_option,
timeout_option,
pass_client,
pass_session
pass_session,
timeout_option,
)
from audible_cli.models import Library
from audible_cli.utils import export_to_csv, parse_api_datetime
from isbntools.app import isbn_from_words


logger = logging.getLogger("audible_cli.cmds.cmd_goodreads-transform")
Expand All @@ -30,7 +31,6 @@
@pass_client
async def cli(session, client, output):
"""YOUR COMMAND DESCRIPTION"""

logger.debug("fetching library")
bunch_size = session.params.get("bunch_size")
library = await Library.from_api_full_sync(
Expand Down Expand Up @@ -71,7 +71,7 @@ def _prepare_library_for_export(library):
if authors is not None:
authors = ", ".join([a["name"] for a in authors])
is_finished = i.is_finished

isbn = i.isbn
if isbn is None:
isbn_counter += 1
Expand Down
1 change: 1 addition & 0 deletions plugin_cmds/cmd_image-urls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import click

from audible_cli.decorators import pass_client, timeout_option


Expand Down
9 changes: 5 additions & 4 deletions plugin_cmds/cmd_listening-stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from datetime import datetime

import click

from audible_cli.decorators import pass_client


Expand All @@ -15,8 +16,8 @@

def ms_to_hms(milliseconds):
seconds = int((milliseconds / 1000) % 60)
minutes = int(((milliseconds / (1000*60)) % 60))
hours = int(((milliseconds / (1000*60*60)) % 24))
minutes = int((milliseconds / (1000*60)) % 60)
hours = int((milliseconds / (1000*60*60)) % 24)
return {"hours": hours, "minutes": minutes, "seconds": seconds}


Expand All @@ -29,7 +30,7 @@ async def _get_stats_year(client, year):
store="Audible"
)
# iterate over each month
for stat in stats['aggregated_monthly_listening_stats']:
for stat in stats["aggregated_monthly_listening_stats"]:
stats_year[stat["interval_identifier"]] = ms_to_hms(stat["aggregated_sum"])
return stats_year

Expand All @@ -51,7 +52,7 @@ async def _get_stats_year(client, year):
)
@pass_client
async def cli(client, output, signup_year):
"""get and analyse listening statistics"""
"""Get and analyse listening statistics"""
year_range = [y for y in range(signup_year, current_year+1)]

r = await asyncio.gather(
Expand Down
11 changes: 7 additions & 4 deletions plugin_cmds/convert_oa_cred.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"""Converts the credentials.json file from OpenAudible >= v2.4 beta to an
audible-cli auth file. The credentials.json file from OpenAudible leaves
unchanged, so you can use one device registration for OpenAudible and
audible-cli."""
audible-cli.
"""


import json
import pathlib

import audible
import click

from audible_cli.config import pass_session


Expand Down Expand Up @@ -41,7 +43,7 @@ def make_auth_file(fn, origin):

website_cookies = dict()
for cookie in tokens["website_cookies"]:
website_cookies[cookie["Name"]] = cookie["Value"].replace(r'"', r'')
website_cookies[cookie["Name"]] = cookie["Value"].replace(r'"', r"")

data = {
"adp_token": adp_token,
Expand Down Expand Up @@ -70,10 +72,11 @@ def make_auth_file(fn, origin):
def cli(session, input):
"""Converts a OpenAudible credential file to a audible-cli auth file

Stores the auth files in app dir"""
Stores the auth files in app dir
"""
fdata = pathlib.Path(input).read_text("utf-8")
fdata = json.loads(fdata)

x = extract_data_from_file(fdata)
for k, v in x.items():
app_dir = pathlib.Path(session.get_app_dir())
Expand Down
2 changes: 1 addition & 1 deletion pyi_entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
multiprocessing.freeze_support()


if __name__ == '__main__':
if __name__ == "__main__":
from audible_cli import cli
cli.main()
Loading