- Command.dump can now output commands pastable into sh
- Command no longer leaves zombies everywhere and exports process return codes - fixed --audio copy/--video copy - it actually copies streams now - files with a colon in their names can be processed now - files with multiple audio streams are sort of handled - halt when ffmpeg returns an error
This commit is contained in:
parent
93777058bd
commit
3fff52fedc
2 changed files with 36 additions and 22 deletions
8
aux.py
8
aux.py
|
@ -63,7 +63,7 @@ class Command:
|
||||||
self.__dict__["fifo"] = None
|
self.__dict__["fifo"] = None
|
||||||
self.__dict__["terminated"] = False
|
self.__dict__["terminated"] = False
|
||||||
|
|
||||||
def dump(self, sequence=None):
|
def dump(self, sequence=None, pretty=False):
|
||||||
out = []
|
out = []
|
||||||
|
|
||||||
if not sequence:
|
if not sequence:
|
||||||
|
@ -77,7 +77,10 @@ class Command:
|
||||||
elif item is not None:
|
elif item is not None:
|
||||||
out.append(str(item))
|
out.append(str(item))
|
||||||
|
|
||||||
return out
|
if pretty:
|
||||||
|
return [('"%s"' %(a) if ' ' in a else a) for a in out]
|
||||||
|
else:
|
||||||
|
return out
|
||||||
|
|
||||||
def run(self, stderr=False):
|
def run(self, stderr=False):
|
||||||
"""
|
"""
|
||||||
|
@ -118,6 +121,7 @@ class Command:
|
||||||
|
|
||||||
if None in buffer:
|
if None in buffer:
|
||||||
self.__dict__["terminated"] = True
|
self.__dict__["terminated"] = True
|
||||||
|
self.__dict__["returncode"] = self.process.poll()
|
||||||
|
|
||||||
if len(buffer) == 1:
|
if len(buffer) == 1:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -31,6 +31,8 @@ command.sub_theora = Command('-codec:v', 'libtheora', '-qscale:v', 'QUALITY', 'V
|
||||||
command.sub_x264 = Command('-codec:v', 'libx264', '-preset', 'slow', '-crf', 'QUALITY', '-profile:v', 'high', '-level', '4.2', 'VFILTER')
|
command.sub_x264 = Command('-codec:v', 'libx264', '-preset', 'slow', '-crf', 'QUALITY', '-profile:v', 'high', '-level', '4.2', 'VFILTER')
|
||||||
command.sub_normalize = Command('-filter:a', 'VOLUME')
|
command.sub_normalize = Command('-filter:a', 'VOLUME')
|
||||||
command.sub_vfilter = Command('-filter:v', 'ARGS')
|
command.sub_vfilter = Command('-filter:v', 'ARGS')
|
||||||
|
command.sub_copy_audio = Command('-codec:a', 'copy')
|
||||||
|
command.sub_copy_video = Command('-codec:v', 'copy')
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ if __name__ == '__main__':
|
||||||
main.add_option('video-quality', 'float', 22, 'Video encoding quality. Use 0-10 for Theora (0 being the lowest, 5-7 is generally watchable) and 0-51 for x264 (0 being lossless, 18-28 is reasonable).', short_name='Q')
|
main.add_option('video-quality', 'float', 22, 'Video encoding quality. Use 0-10 for Theora (0 being the lowest, 5-7 is generally watchable) and 0-51 for x264 (0 being lossless, 18-28 is reasonable).', short_name='Q')
|
||||||
main.add_option('normalize', 'bool', True, 'Normalize the audio track.', short_name='n')
|
main.add_option('normalize', 'bool', True, 'Normalize the audio track.', short_name='n')
|
||||||
main.add_option('normalize-target', 'float', -20.0, 'Target mean volume to target.', short_name='N')
|
main.add_option('normalize-target', 'float', -20.0, 'Target mean volume to target.', short_name='N')
|
||||||
main.add_option('ffmpeg-vfilter', 'str', '', 'Raw ffmpeg -filter:v options, e.g. "scale=1280:-1"', short_name='f')
|
main.add_option('ffmpeg-vfilter', 'str', '', 'Raw ffmpeg -filter:v options, e.g. "scale=720:trunc(ow/a/2)*2"', short_name='f')
|
||||||
main.add_option('move-source', 'str', '', 'Move source file to this directory after conversion.', short_name='m')
|
main.add_option('move-source', 'str', '', 'Move source file to this directory after conversion.', short_name='m')
|
||||||
main.add_option('dump-commands', 'bool', False, 'Print ffmpeg commands that would be executed. If §B--§gnormalize§/ is in effect, the normalization pre-pass will still be performed so that the proper volume correction can be computed.', short_name='D')
|
main.add_option('dump-commands', 'bool', False, 'Print ffmpeg commands that would be executed. If §B--§gnormalize§/ is in effect, the normalization pre-pass will still be performed so that the proper volume correction can be computed.', short_name='D')
|
||||||
main.add_option('armed', 'bool', False, 'Perform the suggested action.', short_name='A', use_cfg_file=False)
|
main.add_option('armed', 'bool', False, 'Perform the suggested action.', short_name='A', use_cfg_file=False)
|
||||||
|
@ -50,8 +52,8 @@ if __name__ == '__main__':
|
||||||
main.add_help('Description', ['Over-Video is a simple video converter.'])
|
main.add_help('Description', ['Over-Video is a simple video converter.'])
|
||||||
main.parse()
|
main.parse()
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
# cfg checks
|
# cfg checks
|
||||||
|
|
||||||
files = over.core.types.ndict()
|
files = over.core.types.ndict()
|
||||||
audio_words = []
|
audio_words = []
|
||||||
|
@ -86,11 +88,14 @@ if __name__ == '__main__':
|
||||||
elif main.cfg.audio == 'vorbis':
|
elif main.cfg.audio == 'vorbis':
|
||||||
files.container = 'ogg'
|
files.container = 'ogg'
|
||||||
|
|
||||||
_print('input parameters:', prefix.start)
|
_print('settings', prefix.start, suffix=':\n')
|
||||||
_print('audio: %s' %(', '.join(audio_words)))
|
_print('audio: %s' %(', '.join(audio_words)))
|
||||||
_print('video: %s' %(', '.join(video_words)))
|
_print('video: %s' %(', '.join(video_words)))
|
||||||
_print('container: §gtype§/=§m%s§/' %(files.container))
|
_print('container: §gtype§/=§m%s§/' %(files.container))
|
||||||
|
|
||||||
|
if main.cfg.move_source:
|
||||||
|
_print('move source files to §B%s§//' %(main.cfg.move_source))
|
||||||
|
|
||||||
if main.cfg.audio not in ('drop', 'copy', 'pcm', 'vorbis'):
|
if main.cfg.audio not in ('drop', 'copy', 'pcm', 'vorbis'):
|
||||||
_print('unknown audio codec', prefix.fail)
|
_print('unknown audio codec', prefix.fail)
|
||||||
raise RuntimeError
|
raise RuntimeError
|
||||||
|
@ -105,6 +110,11 @@ if __name__ == '__main__':
|
||||||
for tgt in main.targets:
|
for tgt in main.targets:
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
files.infile = to_Path(tgt)
|
||||||
|
files.tmpfile = to_Path(tempfile.mktemp(suffix='.' + files.container, dir='.'))
|
||||||
|
files.outfile = files.infile.parent / (str(files.infile.stem) + '.' + files.container)
|
||||||
|
files.move_infile_to = to_Path(main.cfg.move_source) / files.infile.name if main.cfg.move_source else None
|
||||||
|
|
||||||
if not os.path.exists(tgt) or os.path.isdir(tgt):
|
if not os.path.exists(tgt) or os.path.isdir(tgt):
|
||||||
_print('target §y%s§/ §ris not a readable file§/, skipping' %(tgt), prefix.fail)
|
_print('target §y%s§/ §ris not a readable file§/, skipping' %(tgt), prefix.fail)
|
||||||
continue
|
continue
|
||||||
|
@ -115,7 +125,7 @@ if __name__ == '__main__':
|
||||||
# identify the input file
|
# identify the input file
|
||||||
|
|
||||||
command.identify.reset()
|
command.identify.reset()
|
||||||
command.identify.INFILE = tgt
|
command.identify.INFILE = 'file:' + str(files.infile)
|
||||||
command.identify.run()
|
command.identify.run()
|
||||||
identify_raw = command.identify.get_all_output()
|
identify_raw = command.identify.get_all_output()
|
||||||
identify_dict = json.loads(identify_raw.decode('utf-8'))
|
identify_dict = json.loads(identify_raw.decode('utf-8'))
|
||||||
|
@ -133,11 +143,11 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
if amount_vs != 1:
|
if amount_vs != 1:
|
||||||
_print('detected §r%d§/ video streams' %(amount_vs), prefix.fail)
|
_print('detected §r%d§/ video streams' %(amount_vs), prefix.fail)
|
||||||
|
print(video_streams)
|
||||||
main.exit(1)
|
main.exit(1)
|
||||||
|
|
||||||
if amount_as != 1:
|
if amount_as != 1:
|
||||||
_print('detected §r%d§/ audio streams' %(amount_as), prefix.fail)
|
_print('detected §y%d§/ audio streams, picking the "best" one (see man 1 ffmpeg, section STREAM SELECTION)' %(amount_as), prefix.warn)
|
||||||
main.exit(1)
|
|
||||||
|
|
||||||
video = video_streams[0]
|
video = video_streams[0]
|
||||||
audio = audio_streams[0]
|
audio = audio_streams[0]
|
||||||
|
@ -154,8 +164,8 @@ if __name__ == '__main__':
|
||||||
info.audio_language = audio['tags']['language'] if 'tags' in audio and 'language' in audio['tags'] else 'und'
|
info.audio_language = audio['tags']['language'] if 'tags' in audio and 'language' in audio['tags'] else 'und'
|
||||||
info.audio_bitrate = over.core.textui.Unit(audio['bit_rate'], 'b/s') if 'bit_rate' in audio else '§r??§/'
|
info.audio_bitrate = over.core.textui.Unit(audio['bit_rate'], 'b/s') if 'bit_rate' in audio else '§r??§/'
|
||||||
|
|
||||||
except KeyError:
|
except:
|
||||||
_print('KeyError while reading identify_dict, dump follows', prefix.fail)
|
_print('exception while reading identify_dict, dump follows', prefix.fail)
|
||||||
print(identify_dict)
|
print(identify_dict)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
@ -170,7 +180,7 @@ if __name__ == '__main__':
|
||||||
_print('running normalization pre-pass')
|
_print('running normalization pre-pass')
|
||||||
|
|
||||||
command.normalize_prepass.reset()
|
command.normalize_prepass.reset()
|
||||||
command.normalize_prepass.INFILE = tgt
|
command.normalize_prepass.INFILE = 'file:' + str(files.infile)
|
||||||
command.normalize_prepass.run(stderr=True)
|
command.normalize_prepass.run(stderr=True)
|
||||||
|
|
||||||
pb = over.core.textui.ProgressBar(60, int(info.video_fps.value * info.duration), 'frames')
|
pb = over.core.textui.ProgressBar(60, int(info.video_fps.value * info.duration), 'frames')
|
||||||
|
@ -215,19 +225,14 @@ if __name__ == '__main__':
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
# main command assembly
|
# main command assembly
|
||||||
|
|
||||||
files.infile = to_Path(tgt)
|
|
||||||
files.tmpfile = to_Path(tempfile.mktemp(suffix='.' + files.container, dir='.'))
|
|
||||||
files.outfile = files.infile.parent / (str(files.infile.stem) + '.' + files.container)
|
|
||||||
files.move_infile_to = to_Path(main.cfg.move_source) / files.infile.name if main.cfg.move_source else None
|
|
||||||
|
|
||||||
encode_cmd = command.encode_generic
|
encode_cmd = command.encode_generic
|
||||||
encode_cmd.reset()
|
encode_cmd.reset()
|
||||||
|
|
||||||
encode_cmd.INFILE = files.infile
|
encode_cmd.INFILE = 'file:' + str(files.infile)
|
||||||
encode_cmd.OUTFILE = files.tmpfile
|
encode_cmd.OUTFILE = files.tmpfile
|
||||||
|
|
||||||
if main.cfg.audio == 'copy':
|
if main.cfg.audio == 'copy':
|
||||||
encode_cmd.AUDIO = None
|
encode_cmd.AUDIO = command.sub_copy_audio
|
||||||
elif main.cfg.audio == 'drop':
|
elif main.cfg.audio == 'drop':
|
||||||
encode_cmd.AUDIO = '-an'
|
encode_cmd.AUDIO = '-an'
|
||||||
elif main.cfg.audio == 'pcm':
|
elif main.cfg.audio == 'pcm':
|
||||||
|
@ -250,7 +255,7 @@ if __name__ == '__main__':
|
||||||
info.vfilter_command = None
|
info.vfilter_command = None
|
||||||
|
|
||||||
if main.cfg.video == 'copy':
|
if main.cfg.video == 'copy':
|
||||||
encode_cmd.VIDEO = None
|
encode_cmd.VIDEO = command.sub_copy_video
|
||||||
elif main.cfg.video == 'drop':
|
elif main.cfg.video == 'drop':
|
||||||
encode_cmd.VIDEO = '-vn'
|
encode_cmd.VIDEO = '-vn'
|
||||||
elif main.cfg.video == 'theora':
|
elif main.cfg.video == 'theora':
|
||||||
|
@ -270,7 +275,7 @@ if __name__ == '__main__':
|
||||||
# run the command if armed
|
# run the command if armed
|
||||||
|
|
||||||
if main.cfg.dump_commands or main.cfg.armed:
|
if main.cfg.dump_commands or main.cfg.armed:
|
||||||
cmd = ' '.join(encode_cmd.dump())
|
cmd = ' '.join(encode_cmd.dump(pretty=True))
|
||||||
|
|
||||||
if main.cfg.armed:
|
if main.cfg.armed:
|
||||||
_print('executing §B%s§/' %(cmd), prefix.start)
|
_print('executing §B%s§/' %(cmd), prefix.start)
|
||||||
|
@ -301,7 +306,12 @@ if __name__ == '__main__':
|
||||||
new_filesize = over.core.textui.Unit(files.tmpfile.stat().st_size, 'B')
|
new_filesize = over.core.textui.Unit(files.tmpfile.stat().st_size, 'B')
|
||||||
|
|
||||||
pb.blank()
|
pb.blank()
|
||||||
_print('encoding finished: %s -> %s' %(original_filesize, new_filesize), prefix.done)
|
|
||||||
|
if encode_cmd.returncode == 0:
|
||||||
|
_print('encoding finished: %s -> %s' %(original_filesize, new_filesize), prefix.done)
|
||||||
|
else:
|
||||||
|
_print('§rencoding failed§/, ffmpeg returned §y%d§/' %(encode_cmd.returncode), prefix.fail)
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
# shuffle files around
|
# shuffle files around
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue