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()