mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
feat: Add subtitle support in OPDSEntry and enhance metadata handling in API routes
This commit is contained in:
@@ -33,6 +33,32 @@ def test_calibre_opds_feed_exposes_series_metadata() -> None:
|
||||
assert feed_dict["entries"][0]["series_index"] == 4.0
|
||||
|
||||
|
||||
def test_calibre_opds_feed_exposes_subtitle_metadata() -> None:
|
||||
client = CalibreOPDSClient("http://example.com/catalog")
|
||||
xml_payload = """<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||
<feed xmlns=\"http://www.w3.org/2005/Atom\"
|
||||
xmlns:calibre_md=\"http://calibre.kovidgoyal.net/2009/metadata\">
|
||||
<id>catalog</id>
|
||||
<title>Example Catalog</title>
|
||||
<entry>
|
||||
<id>book-1</id>
|
||||
<title>Sample Book</title>
|
||||
<calibre_md:subtitle>A Novel</calibre_md:subtitle>
|
||||
<link rel=\"http://opds-spec.org/acquisition\"
|
||||
href=\"books/sample.epub\"
|
||||
type=\"application/epub+zip\" />
|
||||
</entry>
|
||||
</feed>
|
||||
"""
|
||||
|
||||
feed = client._parse_feed(xml_payload, base_url="http://example.com/catalog")
|
||||
assert feed.entries
|
||||
assert feed.entries[0].subtitle == "A Novel"
|
||||
|
||||
feed_dict = feed_to_dict(feed)
|
||||
assert feed_dict["entries"][0]["subtitle"] == "A Novel"
|
||||
|
||||
|
||||
def test_calibre_opds_feed_extracts_series_from_categories() -> None:
|
||||
client = CalibreOPDSClient("http://example.com/catalog")
|
||||
xml_payload = """<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abogen.web.routes.api import _opds_metadata_overrides
|
||||
|
||||
|
||||
def test_opds_metadata_overrides_maps_author_and_subtitle() -> None:
|
||||
overrides = _opds_metadata_overrides(
|
||||
{
|
||||
"authors": ["Alexandre Dumas"],
|
||||
"subtitle": "Unabridged",
|
||||
"series": "Example",
|
||||
"series_index": 2,
|
||||
"tags": ["Fiction", "Classic"],
|
||||
"summary": "Summary text",
|
||||
}
|
||||
)
|
||||
|
||||
assert overrides["authors"] == "Alexandre Dumas"
|
||||
assert overrides["author"] == "Alexandre Dumas"
|
||||
assert overrides["subtitle"] == "Unabridged"
|
||||
|
||||
# Existing behavior still present
|
||||
assert overrides["series"] == "Example"
|
||||
assert overrides["series_index"] == "2"
|
||||
assert overrides["tags"] == "Fiction, Classic"
|
||||
assert overrides["description"] == "Summary text"
|
||||
|
||||
|
||||
def test_opds_metadata_overrides_accepts_author_string() -> None:
|
||||
overrides = _opds_metadata_overrides({"author": "Mary Shelley"})
|
||||
assert overrides["authors"] == "Mary Shelley"
|
||||
assert overrides["author"] == "Mary Shelley"
|
||||
@@ -0,0 +1,111 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from abogen.utils import load_config, save_config
|
||||
from abogen.web.app import create_app
|
||||
|
||||
|
||||
def test_settings_update_preserves_abs_api_token_when_blank(tmp_path):
|
||||
# Seed config with stored integration secret.
|
||||
save_config(
|
||||
{
|
||||
"language": "en",
|
||||
"integrations": {
|
||||
"audiobookshelf": {
|
||||
"enabled": True,
|
||||
"base_url": "https://abs.example",
|
||||
"api_token": "SECRET_TOKEN",
|
||||
"library_id": "lib1",
|
||||
"folder_id": "fold1",
|
||||
"verify_ssl": True,
|
||||
},
|
||||
"calibre_opds": {
|
||||
"enabled": True,
|
||||
"base_url": "https://opds.example",
|
||||
"username": "user",
|
||||
"password": "SECRET_PASS",
|
||||
"verify_ssl": True,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
app = create_app(
|
||||
{
|
||||
"TESTING": True,
|
||||
"SECRET_KEY": "test",
|
||||
"OUTPUT_FOLDER": str(tmp_path),
|
||||
"UPLOAD_FOLDER": str(tmp_path / "uploads"),
|
||||
}
|
||||
)
|
||||
|
||||
with app.test_client() as client:
|
||||
# Emulate saving settings where integrations are present but secrets are blank
|
||||
# (typical of masked password/token inputs).
|
||||
resp = client.post(
|
||||
"/settings/update",
|
||||
data={
|
||||
"language": "en",
|
||||
"output_format": "mp3",
|
||||
# ABS integration fields (token blank)
|
||||
"audiobookshelf_enabled": "on",
|
||||
"audiobookshelf_base_url": "https://abs.example",
|
||||
"audiobookshelf_api_token": "",
|
||||
"audiobookshelf_library_id": "lib1",
|
||||
"audiobookshelf_folder_id": "fold1",
|
||||
"audiobookshelf_verify_ssl": "on",
|
||||
# Calibre OPDS integration fields (password blank)
|
||||
"calibre_opds_enabled": "on",
|
||||
"calibre_opds_base_url": "https://opds.example",
|
||||
"calibre_opds_username": "user",
|
||||
"calibre_opds_password": "",
|
||||
"calibre_opds_verify_ssl": "on",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert resp.status_code in {302, 303}
|
||||
|
||||
cfg = load_config() or {}
|
||||
integrations = cfg.get("integrations") or {}
|
||||
|
||||
assert integrations["audiobookshelf"]["api_token"] == "SECRET_TOKEN"
|
||||
assert integrations["calibre_opds"]["password"] == "SECRET_PASS"
|
||||
|
||||
|
||||
def test_settings_update_preserves_secrets_when_fields_missing(tmp_path):
|
||||
save_config(
|
||||
{
|
||||
"language": "en",
|
||||
"integrations": {
|
||||
"audiobookshelf": {"api_token": "SECRET_TOKEN"},
|
||||
"calibre_opds": {"password": "SECRET_PASS"},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
app = create_app(
|
||||
{
|
||||
"TESTING": True,
|
||||
"SECRET_KEY": "test",
|
||||
"OUTPUT_FOLDER": str(tmp_path),
|
||||
"UPLOAD_FOLDER": str(tmp_path / "uploads"),
|
||||
}
|
||||
)
|
||||
|
||||
with app.test_client() as client:
|
||||
# Post unrelated changes; omit integration fields completely.
|
||||
resp = client.post(
|
||||
"/settings/update",
|
||||
data={
|
||||
"language": "en",
|
||||
"output_format": "wav",
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
assert resp.status_code in {302, 303}
|
||||
|
||||
cfg = load_config() or {}
|
||||
integrations = cfg.get("integrations") or {}
|
||||
assert integrations["audiobookshelf"]["api_token"] == "SECRET_TOKEN"
|
||||
assert integrations["calibre_opds"]["password"] == "SECRET_PASS"
|
||||
Reference in New Issue
Block a user