diff --git a/README.md b/README.md index 45326c69ec..c928b7f005 100644 --- a/README.md +++ b/README.md @@ -396,6 +396,10 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default 5) + --remux-video FORMAT Remux the video to another container format + if necessary (currently supported: mp4|mkv, + target container format must support video + / audio encoding, remuxing may fail) --recode-video FORMAT Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm|mkv|avi) diff --git a/devscripts/fish-completion.py b/devscripts/fish-completion.py index 51d19dd33d..f69c9b2323 100755 --- a/devscripts/fish-completion.py +++ b/devscripts/fish-completion.py @@ -14,6 +14,7 @@ FISH_COMPLETION_FILE = 'youtube-dl.fish' FISH_COMPLETION_TEMPLATE = 'devscripts/fish-completion.in' EXTRA_ARGS = { + 'remux-video': ['--arguments', 'mp4 mkv', '--exclusive'], 'recode-video': ['--arguments', 'mp4 flv ogg webm mkv', '--exclusive'], # Options that need a file parameter diff --git a/devscripts/zsh-completion.in b/devscripts/zsh-completion.in index b394a1ae74..2658b3119d 100644 --- a/devscripts/zsh-completion.in +++ b/devscripts/zsh-completion.in @@ -16,6 +16,8 @@ __youtube_dl() { _path_files elif [[ ${prev} =~ ${diropts} ]]; then _path_files -/ + elif [[ ${prev} == "--remux-video" ]]; then + _arguments '*: :(mp4 mkv)' elif [[ ${prev} == "--recode-video" ]]; then _arguments '*: :(mp4 flv ogg webm mkv)' else diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 9a659fc654..838df9e560 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -209,6 +209,9 @@ def _real_main(argv=None): opts.audioquality = opts.audioquality.strip('k').strip('K') if not opts.audioquality.isdigit(): parser.error('invalid audio quality specified') + if opts.remuxvideo is not None: + if opts.remuxvideo not in ['mp4', 'mkv']: + parser.error('invalid video container format specified') if opts.recodevideo is not None: if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg', 'mkv', 'avi']: parser.error('invalid video recode format specified') @@ -261,6 +264,11 @@ def _real_main(argv=None): 'preferredquality': opts.audioquality, 'nopostoverwrites': opts.nopostoverwrites, }) + if opts.remuxvideo: + postprocessors.append({ + 'key': 'FFmpegVideoRemuxer', + 'preferedformat': opts.remuxvideo, + }) if opts.recodevideo: postprocessors.append({ 'key': 'FFmpegVideoConvertor', diff --git a/youtube_dl/options.py b/youtube_dl/options.py index 6d5ac62b3b..3b4125f2f4 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -790,6 +790,10 @@ def parseOpts(overrideArguments=None): '--audio-quality', metavar='QUALITY', dest='audioquality', default='5', help='Specify ffmpeg/avconv audio quality, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default %default)') + postproc.add_option( + '--remux-video', + metavar='FORMAT', dest='remuxvideo', default=None, + help='Remux the video to another container format if necessary (currently supported: mp4|mkv, target container format must support video / audio encoding, remuxing may fail)') postproc.add_option( '--recode-video', metavar='FORMAT', dest='recodevideo', default=None, diff --git a/youtube_dl/postprocessor/__init__.py b/youtube_dl/postprocessor/__init__.py index 3ea5183999..2c47028237 100644 --- a/youtube_dl/postprocessor/__init__.py +++ b/youtube_dl/postprocessor/__init__.py @@ -11,6 +11,7 @@ from .ffmpeg import ( FFmpegMergerPP, FFmpegMetadataPP, FFmpegVideoConvertorPP, + FFmpegVideoRemuxerPP, FFmpegSubtitlesConvertorPP, ) from .xattrpp import XAttrMetadataPP @@ -35,6 +36,7 @@ __all__ = [ 'FFmpegPostProcessor', 'FFmpegSubtitlesConvertorPP', 'FFmpegVideoConvertorPP', + 'FFmpegVideoRemuxerPP', 'MetadataFromTitlePP', 'XAttrMetadataPP', ] diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index fd3f921a8a..ab9721305d 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -349,6 +349,27 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor): return [path], information +class FFmpegVideoRemuxerPP(FFmpegPostProcessor): + def __init__(self, downloader=None, preferedformat=None): + super(FFmpegVideoRemuxerPP, self).__init__(downloader) + self._preferedformat = preferedformat + + def run(self, information): + path = information['filepath'] + if information['ext'] == self._preferedformat: + self._downloader.to_screen('[ffmpeg] Not remuxing video file %s - already is in target format %s' % (path, self._preferedformat)) + return [], information + options = ['-c', 'copy'] + prefix, sep, ext = path.rpartition('.') + outpath = prefix + sep + self._preferedformat + self._downloader.to_screen('[' + 'ffmpeg' + '] Remuxing video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) + outpath) + self.run_ffmpeg(path, outpath, options) + information['filepath'] = outpath + information['format'] = self._preferedformat + information['ext'] = self._preferedformat + return [path], information + + class FFmpegVideoConvertorPP(FFmpegPostProcessor): def __init__(self, downloader=None, preferedformat=None): super(FFmpegVideoConvertorPP, self).__init__(downloader)