diff --git a/README.md b/README.md index d11c3d90b..2a51eb66c 100644 --- a/README.md +++ b/README.md @@ -405,6 +405,10 @@ Then simply type this 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 a27ef44f8..be65f4340 100755 --- a/devscripts/fish-completion.py +++ b/devscripts/fish-completion.py @@ -14,6 +14,7 @@ FISH_COMPLETION_FILE = 'youtube-dlc.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 bb021862f..2317f41df 100644 --- a/devscripts/zsh-completion.in +++ b/devscripts/zsh-completion.in @@ -16,6 +16,8 @@ __youtube_dlc() { _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_dlc/__init__.py b/youtube_dlc/__init__.py index fc11642b9..13b658eff 100644 --- a/youtube_dlc/__init__.py +++ b/youtube_dlc/__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_dlc/options.py b/youtube_dlc/options.py index 2cc5eee74..ea372dd6d 100644 --- a/youtube_dlc/options.py +++ b/youtube_dlc/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_dlc/postprocessor/__init__.py b/youtube_dlc/postprocessor/__init__.py index 3ea518399..2c4702823 100644 --- a/youtube_dlc/postprocessor/__init__.py +++ b/youtube_dlc/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_dlc/postprocessor/ffmpeg.py b/youtube_dlc/postprocessor/ffmpeg.py index 5d66a69a6..5e85f4eeb 100644 --- a/youtube_dlc/postprocessor/ffmpeg.py +++ b/youtube_dlc/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)