From 7a0bcf6723a3a9a06931d023e05ee90a62c93c23 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Markus=20B=C3=B6hning?=
<1497707+bohning@users.noreply.github.com>
Date: Sat, 21 Sep 2024 19:36:56 +0200
Subject: [PATCH] Write tags (and artwork, if selected) also to the video file
(mp4 only).
---
CHANGELOG.md | 1 +
src/usdb_syncer/download_options.py | 2 +
src/usdb_syncer/gui/forms/SettingsDialog.ui | 44 ++++++++++++++-------
src/usdb_syncer/gui/settings_dialog.py | 2 +
src/usdb_syncer/settings.py | 9 +++++
src/usdb_syncer/song_loader.py | 25 +++++++++++-
6 files changed, 66 insertions(+), 17 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3c0356b..c1607b46 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
- Comments can now be posted on songs. Each comment includes a message and a rating.
Ratings can be negative, neutral, or positive, with neutral being the default.
- The VP9 codec can be excluded for mp4 video containers (see _Settings_).
+- Tags (and artwork, if selected) are now also written to the video file (mp4 only).
## Developer notes
diff --git a/src/usdb_syncer/download_options.py b/src/usdb_syncer/download_options.py
index b4c0e7f9..2beeb5a4 100644
--- a/src/usdb_syncer/download_options.py
+++ b/src/usdb_syncer/download_options.py
@@ -35,6 +35,7 @@ class VideoOptions:
reencode_format: settings.VideoCodec | None
max_resolution: settings.VideoResolution
max_fps: settings.VideoFps
+ embed_artwork: bool
def ytdl_format(self) -> str:
fmt = self.format.ytdl_format()
@@ -116,6 +117,7 @@ def _video_options() -> VideoOptions | None:
),
max_resolution=settings.get_video_resolution(),
max_fps=settings.get_video_fps(),
+ embed_artwork=settings.get_video_embed_artwork(),
)
diff --git a/src/usdb_syncer/gui/forms/SettingsDialog.ui b/src/usdb_syncer/gui/forms/SettingsDialog.ui
index 92953c41..9bc59f18 100644
--- a/src/usdb_syncer/gui/forms/SettingsDialog.ui
+++ b/src/usdb_syncer/gui/forms/SettingsDialog.ui
@@ -24,7 +24,20 @@
- -
+
-
+
+
+ Qt::Orientation::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
0
@@ -357,7 +370,7 @@
- -
+
-
@@ -401,6 +414,20 @@
+ -
+
+
+ Embed artwork:
+
+
+
+ -
+
+
+
+
+
+
@@ -505,19 +532,6 @@
- -
-
-
- Qt::Orientation::Vertical
-
-
-
- 20
- 40
-
-
-
-
diff --git a/src/usdb_syncer/gui/settings_dialog.py b/src/usdb_syncer/gui/settings_dialog.py
index 12755097..ff2a995f 100644
--- a/src/usdb_syncer/gui/settings_dialog.py
+++ b/src/usdb_syncer/gui/settings_dialog.py
@@ -97,6 +97,7 @@ def _load_settings(self) -> None:
self.comboBox_fps.setCurrentIndex(
self.comboBox_fps.findData(settings.get_video_fps())
)
+ self.checkBox_video_embed_artwork.setChecked(settings.get_video_embed_artwork())
self.groupBox_background.setChecked(settings.get_background())
self.checkBox_background_always.setChecked(settings.get_background_always())
@@ -150,6 +151,7 @@ def _save_settings(self) -> bool:
settings.set_video_reencode(self.groupBox_reencode_video.isChecked())
settings.set_video_resolution(self.comboBox_videoresolution.currentData())
settings.set_video_fps(self.comboBox_fps.currentData())
+ settings.set_video_embed_artwork(self.checkBox_video_embed_artwork.isChecked())
settings.set_background(self.groupBox_background.isChecked())
settings.set_background_always(self.checkBox_background_always.isChecked())
if self._path_template:
diff --git a/src/usdb_syncer/settings.py b/src/usdb_syncer/settings.py
index e8b51cfb..d813911f 100644
--- a/src/usdb_syncer/settings.py
+++ b/src/usdb_syncer/settings.py
@@ -83,6 +83,7 @@ class SettingKey(Enum):
VIDEO_FORMAT_NEW = "downloads/video_format_new"
VIDEO_RESOLUTION_MAX = "downloads/video_resolution_max"
VIDEO_FPS_MAX = "downloads/video_fps_max"
+ VIDEO_EMBED_ARTWORK = "downloads/video_embed_artwork"
COVER = "downloads/cover"
COVER_MAX_SIZE = "downloads/cover_max_size"
BACKGROUND = "downloads/background"
@@ -618,6 +619,14 @@ def set_video_fps(value: VideoFps) -> None:
set_setting(SettingKey.VIDEO_FPS_MAX, value)
+def get_video_embed_artwork() -> bool:
+ return get_setting(SettingKey.VIDEO_EMBED_ARTWORK, False)
+
+
+def set_video_embed_artwork(value: bool) -> None:
+ set_setting(SettingKey.VIDEO_EMBED_ARTWORK, value)
+
+
def get_background() -> bool:
return get_setting(SettingKey.BACKGROUND, True)
diff --git a/src/usdb_syncer/song_loader.py b/src/usdb_syncer/song_loader.py
index 4de3993f..1ab1687c 100644
--- a/src/usdb_syncer/song_loader.py
+++ b/src/usdb_syncer/song_loader.py
@@ -383,6 +383,7 @@ def _run_inner(self) -> UsdbSong:
_maybe_download_cover,
_maybe_download_background,
_maybe_write_audio_tags,
+ _maybe_write_video_tags,
):
self._check_flags()
job(ctx)
@@ -536,7 +537,7 @@ def _maybe_write_audio_tags(ctx: _Context) -> None:
try:
match path.suffix:
case ".m4a":
- _write_m4a_tags(path, resource, ctx, options.embed_artwork)
+ _write_m4a_mp4_tags(path, resource, ctx, options.embed_artwork)
case ".mp3":
_write_mp3_tags(path, resource, ctx, options.embed_artwork)
case ".ogg":
@@ -560,7 +561,27 @@ def _maybe_write_audio_tags(ctx: _Context) -> None:
ctx.logger.debug(f"Audio tags written to file '{path}'.")
-def _write_m4a_tags(
+def _maybe_write_video_tags(ctx: _Context) -> None:
+ if not (options := ctx.options.video_options):
+ return
+ if not (path_resource := ctx.out.video.path_and_resource(ctx.locations, temp=True)):
+ return
+ path, resource = path_resource
+ try:
+ match path.suffix:
+ case ".mp4":
+ _write_m4a_mp4_tags(path, resource, ctx, options.embed_artwork)
+ case other:
+ ctx.logger.debug(f"Video tags not supported for suffix '{other}'.")
+ return
+ except Exception: # pylint: disable=broad-exception-caught
+ ctx.logger.debug(traceback.format_exc())
+ ctx.logger.error(f"Failed to write video tags to file '{path}'!")
+ else:
+ ctx.logger.debug(f"Video tags written to file '{path}'.")
+
+
+def _write_m4a_mp4_tags(
path: Path, resource: str, ctx: _Context, embed_artwork: bool
) -> None:
tags = mutagen.mp4.MP4Tags()