From 4f026fafbc6cc4eac10a5f89b9375b44d64083c9 Mon Sep 17 00:00:00 2001 From: Philipp Hagemeister Date: Mon, 15 Dec 2014 01:06:25 +0100 Subject: [YoutubeDL] Make postprocessors declarative Instead of having to configure PPs in code, this allows us and embedding programs not to worry about imports or finer details, similarly to how we handle IEs. --- youtube_dl/YoutubeDL.py | 19 +++++++++- youtube_dl/__init__.py | 71 +++++++++++++++++++----------------- youtube_dl/postprocessor/__init__.py | 9 ++++- youtube_dl/postprocessor/ffmpeg.py | 4 +- 4 files changed, 65 insertions(+), 38 deletions(-) (limited to 'youtube_dl') diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 578c8daf2..6acfd8cf9 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -27,6 +27,7 @@ from .compat import ( compat_cookiejar, compat_expanduser, compat_http_client, + compat_kwargs, compat_str, compat_urllib_error, compat_urllib_request, @@ -67,7 +68,11 @@ from .cache import Cache from .extractor import get_info_extractor, gen_extractors from .downloader import get_suitable_downloader from .downloader.rtmp import rtmpdump_version -from .postprocessor import FFmpegMergerPP, FFmpegPostProcessor +from .postprocessor import ( + FFmpegMergerPP, + FFmpegPostProcessor, + get_postprocessor, +) from .version import __version__ @@ -176,6 +181,11 @@ class YoutubeDL(object): extract_flat: Do not resolve URLs, return the immediate result. Pass in 'in_playlist' to only show this behavior for playlist items. + postprocessors: A list of dictionaries, each with an entry + key: The name of the postprocessor. See + youtube_dl/postprocessor/__init__.py for a list. + as well as any further keyword arguments for the + postprocessor. The following parameters are not used by YoutubeDL itself, they are used by the FileDownloader: @@ -256,6 +266,13 @@ class YoutubeDL(object): self.print_debug_header() self.add_default_info_extractors() + for pp_def_raw in self.params.get('postprocessors', []): + pp_class = get_postprocessor(pp_def_raw['key']) + pp_def = dict(pp_def_raw) + del pp_def['key'] + pp = pp_class(self, **compat_kwargs(pp_def)) + self.add_post_processor(pp) + def warn_if_short_id(self, argv): # short YouTube ID starting with dash? idxs = [ diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 70c4f25b1..e79320323 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -40,16 +40,6 @@ from .downloader import ( ) from .extractor import gen_extractors from .YoutubeDL import YoutubeDL -from .postprocessor import ( - AtomicParsleyPP, - FFmpegAudioFixPP, - FFmpegMetadataPP, - FFmpegVideoConvertor, - FFmpegExtractAudioPP, - FFmpegEmbedSubtitlePP, - XAttrMetadataPP, - ExecAfterDownloadPP, -) def _real_main(argv=None): @@ -212,6 +202,43 @@ def _real_main(argv=None): any_printing = opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat or opts.getduration or opts.dumpjson or opts.dump_single_json download_archive_fn = compat_expanduser(opts.download_archive) if opts.download_archive is not None else opts.download_archive + # PostProcessors + postprocessors = [] + # Add the metadata pp first, the other pps will copy it + if opts.addmetadata: + postprocessors.append({'key': 'FFmpegMetadata'}) + if opts.extractaudio: + postprocessors.append({ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': opts.audioformat, + 'preferredquality': opts.audioquality, + 'nopostoverwrites': opts.nopostoverwrites, + }) + if opts.recodevideo: + postprocessors.append({ + 'key': 'FFmpegVideoConvertor', + 'preferedformat': opts.recodevideo, + }) + if opts.embedsubtitles: + postprocessors.append({ + 'key': 'FFmpegEmbedSubtitle', + 'subtitlesformat': opts.subtitlesformat, + }) + if opts.xattrs: + postprocessors.append({'key': 'XAttrMetadata'}) + if opts.embedthumbnail: + if not opts.addmetadata: + postprocessors.append({'key': 'FFmpegAudioFix'}) + postprocessors.append({'key': 'AtomicParsley'}) + # Please keep ExecAfterDownload towards the bottom as it allows the user to modify the final file in any way. + # So if the user is able to remove the file before your postprocessor runs it might cause a few problems. + if opts.exec_cmd: + postprocessors.append({ + 'key': 'ExecAfterDownload', + 'verboseOutput': opts.verbose, + 'exec_cmd': opts.exec_cmd, + }) + ydl_opts = { 'usenetrc': opts.usenetrc, 'username': opts.username, @@ -297,32 +324,10 @@ def _real_main(argv=None): 'encoding': opts.encoding, 'exec_cmd': opts.exec_cmd, 'extract_flat': opts.extract_flat, + 'postprocessors': postprocessors, } with YoutubeDL(ydl_opts) as ydl: - # PostProcessors - # Add the metadata pp first, the other pps will copy it - if opts.addmetadata: - ydl.add_post_processor(FFmpegMetadataPP()) - if opts.extractaudio: - ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites)) - if opts.recodevideo: - ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo)) - if opts.embedsubtitles: - ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat)) - if opts.xattrs: - ydl.add_post_processor(XAttrMetadataPP()) - if opts.embedthumbnail: - if not opts.addmetadata: - ydl.add_post_processor(FFmpegAudioFixPP()) - ydl.add_post_processor(AtomicParsleyPP()) - - # Please keep ExecAfterDownload towards the bottom as it allows the user to modify the final file in any way. - # So if the user is able to remove the file before your postprocessor runs it might cause a few problems. - if opts.exec_cmd: - ydl.add_post_processor(ExecAfterDownloadPP( - verboseOutput=opts.verbose, exec_cmd=opts.exec_cmd)) - # Update version if opts.update_self: update_self(ydl.to_screen, opts.verbose) diff --git a/youtube_dl/postprocessor/__init__.py b/youtube_dl/postprocessor/__init__.py index fb367ebe4..7f505b58e 100644 --- a/youtube_dl/postprocessor/__init__.py +++ b/youtube_dl/postprocessor/__init__.py @@ -8,11 +8,16 @@ from .ffmpeg import ( FFmpegExtractAudioPP, FFmpegMergerPP, FFmpegMetadataPP, - FFmpegVideoConvertor, + FFmpegVideoConvertorPP, ) from .xattrpp import XAttrMetadataPP from .execafterdownload import ExecAfterDownloadPP + +def get_postprocessor(key): + return globals()[key + 'PP'] + + __all__ = [ 'AtomicParsleyPP', 'ExecAfterDownloadPP', @@ -22,6 +27,6 @@ __all__ = [ 'FFmpegMergerPP', 'FFmpegMetadataPP', 'FFmpegPostProcessor', - 'FFmpegVideoConvertor', + 'FFmpegVideoConvertorPP', 'XAttrMetadataPP', ] diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index 965ded4c1..048525efc 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -236,9 +236,9 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor): return self._nopostoverwrites, information -class FFmpegVideoConvertor(FFmpegPostProcessor): +class FFmpegVideoConvertorPP(FFmpegPostProcessor): def __init__(self, downloader=None, preferedformat=None): - super(FFmpegVideoConvertor, self).__init__(downloader) + super(FFmpegVideoConvertorPP, self).__init__(downloader) self._preferedformat = preferedformat def run(self, information): -- cgit v1.2.3