From f2ab62aeab24c0605d3424378b94ede3feb7e66e Mon Sep 17 00:00:00 2001 From: JB Date: Sun, 2 Nov 2025 07:04:29 -0800 Subject: [PATCH] feat: Enhance Audiobookshelf metadata handling to support additional series and book number tags --- abogen/web/service.py | 22 +++++++++++++++++++--- tests/test_service.py | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/abogen/web/service.py b/abogen/web/service.py index 102b6ed..c3f8050 100644 --- a/abogen/web/service.py +++ b/abogen/web/service.py @@ -13,7 +13,7 @@ import traceback from dataclasses import dataclass, field from enum import Enum from pathlib import Path -from typing import Any, Callable, Dict, Iterable, List, Optional, Mapping +from typing import Any, Callable, Dict, Iterable, List, Optional, Mapping, Tuple from abogen.utils import get_internal_cache_path, get_user_settings_dir, load_config from abogen.voice_cache import bootstrap_voice_cache @@ -264,6 +264,16 @@ def _split_people_field(raw: Any) -> List[str]: _LIST_SPLIT_RE = re.compile(r"[;,\n]") +_SERIES_SEQUENCE_TAG_KEYS: Tuple[str, ...] = ( + "series_index", + "series_position", + "series_sequence", + "series_number", + "seriesnumber", + "book_number", + "booknumber", +) + def _split_simple_list(raw: Any) -> List[str]: if raw is None: @@ -340,8 +350,14 @@ def _build_audiobookshelf_metadata(job: Job) -> Dict[str, Any]: genres = _split_simple_list(tags.get("genre")) keywords = _split_simple_list(tags.get("tags") or tags.get("keywords")) language = _first_nonempty(tags.get("language"), tags.get("lang")) or job.language or "" - series_name = _first_nonempty(tags.get("series"), tags.get("series_name")) - series_sequence = _first_nonempty(tags.get("series_index"), tags.get("series_position")) + series_name = _first_nonempty( + tags.get("series"), + tags.get("series_name"), + tags.get("seriesname"), + tags.get("series_title"), + tags.get("seriestitle"), + ) + series_sequence = _first_nonempty(*(tags.get(key) for key in _SERIES_SEQUENCE_TAG_KEYS)) data: Dict[str, Any] = { "title": title, "authors": authors, diff --git a/tests/test_service.py b/tests/test_service.py index af535ff..c0fe906 100644 --- a/tests/test_service.py +++ b/tests/test_service.py @@ -2,7 +2,7 @@ from __future__ import annotations import io import time -from abogen.web.service import Job, JobStatus, build_service, _JOB_LOGGER +from abogen.web.service import Job, JobStatus, build_service, _JOB_LOGGER, _build_audiobookshelf_metadata def test_service_processes_job(tmp_path): @@ -156,4 +156,35 @@ def test_retry_removes_failed_job(tmp_path): assert job.id not in job_ids assert new_job.id in job_ids finally: - service.shutdown() \ No newline at end of file + service.shutdown() + + +def test_audiobookshelf_metadata_uses_book_number(tmp_path): + source = tmp_path / "book.txt" + source.write_text("content", encoding="utf-8") + + job = Job( + id="job-abs", + original_filename="book.txt", + stored_path=source, + language="en", + voice="af_alloy", + speed=1.0, + use_gpu=False, + subtitle_mode="Sentence", + output_format="mp3", + save_mode="Save next to input file", + output_folder=tmp_path, + replace_single_newlines=False, + subtitle_format="srt", + created_at=time.time(), + metadata_tags={ + "series": "Example Saga", + "book_number": "7", + }, + ) + + metadata = _build_audiobookshelf_metadata(job) + + assert metadata["seriesName"] == "Example Saga" + assert metadata["seriesSequence"] == "7" \ No newline at end of file