Coverage for src/epublib/ncx/reset.py: 100%
49 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-07 15:00 -0300
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-07 15:00 -0300
1from pathlib import Path
3from bs4.element import NamespacedAttribute
5from epublib.exceptions import EPUBError
6from epublib.ncx.resource import NCXFile
7from epublib.package.metadata import BookMetadata, ValuedMetadataItem
8from epublib.types import BookProtocol
10ncx_template = """<?xml version="1.0" encoding="UTF-8"?>
11<ncx version="2005-1" {lang_attr} xmlns="http://www.daisy.org/z3986/2005/ncx/">
12<head></head>
13<docTitle><text>{title}</text></docTitle>
14<navMap></navMap>
15</ncx
16"""
19def get_minimal_ncx_content(title: str, lang: str | None) -> bytes:
20 """
21 Get a minimal NCX file content with the given title and language.
22 Caution: the minimality of this template is in regard to the parsing
23 available in this library. To get a minimal valid NCX file, consider
24 using `EPUB.generate_ncx` instead.
25 """
26 if lang:
27 lang_attr = f'xml:lang="{lang}"'
28 else:
29 lang_attr = ""
30 return ncx_template.format(title=title, lang_attr=lang_attr).encode()
33def generate_ncx(book: BookProtocol, filename: str | Path | None = None) -> NCXFile:
34 if filename is None:
35 filename = book.base_dir / "toc.ncx"
37 if not book.metadata.title:
38 raise EPUBError("Can't generate NCX without book title in metadata")
40 if book.ncx is not None:
41 raise EPUBError(
42 "Can't generate NCX as it already exists. Try "
43 f"{book.__class__.__name__}.reset_ncx() instead"
44 )
46 ncx = NCXFile(
47 get_minimal_ncx_content(
48 book.metadata.title,
49 book.metadata.language,
50 ),
51 filename,
52 )
54 ncx = reset_ncx(book, ncx)
55 book.resources.add(ncx)
56 book.spine.tag["toc"] = book.manifest[ncx.filename].id
57 return ncx
60def reset_author(ncx: NCXFile, metadata: BookMetadata) -> None:
61 creator_item = metadata.get("creator")
62 creator = (
63 creator_item.value if isinstance(creator_item, ValuedMetadataItem) else None
64 )
66 for author in ncx.authors:
67 if author.text == creator:
68 continue
69 __ = ncx.remove_author(author)
71 if creator and not ncx.get_author(creator):
72 __ = ncx.add_author(creator)
75def reset_ncx(book: BookProtocol, ncx: NCXFile | None = None) -> NCXFile:
76 if not book.metadata.title:
77 raise EPUBError("Can't reset NCX without book title in metadata")
79 if ncx is None:
80 ncx = book.ncx
82 if ncx is None:
83 return generate_ncx(book)
85 ncx.title.text = book.metadata.title
86 reset_author(ncx, book.metadata)
88 if book.metadata.language:
89 ncx.soup.ncx[NamespacedAttribute("xml", "lang")] = book.metadata.language
91 __ = ncx.sync_toc(book.nav)
92 if book.nav.page_list:
93 __ = ncx.sync_page_list(book.nav)
94 __ = ncx.sync_head(book.metadata)
96 return ncx