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
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: FedericoCarboni/setup-ffmpeg@v2
- uses: FedericoCarboni/setup-ffmpeg@v3
id: setup-ffmpeg
with:
token: ${{ secrets.GH_API_TOKEN }}
token: ${{ github.token }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies = [
"jinja2",
"click>=6.0",
"progressbar2",
"lxml>=2.0",
"lxml>=2.0,<5.2.0",
"SQLAlchemy>=1.4,<2",
"attrs>=22.0",
"backports.strenum>=1.1.1;python_version<'3.11'",
Expand Down
8 changes: 4 additions & 4 deletions scripts/bundle_fa_subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def extract_font_awesome(icon: str, css: str) -> str:
name = f".fa-{icon}"

m = re.search(
name + ":+before {\s+content: " "['\"]+(?P<codepoint>[^'\"]+)",
name + r":+before {\s+content: " "['\"]+(?P<codepoint>[^'\"]+)",
css,
re.MULTILINE,
)
Expand Down Expand Up @@ -176,9 +176,9 @@ def bad_options(message: str) -> typing.NoReturn:
ExistingFileOrDir = click.Path(
dir_okay=True, file_okay=True, exists=True, path_type=pathlib.Path
) # type: ignore
FA_VERSION_TEMPLATE: typing.Final[
str
] = "https://use.fontawesome.com/releases/v{version}/fontawesome-free-{version}-web.zip"
FA_VERSION_TEMPLATE: typing.Final[str] = (
"https://use.fontawesome.com/releases/v{version}/fontawesome-free-{version}-web.zip"
)
LATEST_FA_VERSION: typing.Final[str] = "6.2.0"


Expand Down
2 changes: 1 addition & 1 deletion src/audio_feeder/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
try:
from enum import StrEnum # type: ignore[attr-defined]
except ImportError:
from backports.strenum import StrEnum # type: ignore[import]
from backports.strenum import StrEnum # type: ignore[import,no-redef]

__all__ = ("Self", "StrEnum")
16 changes: 6 additions & 10 deletions src/audio_feeder/_db_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

This exists for compatibility while the old database_handler code still exists.
"""

import typing

from ._object_types import ID, SchemaObject
Expand All @@ -16,17 +17,12 @@

# pragma: nocover
class DatabaseHandler(typing.Protocol):
def __init__(self, db_loc: PathType):
...
def __init__(self, db_loc: PathType): ...

def save_table(self, table_name: TableName, table_contents: Table) -> None:
...
def save_table(self, table_name: TableName, table_contents: Table) -> None: ...

def load_table(self, table_name: TableName) -> Table:
...
def load_table(self, table_name: TableName) -> Table: ...

def save_database(self, database: Database) -> None:
...
def save_database(self, database: Database) -> None: ...

def load_database(self) -> Database:
...
def load_database(self) -> Database: ...
7 changes: 3 additions & 4 deletions src/audio_feeder/_object_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
This is partially to allow typing without circular imports and partially to
allow typing on the generated schema types.
"""

import typing

from ._compat import Self
Expand All @@ -15,13 +16,11 @@
class SchemaObject(typing.Protocol):
id: ID

def to_dict(self) -> typing.Mapping[str, typing.Any]:
...
def to_dict(self) -> typing.Mapping[str, typing.Any]: ...

def to_dict_sparse(
self, *, _filter: typing.Callable = ...
) -> typing.Mapping[str, typing.Any]:
...
) -> typing.Mapping[str, typing.Any]: ...

def copy(self: Self) -> Self:
"""Make a new copy of this object."""
Expand Down
1 change: 1 addition & 0 deletions src/audio_feeder/cache_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Functions useful for caching.
"""

import functools
import typing

Expand Down
1 change: 1 addition & 0 deletions src/audio_feeder/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Command line scripts
"""

import pathlib

import click
Expand Down
21 changes: 10 additions & 11 deletions src/audio_feeder/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Configuration manager - handles the application's global configuration.
"""

import base64
import functools
import graphlib
Expand Down Expand Up @@ -64,13 +65,11 @@ class TemplateStr(str):


@typing.overload
def _path_converter(s: typing.Union[TemplatePath, TemplateStr]) -> TemplatePath:
...
def _path_converter(s: typing.Union[TemplatePath, TemplateStr]) -> TemplatePath: ...


@typing.overload
def _path_converter(s: typing.Union[str, Path]) -> Path:
...
def _path_converter(s: typing.Union[str, Path]) -> Path: ...


def _path_converter(s):
Expand Down Expand Up @@ -288,25 +287,25 @@ def find_requirements(attr_name: str) -> typing.Sequence[str]:
@typing.overload
def _make_single_replacement(
self, value: typing.Union[str, TemplateStr], replacements: _ReplacementsMapType
) -> str:
...
) -> str: ...

@typing.overload
def _make_single_replacement(
self,
value: typing.Union[Path, TemplatePath],
replacements: _ReplacementsMapType,
) -> Path:
...
) -> Path: ...

def _make_single_replacement(self, value, replacements):
if isinstance(value, TemplatePath):
return Path(
*(
self._make_single_replacement(
TemplateStr(component)
if component.startswith("{{")
else component,
(
TemplateStr(component)
if component.startswith("{{")
else component
),
replacements=replacements,
)
for component in value.parts
Expand Down
8 changes: 4 additions & 4 deletions src/audio_feeder/directory_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ class AudiobookLoader(BaseAudioLoader):
)

COVER_PATTERNS: typing.Final[typing.Sequence[re.Pattern]] = [
re.compile("$.*\-Cover^", re.IGNORECASE),
re.compile(r"$.*-Cover^", re.IGNORECASE),
re.compile("cover", re.IGNORECASE),
]

DIR_NAME_RE = re.compile(
"(?P<authors>.+?)(?= \- )"
+ "(?: \- \[(?P<series_name>.*?) (?P<series_number>\d+)\])? \- "
+ "(?P<title>.*$)"
r"(?P<authors>.+?)(?= - )"
+ r"(?: - \[(?P<series_name>.*?) (?P<series_number>\d+)\])? - "
+ r"(?P<title>.*$)"
)

@classmethod
Expand Down
3 changes: 3 additions & 0 deletions src/audio_feeder/file_probe.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Utilities for determining information about the files."""

import copy
import datetime
import functools
Expand Down Expand Up @@ -66,6 +67,7 @@ class OverdriveMediaMarker:
@classmethod
def from_xml(cls, xml: str) -> typing.Sequence[Self]:
root = lxml.etree.fromstring(xml)
assert isinstance(root.tag, str)
if root.tag != "Markers":
raise ValueError(
f"Unknown media marker tag: {root.tag}. " + "Should be 'Markers'"
Expand Down Expand Up @@ -196,6 +198,7 @@ def from_json(cls, json_dict: FFProbeReturnJSON) -> Self:
if omm_tag in format_info.tags:
omm_tags = OverdriveMediaMarker.from_xml(format_info.tags[omm_tag])
omm_tags = sorted(omm_tags)
assert format_info.duration is not None # TODO: Handle this gracefully

for num, start_tag, end_tag in zip(
itertools.count(1),
Expand Down
9 changes: 3 additions & 6 deletions src/audio_feeder/hash_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@


class _Hash(typing.Protocol):
def update(self, data: bytes) -> None:
...
def update(self, data: bytes) -> None: ...

def hexdigest(self) -> str:
...
def hexdigest(self) -> str: ...

def digest(self) -> bytes:
...
def digest(self) -> bytes: ...


_HashFunc = typing.Callable[[], _Hash]
Expand Down
1 change: 1 addition & 0 deletions src/audio_feeder/html_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Module for handling HTML-related operations
"""

import bisect
import io
from html.parser import HTMLParser
Expand Down
15 changes: 9 additions & 6 deletions src/audio_feeder/m4btools.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Tools to split or create chaptered m4b files."""

import copy
import functools
import logging
Expand Down Expand Up @@ -270,7 +271,7 @@ def _merge_subsets(
"\n".join(_to_file_list_entry(x.path, x.start, x.end) for x in subsets)
)

cmd_before = [
cmd_before: Sequence[str] = (
"ffmpeg",
"-loglevel",
"error",
Expand All @@ -288,9 +289,9 @@ def _merge_subsets(
"0",
"-c:a",
audio_codec,
]
)

cmd_after = []
cmd_after: Sequence[str] = ()

logging.debug("file list:\n%s", file_list.read_text())
_try_video_variations(
Expand Down Expand Up @@ -331,7 +332,7 @@ def _extract_subset(
if subset.end:
subset_directives.extend(("-to", str(subset.end)))

cmd_before = [
cmd_before: Sequence[str] = [
"ffmpeg",
"-loglevel",
"error",
Expand All @@ -348,7 +349,7 @@ def _extract_subset(
"copy",
]

cmd_after = []
cmd_after: Sequence[str] = ()

_try_video_variations(
cmd_before,
Expand Down Expand Up @@ -412,7 +413,9 @@ def chapter_split_jobs(

job_pool.append(new_job)

if abs(chapter.end_time - file_infos[fpath].format_info.duration) < 0.25:
duration = file_infos[fpath].format_info.duration
assert duration is not None # TODO: Handle this gracefully
if abs(chapter.end_time - duration) < 0.25:
end_time = None
else:
end_time = chapter.end_time
Expand Down
1 change: 1 addition & 0 deletions src/audio_feeder/media_renderer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Utilties for handling media rendering for derived feeds."""

import functools
import hashlib
import json
Expand Down
6 changes: 3 additions & 3 deletions src/audio_feeder/object_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@ class BaseObject:
id: ID

def to_dict(self) -> typing.Mapping[str, typing.Any]:
return attrs.asdict(self)
return attrs.asdict(self) # type: ignore[arg-type]

def to_dict_sparse(
self, *, _filter=_filter_sparse
) -> typing.Mapping[str, typing.Any]:
return attrs.asdict(self, filter=_filter)
return attrs.asdict(self, filter=_filter) # type: ignore[arg-type]

def copy(self: Self) -> Self:
"""Make a new copy of this object."""
return attrs.evolve(self)
return attrs.evolve(self) # type: ignore[misc]


# SqlAlchemy has problems with slots ☹
Expand Down
1 change: 1 addition & 0 deletions src/audio_feeder/page_generator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Page generator
"""

import math
import os
import string
Expand Down
1 change: 1 addition & 0 deletions src/audio_feeder/resources.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Modules for handling package data resources."""

import hashlib
import importlib.resources
import os
Expand Down
4 changes: 3 additions & 1 deletion src/audio_feeder/rss_feeds.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
RSS Feed generators
"""

import datetime
import functools
import json
Expand Down Expand Up @@ -207,9 +208,10 @@ def _executor() -> futures.Executor:
file_hash = None

if entry_obj.file_metadata is None or m_rel_path not in entry_obj.file_metadata:
# TODO: Use an assertion or fix fl.path possibly being None
metadata_futures.append(
_executor().submit(
lambda fl, fh: (fl, fp.FileInfo.from_file(fl.path), fh),
lambda fl, fh: (fl, fp.FileInfo.from_file(fl.path), fh), # type: ignore[arg-type]
file_loc,
file_hash,
)
Expand Down
1 change: 1 addition & 0 deletions src/audio_feeder/sql_database_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import attrs
import sqlalchemy as sa
import sqlalchemy.types # pylint: disable=unused-import
from sqlalchemy import orm

from . import _object_types as ot
Expand Down
2 changes: 1 addition & 1 deletion src/audio_feeder/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
# This technically violates PEP 484, but it works. See
# https://github.com/python/mypy/issues/14023 for more details.
_PBar = typing.TypeVar(
"_PBar", bound=typing.Callable[[typing.Iterable[_T]], typing.Iterable[_T]]
"_PBar", bound=typing.Callable[[typing.Iterable[_T]], typing.Iterable[_T]] # type: ignore[valid-type]
)


Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ def change_config_loc(new_config_loc: _OptPath = None) -> typing.Iterator[_OptPa
class SentinelEnum(enum.Enum):
Sentinel = enum.auto

old_config_dir: typing.Union[
str, typing.Literal[SentinelEnum.Sentinel]
] = os.environ.get("AF_CONFIG_DIR", SentinelEnum.Sentinel)
old_config_dir: typing.Union[str, typing.Literal[SentinelEnum.Sentinel]] = (
os.environ.get("AF_CONFIG_DIR", SentinelEnum.Sentinel)
)
try:
if new_config_loc is None:
del os.environ["AF_CONFIG_DIR"]
Expand Down
1 change: 1 addition & 0 deletions tests/test_updater.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Tests for audio_feeder.updater"""

import contextlib
import json
import pathlib
Expand Down
Loading
Loading