diff --git a/doclang/_packaging.py b/doclang/_packaging.py
index 0dbf10e..b917366 100644
--- a/doclang/_packaging.py
+++ b/doclang/_packaging.py
@@ -17,6 +17,17 @@
+
+
+
+
+
+
+
+
+
+
+
"""
@@ -37,6 +48,9 @@
Mapping[int, Union[str, Path]],
]
+# Whole-track media (audio/, video/) uses the same 1-based positional model as pages/.
+MediaInput = PagesInput
+
AssetsInput = Union[
str,
Path,
@@ -96,25 +110,71 @@ def _place_document(stage: Path, document: Path) -> None:
shutil.copy2(document, stage / "document.xml")
-def _place_pages(stage: Path, pages: PagesInput) -> None:
- pages_dir = stage / "pages"
- if isinstance(pages, Mapping):
- for page_number, source in pages.items():
- if not isinstance(page_number, int) or page_number < 1:
- raise PackagingError(f"Page numbers must be positive integers, got {page_number!r}")
+def _place_indexed(
+ stage: Path,
+ spec: PagesInput,
+ *,
+ subdir: str,
+ file_label: str,
+ dir_label: str,
+ number_label: str,
+) -> None:
+ """Place 1-based positionally-indexed files (``{N}.{ext}``) under ``stage/subdir``.
+
+ Shared by ``pages/`` (page images) and ``audio/`` / ``video/`` (whole-track media),
+ which all key a file to an integer position in the document.
+ """
+ target_dir = stage / subdir
+ if isinstance(spec, Mapping):
+ for number, source in spec.items():
+ if not isinstance(number, int) or number < 1:
+ raise PackagingError(f"{number_label} must be positive integers, got {number!r}")
source_path = Path(source)
- destination = pages_dir / f"{page_number}{source_path.suffix}"
- _copy_file(source_path, destination, label="Page file")
+ destination = target_dir / f"{number}{source_path.suffix}"
+ _copy_file(source_path, destination, label=file_label)
return
- if isinstance(pages, str | Path):
- _copy_tree_into(Path(pages), pages_dir, label="Pages directory")
+ if isinstance(spec, str | Path):
+ _copy_tree_into(Path(spec), target_dir, label=dir_label)
return
- for index, source in enumerate(pages, start=1):
+ for index, source in enumerate(spec, start=1):
source_path = Path(source)
- destination = pages_dir / f"{index}{source_path.suffix}"
- _copy_file(source_path, destination, label="Page file")
+ destination = target_dir / f"{index}{source_path.suffix}"
+ _copy_file(source_path, destination, label=file_label)
+
+
+def _place_pages(stage: Path, pages: PagesInput) -> None:
+ _place_indexed(
+ stage,
+ pages,
+ subdir="pages",
+ file_label="Page file",
+ dir_label="Pages directory",
+ number_label="Page numbers",
+ )
+
+
+def _place_audio(stage: Path, audio: MediaInput) -> None:
+ _place_indexed(
+ stage,
+ audio,
+ subdir="audio",
+ file_label="Audio file",
+ dir_label="Audio directory",
+ number_label="Audio track numbers",
+ )
+
+
+def _place_video(stage: Path, video: MediaInput) -> None:
+ _place_indexed(
+ stage,
+ video,
+ subdir="video",
+ file_label="Video file",
+ dir_label="Video directory",
+ number_label="Video track numbers",
+ )
def _place_assets(stage: Path, assets: AssetsInput) -> None:
@@ -164,6 +224,8 @@ def _pack(
output: Union[str, Path, None] = None,
pages: PagesInput | None = None,
assets: AssetsInput | None = None,
+ audio: MediaInput | None = None,
+ video: MediaInput | None = None,
validate: bool = False,
) -> Path:
document_path = Path(document)
@@ -176,6 +238,10 @@ def _pack(
_place_pages(stage, pages)
if assets is not None:
_place_assets(stage, assets)
+ if audio is not None:
+ _place_audio(stage, audio)
+ if video is not None:
+ _place_video(stage, video)
_write_opc_metadata(stage)
if validate:
diff --git a/doclang/cli.py b/doclang/cli.py
index 0db1f5f..32fced7 100644
--- a/doclang/cli.py
+++ b/doclang/cli.py
@@ -200,6 +200,38 @@ def pack(
file_okay=False,
dir_okay=True,
),
+ audio_dir: Path | None = typer.Option(
+ None,
+ "--audio-dir",
+ help="Directory of whole-track audio files (2.mp3, 4.ogg, …)",
+ exists=True,
+ file_okay=False,
+ dir_okay=True,
+ ),
+ audio_files: list[Path] | None = typer.Option(
+ None,
+ "--audio",
+ help="Whole-track audio file; repeat to add tracks in order (renumbered as 1.ext, 2.ext, …)",
+ exists=True,
+ file_okay=True,
+ dir_okay=False,
+ ),
+ video_dir: Path | None = typer.Option(
+ None,
+ "--video-dir",
+ help="Directory of whole-track video files (3.mp4, 4.mkv, …)",
+ exists=True,
+ file_okay=False,
+ dir_okay=True,
+ ),
+ video_files: list[Path] | None = typer.Option(
+ None,
+ "--video",
+ help="Whole-track video file; repeat to add tracks in order (renumbered as 1.ext, 2.ext, …)",
+ exists=True,
+ file_okay=True,
+ dir_okay=False,
+ ),
validate_before_pack: bool = typer.Option(False, "--validate", help="Validate document before packing"),
quiet: bool = typer.Option(False, "--quiet", "-q", help="Quiet mode (exit code only)"),
):
@@ -207,8 +239,10 @@ def pack(
Pack a DocLang markup file and optional media into a .dclx archive.
DOCUMENT is copied to document.xml. Optional page images (--pages, --page)
- are placed under pages/. Optional payload files (--assets, --asset) are
- placed under assets/ for URIs referenced in the markup. OPC metadata
+ are placed under pages/. Optional whole-track audio/video (--audio-dir,
+ --audio, --video-dir, --video) are placed under audio/ and video/ as
+ {N}.{ext} for the Nth