From 08649d40d119b1216b7627b514802a3a68a14589 Mon Sep 17 00:00:00 2001 From: tcely Date: Tue, 22 Apr 2025 14:11:45 -0400 Subject: [PATCH 1/4] Add a post processor for after move --- tubesync/sync/youtube.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tubesync/sync/youtube.py b/tubesync/sync/youtube.py index 61f9a489..8b753ff3 100644 --- a/tubesync/sync/youtube.py +++ b/tubesync/sync/youtube.py @@ -381,10 +381,30 @@ def download_media( 'modifychapters+ffmpeg': codec_options, }) + # Provide the user control of 'overwrites' in the post processors. + pp_opts.overwrites = opts.get( + 'overwrites', + ytopts.get( + 'overwrites', + default_opts.overwrites, + ), + ) + # Create the post processors list. # It already included user configured post processors as well. ytopts['postprocessors'] = list(yt_dlp.get_postprocessors(pp_opts)) + if pp_opts.extractaudio: + ytopts['postprocessors'].append(dict( + key='Exec', + when='after_move', + exec_cmd=( + f'test -f {output_file} || ' + 'mv -f {} ' + f'{output_file}' + ), + )) + opts.update(ytopts) with yt_dlp.YoutubeDL(opts) as y: From fcc14bcd368e6fe72e0f4917d4bf86d04ca7cf90 Mon Sep 17 00:00:00 2001 From: tcely Date: Tue, 22 Apr 2025 15:55:22 -0400 Subject: [PATCH 2/4] Use `shell_quote` function and add `final_ext` --- tubesync/sync/youtube.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tubesync/sync/youtube.py b/tubesync/sync/youtube.py index 8b753ff3..1cf658dc 100644 --- a/tubesync/sync/youtube.py +++ b/tubesync/sync/youtube.py @@ -20,7 +20,7 @@ from .utils import mkdir_p import yt_dlp import yt_dlp.patch.check_thumbnails import yt_dlp.patch.fatal_http_errors -from yt_dlp.utils import remove_end, OUTTMPL_TYPES +from yt_dlp.utils import remove_end, shell_quote, OUTTMPL_TYPES _defaults = getattr(settings, 'YOUTUBE_DEFAULTS', {}) @@ -317,6 +317,7 @@ def download_media( ytopts = { 'format': media_format, + 'final_ext': extension, 'merge_output_format': extension, 'outtmpl': os.path.basename(output_file), 'quiet': False if settings.DEBUG else True, @@ -394,14 +395,21 @@ def download_media( # It already included user configured post processors as well. ytopts['postprocessors'] = list(yt_dlp.get_postprocessors(pp_opts)) + final_path = Path(output_file) + try: + final_path = final_path.resolve(strict=True) + except FileNotFoundError: + # This is very likely the common case + final_path = Path(output_file).resolve(strict=False) if pp_opts.extractaudio: + expected_file = shell_quote(str(final_path)) ytopts['postprocessors'].append(dict( key='Exec', when='after_move', exec_cmd=( - f'test -f {output_file} || ' - 'mv -f {} ' - f'{output_file}' + f'test -f {expected_file} || ' + 'mv -T -u -- %(filepath,_filename|)q ' + f'{expected_file}' ), )) From f2201d327ecc7f2ff3e03383edd2f69f80baaf6f Mon Sep 17 00:00:00 2001 From: tcely Date: Tue, 22 Apr 2025 15:58:13 -0400 Subject: [PATCH 3/4] Add comments about the added post processor --- tubesync/sync/youtube.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tubesync/sync/youtube.py b/tubesync/sync/youtube.py index 1cf658dc..d3c8f4a0 100644 --- a/tubesync/sync/youtube.py +++ b/tubesync/sync/youtube.py @@ -395,6 +395,9 @@ def download_media( # It already included user configured post processors as well. ytopts['postprocessors'] = list(yt_dlp.get_postprocessors(pp_opts)) + # The ExtractAudio post processor can change the extension. + # This post processor is to change the final filename back + # to what we are expecting it to be. final_path = Path(output_file) try: final_path = final_path.resolve(strict=True) From 112b477f116b58cddf71ed775f7f06dde19c80f7 Mon Sep 17 00:00:00 2001 From: tcely Date: Tue, 22 Apr 2025 16:39:44 -0400 Subject: [PATCH 4/4] Play nicely with `yt_dlp` options --- tubesync/sync/youtube.py | 42 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tubesync/sync/youtube.py b/tubesync/sync/youtube.py index d3c8f4a0..ffcbb074 100644 --- a/tubesync/sync/youtube.py +++ b/tubesync/sync/youtube.py @@ -314,6 +314,27 @@ def download_media( if extension in audio_exts: pp_opts.extractaudio = True pp_opts.nopostoverwrites = False + # The ExtractAudio post processor can change the extension. + # This post processor is to change the final filename back + # to what we are expecting it to be. + final_path = Path(output_file) + try: + final_path = final_path.resolve(strict=True) + except FileNotFoundError: + # This is very likely the common case + final_path = Path(output_file).resolve(strict=False) + expected_file = shell_quote(str(final_path)) + cmds = pp_opts.exec_cmd.get('after_move', list()) + # It is important that we use a tuple for strings. + # Otherwise, list adds each character instead. + # That last comma is really necessary! + cmds += ( + f'test -f {expected_file} || ' + 'mv -T -u -- %(filepath,_filename|)q ' + f'{expected_file}', + ) + # assignment is the quickest way to cover both 'get' cases + pp_opts.exec_cmd['after_move'] = cmds ytopts = { 'format': media_format, @@ -395,27 +416,6 @@ def download_media( # It already included user configured post processors as well. ytopts['postprocessors'] = list(yt_dlp.get_postprocessors(pp_opts)) - # The ExtractAudio post processor can change the extension. - # This post processor is to change the final filename back - # to what we are expecting it to be. - final_path = Path(output_file) - try: - final_path = final_path.resolve(strict=True) - except FileNotFoundError: - # This is very likely the common case - final_path = Path(output_file).resolve(strict=False) - if pp_opts.extractaudio: - expected_file = shell_quote(str(final_path)) - ytopts['postprocessors'].append(dict( - key='Exec', - when='after_move', - exec_cmd=( - f'test -f {expected_file} || ' - 'mv -T -u -- %(filepath,_filename|)q ' - f'{expected_file}' - ), - )) - opts.update(ytopts) with yt_dlp.YoutubeDL(opts) as y: