feat: Add metadata fields for title, subtitle, author, series, publisher, and description in book form

This commit is contained in:
JB
2025-12-02 10:51:51 -08:00
parent 1e13c901fe
commit a501e96b12
4 changed files with 103 additions and 27 deletions
+13
View File
@@ -225,6 +225,7 @@ def api_calibre_opds_import() -> ResponseReturnValue:
raw_series = metadata_payload.get("series") or metadata_payload.get("series_name")
series_name = str(raw_series or "").strip()
if series_name:
metadata_overrides["series"] = series_name
metadata_overrides.setdefault("series_name", series_name)
@@ -258,6 +259,18 @@ def api_calibre_opds_import() -> ResponseReturnValue:
metadata_overrides.setdefault("description", description_text)
metadata_overrides.setdefault("summary", description_text)
subtitle_value = metadata_payload.get("subtitle")
if subtitle_value:
subtitle_text = _stringify_metadata_value(subtitle_value)
if subtitle_text:
metadata_overrides.setdefault("subtitle", subtitle_text)
publisher_value = metadata_payload.get("publisher")
if publisher_value:
publisher_text = _stringify_metadata_value(publisher_value)
if publisher_text:
metadata_overrides.setdefault("publisher", publisher_text)
settings = load_settings()
integrations = load_integration_settings()
calibre_settings = integrations.get("calibre_opds", {})
+40
View File
@@ -600,6 +600,46 @@ def apply_book_step_form(
pending.applied_speaker_config = (form.get("speaker_config") or "").strip() or None
# Metadata updates
if "meta_title" in form:
pending.metadata_tags["title"] = str(form.get("meta_title", "")).strip()
if "meta_subtitle" in form:
pending.metadata_tags["subtitle"] = str(form.get("meta_subtitle", "")).strip()
if "meta_author" in form:
authors = str(form.get("meta_author", "")).strip()
pending.metadata_tags["authors"] = authors
pending.metadata_tags["author"] = authors
if "meta_series" in form:
series = str(form.get("meta_series", "")).strip()
pending.metadata_tags["series"] = series
pending.metadata_tags["series_name"] = series
pending.metadata_tags["seriesname"] = series
pending.metadata_tags["series_title"] = series
pending.metadata_tags["seriestitle"] = series
# If user manually edits series, update opds_series too so it persists
if "opds_series" in pending.metadata_tags:
pending.metadata_tags["opds_series"] = series
if "meta_series_index" in form:
idx = str(form.get("meta_series_index", "")).strip()
pending.metadata_tags["series_index"] = idx
pending.metadata_tags["series_sequence"] = idx
if "meta_publisher" in form:
pending.metadata_tags["publisher"] = str(form.get("meta_publisher", "")).strip()
if "meta_description" in form:
desc = str(form.get("meta_description", "")).strip()
pending.metadata_tags["description"] = desc
pending.metadata_tags["summary"] = desc
if coerce_bool(form.get("remove_cover"), False):
pending.cover_image_path = None
pending.cover_image_mime = None
def persist_cover_image(extraction_result: Any, stored_path: Path) -> tuple[Optional[Path], Optional[str]]:
cover_bytes = getattr(extraction_result, "cover_image", None)
if not cover_bytes:
+3 -27
View File
@@ -389,33 +389,7 @@ def build_audiobookshelf_metadata(job: Job) -> Dict[str, Any]:
tags.get("series_title"),
tags.get("seriestitle"),
)
if series_name:
normalized_series = series_name.strip()
if normalized_series:
author_candidates: set[str] = set()
for name in authors:
author_name = name.strip()
if author_name:
author_candidates.add(author_name.casefold())
raw_author_fields = (
tags.get("authors"),
tags.get("author"),
tags.get("album_artist"),
tags.get("artist"),
tags.get("writer"),
tags.get("composer"),
)
for raw_author in raw_author_fields:
if not raw_author:
continue
author_text = str(raw_author).strip()
if author_text:
author_candidates.add(author_text.casefold())
joined_authors = ", ".join(authors)
if joined_authors:
author_candidates.add(joined_authors.casefold())
if normalized_series.casefold() in author_candidates:
series_name = None
series_sequence = None
for key in _SERIES_SEQUENCE_TAG_KEYS:
raw_value = tags.get(key)
@@ -427,9 +401,11 @@ def build_audiobookshelf_metadata(job: Job) -> Dict[str, Any]:
series_sequence = None
data: Dict[str, Any] = {
"title": title,
"subtitle": tags.get("subtitle"),
"authors": authors,
"narrators": narrators,
"description": description,
"publisher": tags.get("publisher"),
"genres": genres,
"tags": keywords,
"language": language,
@@ -220,6 +220,53 @@
</div>
</section>
<section class="form-section">
<h3 class="form-section__title">Book Metadata</h3>
<div class="field-grid field-grid--compact">
<div class="field field--span-2">
<label for="meta_title">Title</label>
<input type="text" id="meta_title" name="meta_title" value="{{ pending.metadata_tags.get('title', '') }}" {{ 'disabled' if readonly else '' }}>
</div>
<div class="field field--span-2">
<label for="meta_subtitle">Subtitle</label>
<input type="text" id="meta_subtitle" name="meta_subtitle" value="{{ pending.metadata_tags.get('subtitle', '') }}" {{ 'disabled' if readonly else '' }}>
</div>
<div class="field field--span-2">
<label for="meta_author">Author(s)</label>
<input type="text" id="meta_author" name="meta_author" value="{{ pending.metadata_tags.get('authors', '') or pending.metadata_tags.get('author', '') }}" {{ 'disabled' if readonly else '' }}>
<p class="hint">Comma separated for multiple authors</p>
</div>
<div class="field">
<label for="meta_series">Series</label>
<input type="text" id="meta_series" name="meta_series" value="{{ pending.metadata_tags.get('series', '') or pending.metadata_tags.get('series_name', '') }}" {{ 'disabled' if readonly else '' }}>
</div>
<div class="field">
<label for="meta_series_index">Series Index</label>
<input type="text" id="meta_series_index" name="meta_series_index" value="{{ pending.metadata_tags.get('series_index', '') or pending.metadata_tags.get('series_sequence', '') }}" {{ 'disabled' if readonly else '' }}>
</div>
<div class="field field--span-2">
<label for="meta_publisher">Publisher</label>
<input type="text" id="meta_publisher" name="meta_publisher" value="{{ pending.metadata_tags.get('publisher', '') }}" {{ 'disabled' if readonly else '' }}>
</div>
<div class="field field--span-2">
<label for="meta_description">Description</label>
<textarea id="meta_description" name="meta_description" rows="3" {{ 'disabled' if readonly else '' }}>{{ pending.metadata_tags.get('description', '') or pending.metadata_tags.get('summary', '') }}</textarea>
</div>
<div class="field field--stack field--span-2">
<label class="toggle-pill">
<input type="hidden" name="remove_cover" value="false">
<input type="checkbox" name="remove_cover" value="true" {{ 'disabled' if readonly else '' }}>
<span>Remove Cover Image</span>
</label>
{% if pending.cover_image_path %}
<p class="hint">Cover image currently exists.</p>
{% else %}
<p class="hint">No cover image found.</p>
{% endif %}
</div>
</div>
</section>
<section class="form-section">
<h3 class="form-section__title">Narrator defaults</h3>
<div class="form-section__layout form-section__layout--split">