diff --git a/aux.py b/aux.py index 329847e..f651bf8 100644 --- a/aux.py +++ b/aux.py @@ -63,7 +63,7 @@ class Command: self.__dict__["fifo"] = None self.__dict__["terminated"] = False - def dump(self, sequence=None): + def dump(self, sequence=None, pretty=False): out = [] if not sequence: @@ -77,7 +77,10 @@ class Command: elif item is not None: 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): """ @@ -118,6 +121,7 @@ class Command: if None in buffer: self.__dict__["terminated"] = True + self.__dict__["returncode"] = self.process.poll() if len(buffer) == 1: return None diff --git a/over-video.py b/over-video.py index 45b3d32..a07cb82 100755 --- a/over-video.py +++ b/over-video.py @@ -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_normalize = Command('-filter:a', 'VOLUME') 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('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('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('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) @@ -50,8 +52,8 @@ if __name__ == '__main__': main.add_help('Description', ['Over-Video is a simple video converter.']) main.parse() -# -------------------------------------------------- -# cfg checks + # -------------------------------------------------- + # cfg checks files = over.core.types.ndict() audio_words = [] @@ -86,11 +88,14 @@ if __name__ == '__main__': elif main.cfg.audio == 'vorbis': files.container = 'ogg' - _print('input parameters:', prefix.start) + _print('settings', prefix.start, suffix=':\n') _print('audio: %s' %(', '.join(audio_words))) _print('video: %s' %(', '.join(video_words))) _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'): _print('unknown audio codec', prefix.fail) raise RuntimeError @@ -105,6 +110,11 @@ if __name__ == '__main__': for tgt in main.targets: 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): _print('target §y%s§/ §ris not a readable file§/, skipping' %(tgt), prefix.fail) continue @@ -115,7 +125,7 @@ if __name__ == '__main__': # identify the input file command.identify.reset() - command.identify.INFILE = tgt + command.identify.INFILE = 'file:' + str(files.infile) command.identify.run() identify_raw = command.identify.get_all_output() identify_dict = json.loads(identify_raw.decode('utf-8')) @@ -133,11 +143,11 @@ if __name__ == '__main__': if amount_vs != 1: _print('detected §r%d§/ video streams' %(amount_vs), prefix.fail) + print(video_streams) main.exit(1) if amount_as != 1: - _print('detected §r%d§/ audio streams' %(amount_as), prefix.fail) - main.exit(1) + _print('detected §y%d§/ audio streams, picking the "best" one (see man 1 ffmpeg, section STREAM SELECTION)' %(amount_as), prefix.warn) video = video_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_bitrate = over.core.textui.Unit(audio['bit_rate'], 'b/s') if 'bit_rate' in audio else '§r??§/' - except KeyError: - _print('KeyError while reading identify_dict, dump follows', prefix.fail) + except: + _print('exception while reading identify_dict, dump follows', prefix.fail) print(identify_dict) raise @@ -170,7 +180,7 @@ if __name__ == '__main__': _print('running normalization pre-pass') command.normalize_prepass.reset() - command.normalize_prepass.INFILE = tgt + command.normalize_prepass.INFILE = 'file:' + str(files.infile) command.normalize_prepass.run(stderr=True) pb = over.core.textui.ProgressBar(60, int(info.video_fps.value * info.duration), 'frames') @@ -215,19 +225,14 @@ if __name__ == '__main__': # -------------------------------------------------- # 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.reset() - encode_cmd.INFILE = files.infile + encode_cmd.INFILE = 'file:' + str(files.infile) encode_cmd.OUTFILE = files.tmpfile if main.cfg.audio == 'copy': - encode_cmd.AUDIO = None + encode_cmd.AUDIO = command.sub_copy_audio elif main.cfg.audio == 'drop': encode_cmd.AUDIO = '-an' elif main.cfg.audio == 'pcm': @@ -250,7 +255,7 @@ if __name__ == '__main__': info.vfilter_command = None if main.cfg.video == 'copy': - encode_cmd.VIDEO = None + encode_cmd.VIDEO = command.sub_copy_video elif main.cfg.video == 'drop': encode_cmd.VIDEO = '-vn' elif main.cfg.video == 'theora': @@ -270,7 +275,7 @@ if __name__ == '__main__': # run the command if 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: _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') 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