cmd/ffstreams.py

157 lines
4.3 KiB
Python
Raw Normal View History

2020-01-16 05:26:16 +00:00
import argparse
import re
import subprocess
import sys
from voussoirkit import pathclass
2020-01-16 06:33:44 +00:00
from voussoirkit import winglob
2020-01-16 05:26:16 +00:00
from voussoirkit import winwhich
2021-01-23 04:59:12 +00:00
from voussoirkit import vlogging
log = vlogging.getLogger(__name__, 'ffstreams')
2020-01-16 05:26:16 +00:00
AUDIO_EXTENSIONS = {
2020-01-16 05:26:16 +00:00
'aac': 'm4a',
2020-02-19 23:15:25 +00:00
'ac3': 'ac3',
'flac': 'flac',
2020-01-16 05:26:16 +00:00
'mp3': 'mp3',
'opus': 'opus',
2020-10-02 01:10:34 +00:00
'vorbis': 'ogg',
'*': 'mka',
2020-01-16 05:26:16 +00:00
}
SUBTITLE_EXTENSIONS = {
'ass': 'ass',
'subrip': 'srt',
'*': 'mks',
}
2020-01-16 05:26:16 +00:00
FFMPEG = winwhich.which('ffmpeg')
def make_maps(input_file, prefix, search_pattern, extension_map, moveto=None):
input_file = pathclass.Path(input_file)
2020-01-22 23:49:06 +00:00
2020-01-16 05:26:16 +00:00
if moveto is not None:
moveto = pathclass.Path(moveto)
2020-01-22 23:49:06 +00:00
command = [FFMPEG, '-i', input_file.absolute_path]
2020-01-16 05:26:16 +00:00
try:
output = subprocess.check_output(command, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as exc:
output = exc.output
output = output.decode('utf-8')
2020-01-22 23:49:06 +00:00
2020-01-16 05:26:16 +00:00
maps = []
for line in output.splitlines():
match = re.search(search_pattern, line)
2020-01-16 05:26:16 +00:00
if match is None:
continue
2020-01-22 23:49:06 +00:00
(stream_index, language, codec) = match.groups()
if language is None:
language = ''
else:
language = language.strip('()')
language = '.' + language
extension = extension_map.get(codec, extension_map['*'])
output_filename = input_file.replace_extension('')
output_filename = output_filename.add_extension(f'{prefix}{stream_index}{language}.{extension}')
2021-01-23 04:59:12 +00:00
log.debug(f'{stream_index}, codec={codec}, ext=.{extension}')
2020-01-22 23:49:06 +00:00
2020-01-16 05:26:16 +00:00
if moveto:
output_filename = moveto.with_child(output_filename.basename)
args = ['-map', f'0:{stream_index}', '-c', 'copy']
if extension == 'mks':
args.extend(['-f', 'matroska'])
args.append(output_filename.absolute_path)
maps.extend(args)
return maps
2020-01-16 05:26:16 +00:00
def video_maps(input_file, moveto=None):
return make_maps(
input_file,
prefix='v',
search_pattern=r'Stream #0:(\d+)(\(\w+\))?[^\s]*: Video: (\w+)',
extension_map=AUDIO_EXTENSIONS,
moveto=moveto,
)
def audio_maps(input_file, moveto=None):
return make_maps(
input_file,
prefix='a',
search_pattern=r'Stream #0:(\d+)(\(\w+\))?[^\s]*: Audio: (\w+)',
extension_map=AUDIO_EXTENSIONS,
moveto=moveto,
)
def subtitle_maps(input_file, moveto=None):
return make_maps(
input_file,
prefix='s',
search_pattern=r'Stream #0:(\d+)(\(\w+\))?[^\s]*: Subtitle: (\w+)',
extension_map=SUBTITLE_EXTENSIONS,
moveto=moveto,
)
2020-01-16 05:26:16 +00:00
2021-08-10 00:37:19 +00:00
def ffstreams(
input_file,
do_videos=False,
do_audios=False,
do_subtitles=False,
dry=False,
moveto=None,
):
maps = []
if do_videos:
maps.extend(video_maps(input_file, moveto=moveto))
if do_audios:
maps.extend(audio_maps(input_file, moveto=moveto))
if do_subtitles:
maps.extend(subtitle_maps(input_file, moveto=moveto))
if not maps:
return
command = [FFMPEG, '-i', input_file.absolute_path, *maps]
2021-01-23 04:59:12 +00:00
log.info(command)
if not dry:
subprocess.run(command, stderr=subprocess.STDOUT)
2020-01-22 23:49:06 +00:00
def ffstreams_argparse(args):
2020-01-16 05:26:16 +00:00
for pattern in args.input_filename:
2020-01-16 06:33:44 +00:00
for input_filename in winglob.glob(pattern):
input_file = pathclass.Path(input_filename)
ffstreams(
input_file,
do_videos=args.videos,
do_audios=args.audios,
do_subtitles=args.subtitles,
dry=args.dry,
moveto=args.moveto,
)
2020-01-16 05:26:16 +00:00
@vlogging.main_decorator
2020-01-16 05:26:16 +00:00
def main(argv):
2021-01-23 04:59:12 +00:00
parser = argparse.ArgumentParser(description=__doc__)
2020-01-16 05:26:16 +00:00
parser.add_argument('input_filename', nargs='+')
parser.add_argument('--moveto', default=None)
parser.add_argument('--video', '--videos', dest='videos', action='store_true')
parser.add_argument('--audio', '--audios', dest='audios', action='store_true')
2020-01-27 00:24:55 +00:00
parser.add_argument('--subtitles', '--subs', dest='subtitles', action='store_true')
parser.add_argument('--dry', dest='dry', action='store_true')
2020-01-22 23:49:06 +00:00
parser.set_defaults(func=ffstreams_argparse)
2020-01-16 05:26:16 +00:00
args = parser.parse_args(argv)
return args.func(args)
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))