mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 21:50:28 +02:00
feat: Add metadata fields for title, subtitle, author, series, publisher, and description in book form
This commit is contained in:
@@ -225,6 +225,7 @@ def api_calibre_opds_import() -> ResponseReturnValue:
|
|||||||
|
|
||||||
raw_series = metadata_payload.get("series") or metadata_payload.get("series_name")
|
raw_series = metadata_payload.get("series") or metadata_payload.get("series_name")
|
||||||
series_name = str(raw_series or "").strip()
|
series_name = str(raw_series or "").strip()
|
||||||
|
|
||||||
if series_name:
|
if series_name:
|
||||||
metadata_overrides["series"] = series_name
|
metadata_overrides["series"] = series_name
|
||||||
metadata_overrides.setdefault("series_name", 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("description", description_text)
|
||||||
metadata_overrides.setdefault("summary", 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()
|
settings = load_settings()
|
||||||
integrations = load_integration_settings()
|
integrations = load_integration_settings()
|
||||||
calibre_settings = integrations.get("calibre_opds", {})
|
calibre_settings = integrations.get("calibre_opds", {})
|
||||||
|
|||||||
@@ -600,6 +600,46 @@ def apply_book_step_form(
|
|||||||
|
|
||||||
pending.applied_speaker_config = (form.get("speaker_config") or "").strip() or None
|
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]]:
|
def persist_cover_image(extraction_result: Any, stored_path: Path) -> tuple[Optional[Path], Optional[str]]:
|
||||||
cover_bytes = getattr(extraction_result, "cover_image", None)
|
cover_bytes = getattr(extraction_result, "cover_image", None)
|
||||||
if not cover_bytes:
|
if not cover_bytes:
|
||||||
|
|||||||
+3
-27
@@ -389,33 +389,7 @@ def build_audiobookshelf_metadata(job: Job) -> Dict[str, Any]:
|
|||||||
tags.get("series_title"),
|
tags.get("series_title"),
|
||||||
tags.get("seriestitle"),
|
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
|
series_sequence = None
|
||||||
for key in _SERIES_SEQUENCE_TAG_KEYS:
|
for key in _SERIES_SEQUENCE_TAG_KEYS:
|
||||||
raw_value = tags.get(key)
|
raw_value = tags.get(key)
|
||||||
@@ -427,9 +401,11 @@ def build_audiobookshelf_metadata(job: Job) -> Dict[str, Any]:
|
|||||||
series_sequence = None
|
series_sequence = None
|
||||||
data: Dict[str, Any] = {
|
data: Dict[str, Any] = {
|
||||||
"title": title,
|
"title": title,
|
||||||
|
"subtitle": tags.get("subtitle"),
|
||||||
"authors": authors,
|
"authors": authors,
|
||||||
"narrators": narrators,
|
"narrators": narrators,
|
||||||
"description": description,
|
"description": description,
|
||||||
|
"publisher": tags.get("publisher"),
|
||||||
"genres": genres,
|
"genres": genres,
|
||||||
"tags": keywords,
|
"tags": keywords,
|
||||||
"language": language,
|
"language": language,
|
||||||
|
|||||||
@@ -220,6 +220,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</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">
|
<section class="form-section">
|
||||||
<h3 class="form-section__title">Narrator defaults</h3>
|
<h3 class="form-section__title">Narrator defaults</h3>
|
||||||
<div class="form-section__layout form-section__layout--split">
|
<div class="form-section__layout form-section__layout--split">
|
||||||
|
|||||||
Reference in New Issue
Block a user