refactor: add mkvpropedit as alternative for in-place modification

This commit is contained in:
Louis Heredero 2025-05-12 10:43:23 +02:00
parent 7a49849ee9
commit 4b6576ec53
Signed by: HEL
GPG Key ID: 8D83DE470F8544E7
3 changed files with 82 additions and 25 deletions

View File

@ -1,4 +1,4 @@
FROM debian:bullseye-slim AS builder FROM debian:bookworm-slim AS builder
# Install ffmpeg and mkvtoolnix # Install ffmpeg and mkvtoolnix
# but only keep the binaries and libs for ffprobe and mkvmerge # but only keep the binaries and libs for ffprobe and mkvmerge
@ -7,11 +7,13 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& mkdir -p /artifacts/bin /artifacts/lib \ && mkdir -p /artifacts/bin /artifacts/lib \
&& cp $(which ffprobe) /artifacts/bin/ \ && cp $(which ffprobe) /artifacts/bin/ \
&& cp $(which mkvmerge) /artifacts/bin/ \ && cp $(which mkvmerge) /artifacts/bin/ \
&& cp $(which mkvpropedit) /artifacts/bin/ \
&& ldd $(which ffprobe) | awk '{print $3}' | xargs -I '{}' cp -v '{}' /artifacts/lib/ || true \ && ldd $(which ffprobe) | awk '{print $3}' | xargs -I '{}' cp -v '{}' /artifacts/lib/ || true \
&& ldd $(which mkvmerge) | awk '{print $3}' | xargs -I '{}' cp -v '{}' /artifacts/lib/ || true && ldd $(which mkvmerge) | awk '{print $3}' | xargs -I '{}' cp -v '{}' /artifacts/lib/ || true \
&& ldd $(which mkvpropedit) | awk '{print $3}' | xargs -I '{}' cp -v '{}' /artifacts/lib/ || true
# Must be the same base as builder image for shared libraries compatibility # Must be the same base as builder image for shared libraries compatibility
FROM python:3.13.3-slim-bullseye FROM python:3.13.3-slim-bookworm
COPY --from=builder /artifacts/bin/* /usr/local/bin/ COPY --from=builder /artifacts/bin/* /usr/local/bin/
COPY --from=builder /artifacts/lib/* /usr/local/lib/ COPY --from=builder /artifacts/lib/* /usr/local/lib/

View File

@ -81,6 +81,12 @@ class MetadataExtractor:
} }
metadata["subtitle_tracks"].append(track) metadata["subtitle_tracks"].append(track)
elif codec_type == "video":
pass
elif codec_type == "button":
pass
else: else:
self.logger.warning(f"Unknown track codec type '{codec_type}'") self.logger.warning(f"Unknown track codec type '{codec_type}'")

View File

@ -11,26 +11,7 @@ class MetadataWriter:
def __init__(self): def __init__(self):
self.logger: logging.Logger = logging.getLogger("MetadataWriter") self.logger: logging.Logger = logging.getLogger("MetadataWriter")
def apply_metadata(self, metadata: dict, in_path: str, out_path: Optional[str] = None) -> bool: def get_mkvmerge_cmd(self, metadata: dict, in_path: str, out_path: str) -> list[str]:
"""
Writes metadata to a video file using mkvmerge
:param metadata: Metadata information
:param in_path: Path of the input video file
:param out_path: Path of the output video file. If None, ``"_modified"`` is appended to ``in_path`` instead
:return: True if successful, False otherwise
"""
if not os.path.isfile(in_path):
self.logger.error(f"Input file not found: {in_path}")
return False
if out_path is None:
# Create a temporary output file
base_name, ext = os.path.splitext(in_path)
out_path: str = f"{base_name}_modified{ext}"
# Start building the mkvmerge command
cmd: list[str] = [ cmd: list[str] = [
"mkvmerge", "mkvmerge",
"-o", out_path "-o", out_path
@ -66,8 +47,76 @@ class MetadataWriter:
# Add input file # Add input file
cmd.append(in_path) cmd.append(in_path)
return cmd
# Execute the mkvmerge command def get_mkvpropedit_cmd(self, metadata: dict, path: str) -> list[str]:
cmd: list[str] = [
"mkvpropedit",
path
]
# Add global metadata (title)
if "title" in metadata:
cmd.extend(["--edit", "info", "--set", f"title={metadata["title"]}"])
# Process audio + subtitle tracks
tracks: list[dict] = metadata.get("audio_tracks", []) + metadata.get("subtitle_tracks", [])
for track in tracks:
# Use the actual track index from the metadata
track_id = track.get("index", 0)
cmd.extend(["--edit", f"track:{track_id}"])
# Set language
if "language" in track:
cmd.extend(["--set", f"language={track["language"]}"])
# Set title/name
if "name" in track and track["name"]:
cmd.extend(["--set", f"name={track["name"]}"])
# Set disposition flags
flags = track.get("flags", {})
def yes_no(flag: str):
return f"{track_id}:{"yes" if flags.get(flag, False) else "no"}"
cmd.extend(["--set", f"flag-default={int(flags.get("default", False))}"])
cmd.extend(["--set", f"flag-forced={int(flags.get("forced", False))}"])
cmd.extend(["--set", f"flag-original={int(flags.get("original", False))}"])
return cmd
def apply_metadata(self, metadata: dict, in_path: str, out_path: Optional[str] = None) -> bool:
"""
Writes metadata to a video file using mkvmerge or mkvpropedit
:param metadata: Metadata information
:param in_path: Path of the input video file
:param out_path: Path of the output video file. If None, ``"_modified"`` is appended to ``in_path`` instead
:return: True if successful, False otherwise
"""
if not os.path.isfile(in_path):
self.logger.error(f"Input file not found: {in_path}")
return False
if out_path is None:
# Create a temporary output file
base_name, ext = os.path.splitext(in_path)
out_path: str = f"{base_name}_modified{ext}"
# Build the command
overwriting: bool = os.path.abspath(in_path) == os.path.abspath(out_path)
cmd: list[str] = (
self.get_mkvpropedit_cmd(metadata, in_path)
if overwriting else
self.get_mkvmerge_cmd(metadata, in_path, out_path)
)
print(cmd)
# Execute the command
self.logger.debug(f"Writing metadata to {os.path.basename(out_path)}") self.logger.debug(f"Writing metadata to {os.path.basename(out_path)}")
try: try:
@ -80,7 +129,7 @@ class MetadataWriter:
return True return True
except Exception as e: except Exception as e:
self.logger.error(f"Error executing mkvmerge: {str(e)}") self.logger.error(f"Error executing {cmd[0]}: {str(e)}")
return False return False
@staticmethod @staticmethod