diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index dad44435f..57dd2b524 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -250,11 +250,13 @@ class YoutubeDL(object): postprocessor. progress_hooks: A list of functions that get called on download progress, with a dictionary with the entries - * status: One of "downloading", "error", or "finished". + * status: One of "downloading", "error", "finished", + or "postprocessed". Check this first and ignore unknown values. - If status is one of "downloading", or "finished", the - following properties may also be present: + If status is one of "downloading", "finished", or + "postprocessed", the following properties may also be + present: * filename: The final filename (always present) * tmpfilename: The filename we're currently writing to * downloaded_bytes: Bytes on disk @@ -269,6 +271,9 @@ class YoutubeDL(object): downloaded video fragment. * fragment_count: The number of fragments (= individual files that will be merged) + * postprocessor: The specific postprocessor that ran. + See youtube_dl/postprocessor/__init__.py + for a list of possibilities. Progress hooks are guaranteed to be called at least once (with status "finished") if the download is successful. @@ -2292,6 +2297,8 @@ class YoutubeDL(object): pps_chain.extend(ie_info['__postprocessors']) pps_chain.extend(self._pps) for pp in pps_chain: + for ph in self._progress_hooks: + pp.add_progress_hook(ph) files_to_delete = [] try: files_to_delete, info = pp.run(info) diff --git a/youtube_dl/postprocessor/common.py b/youtube_dl/postprocessor/common.py index 599dd1df2..22a27127f 100644 --- a/youtube_dl/postprocessor/common.py +++ b/youtube_dl/postprocessor/common.py @@ -33,6 +33,7 @@ class PostProcessor(object): def __init__(self, downloader=None): self._downloader = downloader + self._progress_hooks = [] def set_downloader(self, downloader): """Sets the downloader for this PP.""" @@ -64,6 +65,15 @@ class PostProcessor(object): def _configuration_args(self, default=[]): return cli_configuration_args(self._downloader.params, 'postprocessor_args', default) + def _hook_progress(self, status): + for ph in self._progress_hooks: + ph(status) + + def add_progress_hook(self, ph): + # See YoutubeDl.py (search for progress_hooks) for a description of + # this interface + self._progress_hooks.append(ph) + class AudioConversionError(PostProcessingError): pass diff --git a/youtube_dl/postprocessor/embedthumbnail.py b/youtube_dl/postprocessor/embedthumbnail.py index b6c60e127..519d98235 100644 --- a/youtube_dl/postprocessor/embedthumbnail.py +++ b/youtube_dl/postprocessor/embedthumbnail.py @@ -130,4 +130,12 @@ class EmbedThumbnailPP(FFmpegPostProcessor): else: raise EmbedThumbnailPPError('Only mp3 and m4a/mp4 are supported for thumbnail embedding for now.') + fsize = os.path.getsize(encodeFilename(filename)) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': encodeFilename(filename), + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) + return [], info diff --git a/youtube_dl/postprocessor/execafterdownload.py b/youtube_dl/postprocessor/execafterdownload.py index 64dabe790..a546d51c9 100644 --- a/youtube_dl/postprocessor/execafterdownload.py +++ b/youtube_dl/postprocessor/execafterdownload.py @@ -27,5 +27,9 @@ class ExecAfterDownloadPP(PostProcessor): if retCode != 0: raise PostProcessingError( 'Command returned error code %d' % retCode) + self._hook_progress({ + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return [], information diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index 214825aa9..e343d48c1 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -342,6 +342,14 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor): new_path, time.time(), information['filetime'], errnote='Cannot update utime of audio file') + fsize = os.path.getsize(new_path) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': new_path, + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) + return [path], information @@ -365,6 +373,13 @@ class FFmpegVideoConvertorPP(FFmpegPostProcessor): information['filepath'] = outpath information['format'] = self._preferedformat information['ext'] = self._preferedformat + fsize = os.path.getsize(outpath) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': outpath, + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return [path], information @@ -422,6 +437,13 @@ class FFmpegEmbedSubtitlePP(FFmpegPostProcessor): self.run_ffmpeg_multiple_files(input_files, temp_filename, opts) os.remove(encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) + fsize = os.path.getsize(encodeFilename(filename)) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': encodeFilename(filename), + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return sub_filenames, information @@ -507,6 +529,13 @@ class FFmpegMetadataPP(FFmpegPostProcessor): os.remove(metadata_filename) os.remove(encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) + fsize = os.path.getsize(encodeFilename(filename)) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': encodeFilename(filename), + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return [], info @@ -518,6 +547,13 @@ class FFmpegMergerPP(FFmpegPostProcessor): self._downloader.to_screen('[ffmpeg] Merging formats into "%s"' % filename) self.run_ffmpeg_multiple_files(info['__files_to_merge'], temp_filename, args) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) + fsize = os.path.getsize(encodeFilename(filename)) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': encodeFilename(filename), + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return info['__files_to_merge'], info def can_merge(self): @@ -553,6 +589,13 @@ class FFmpegFixupStretchedPP(FFmpegPostProcessor): os.remove(encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) + fsize = os.path.getsize(encodeFilename(filename)) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': encodeFilename(filename), + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return [], info @@ -571,6 +614,13 @@ class FFmpegFixupM4aPP(FFmpegPostProcessor): os.remove(encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) + fsize = os.path.getsize(encodeFilename(filename)) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': encodeFilename(filename), + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return [], info @@ -587,6 +637,14 @@ class FFmpegFixupM3u8PP(FFmpegPostProcessor): os.remove(encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) + + fsize = os.path.getsize(encodeFilename(filename)) + self._hook_progress({ + 'total_bytes': fsize, + 'filename': encodeFilename(filename), + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return [], info @@ -650,4 +708,9 @@ class FFmpegSubtitlesConvertorPP(FFmpegPostProcessor): 'data': f.read(), } + self._hook_progress({ + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) + return sub_filenames, info diff --git a/youtube_dl/postprocessor/metadatafromtitle.py b/youtube_dl/postprocessor/metadatafromtitle.py index 6cd5bb70f..f0d56666b 100644 --- a/youtube_dl/postprocessor/metadatafromtitle.py +++ b/youtube_dl/postprocessor/metadatafromtitle.py @@ -46,5 +46,9 @@ class MetadataFromTitlePP(PostProcessor): self._downloader.to_screen( '[fromtitle] parsed %s: %s' % (attribute, value if value is not None else 'NA')) + self._hook_progress({ + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) return [], info diff --git a/youtube_dl/postprocessor/xattrpp.py b/youtube_dl/postprocessor/xattrpp.py index 814dabecf..8ffdd1661 100644 --- a/youtube_dl/postprocessor/xattrpp.py +++ b/youtube_dl/postprocessor/xattrpp.py @@ -55,6 +55,11 @@ class XAttrMetadataPP(PostProcessor): write_xattr(filename, xattrname, byte_value) num_written += 1 + self._hook_progress({ + 'status': 'postprocessed', + 'postprocessor': self.__class__.__name__ + }) + return [], info except XAttrUnavailableError as e: