From 609a1209ab932188ecee5fe71497c9db4794fe93 Mon Sep 17 00:00:00 2001 From: j Date: Sun, 13 Aug 2017 19:24:50 +0200 Subject: [PATCH] align subtitles to clips --- edit.py | 31 ++++++++++++++++++++++--------- ffmpeg.py | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/edit.py b/edit.py index ae643a6..f659a6d 100755 --- a/edit.py +++ b/edit.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 import os import json +import math import ox import ox.web.auth @@ -46,7 +47,7 @@ def sort_clips(edit, sort): 'id', 'index', 'in', 'out', 'duration', 'title', 'director', 'year', 'videoRatio' ]: - s = sorted(clips, key=lambda c: (str(c.get(sort, last)), c['in'], c['out'])) + s = sorted(clips, key=lambda c: (str(c.get(sort, last)), c['title'], c['in'], c['out'])) if reverse: s = reversed(s) else: @@ -77,6 +78,15 @@ if __name__ == '__main__': position = 0 for clip in sort_clips(edit, sort_by): + clip_subtitles = [] + for sub in clip['layers']['subtitles']: + subtitles.append({ + 'in': position, + 'out': position + (sub['out'] - sub['in']), + 'value': sub['value'].replace('
', '\n').replace('
', '\n').replace('\n\n', '\n'), + }) + clip_subtitles.append(sub['value'].replace('
', '\n').replace('
', '\n').replace('\n\n', '\n')) + part_pos = 0 for i, duration in enumerate(clip['durations']): if part_pos + duration < clip['in']: @@ -84,6 +94,10 @@ if __name__ == '__main__': elif part_pos <= clip['in']: stream_in = clip['in'] - part_pos stream_out = min(clip['out'] - part_pos, duration) + + stream_in = math.ceil(stream_in / (1/25)) * 1/25 + stream_out = math.ceil(stream_out / (1/25)) * 1/25 + part_pos += duration info = get_info(api, clip['streams'][i]) videos.append({ @@ -91,11 +105,14 @@ if __name__ == '__main__': 'path': os.path.join(prefix, info['path']), 'resolution': info['resolution'], 'in': stream_in, - 'out': stream_out + 'out': stream_out, + 'subtitles': '\n'.join(clip_subtitles) }) elif clip['out'] > part_pos: stream_in = part_pos stream_out = min(clip['out'] - part_pos, duration) + stream_in = math.ceil(stream_in / (1/25)) * 1/25 + stream_out = math.ceil(stream_out / (1/25)) * 1/25 part_pos += duration info = get_info(api, clip['streams'][i]) videos.append({ @@ -103,17 +120,13 @@ if __name__ == '__main__': 'path': info['path'], 'resolution': info['resolution'], 'in': stream_in, - 'out': stream_out + 'out': stream_out, + 'subtitles': '\n'.join(clip_subtitles) }) - for sub in clip['layers']['subtitles']: - subtitles.append({ - 'in': position, - 'out': position + (sub['out'] - sub['in']), - 'value': sub['value'].replace('
', '\n').replace('
', '\n').replace('\n\n', '\n'), - }) position += clip['duration'] + position = math.ceil(position / (1/25)) * 1/25 name = normalize(edit_id) if sort_by != 'year': diff --git a/ffmpeg.py b/ffmpeg.py index 2eed9a0..31dae70 100755 --- a/ffmpeg.py +++ b/ffmpeg.py @@ -3,6 +3,7 @@ import json import os import subprocess import sys +import ox def run(cmd): #print(' '.join('"%s"' % c for c in cmd)) @@ -12,7 +13,7 @@ def run(cmd): edit_json = sys.argv[1] edit = json.load(open(edit_json)) -render = '/tmp/out' +render = './cache' output = os.path.splitext(edit_json)[0] + '.mp4' height = 480 aspect = 16/9 @@ -23,10 +24,23 @@ if not os.path.exists(render): os.makedirs(render) files = [] +edit_duration = 0 +subtitles = [] +position = 0 for clip in edit: out = render + '/%s_%0.3f-%0.3f.ts' % (clip['oshash'], clip['in'], clip['out']) + edit_duration += (clip['out']-clip['in']) files.append(out) if os.path.exists(out): + duration = ox.avinfo(out)['duration'] + if clip['subtitles']: + subtitles.append({ + 'in': position, + 'out': position+duration, + 'value': clip['subtitles'] + }) + position += duration + print(out, duration, (clip['out'] - clip['in'])) continue clip_aspect = clip['resolution'][0] / clip['resolution'][1] if clip_aspect < aspect: @@ -44,12 +58,14 @@ for clip in edit: offset = int(((y - height) / 3)) vf += ',crop=w=%s:h=%s:x=0:y=%s' % (width, height, offset) options = [ + '-map_metadata', '-1', '-vf', vf, '-aspect', str(aspect), '-c:v', 'libx264', '-b:v', '2M', '-preset:v', 'medium', '-profile:v', 'high', '-level:v', '4.0', - '-r:v', '25', + '-map', '0:0,0:0', '-map', '0:1,0:1', + '-r', '25', '-c:a', 'aac', '-ar', '48000', '-ac', '2', @@ -65,6 +81,15 @@ for clip in edit: out ] run(cmd) + duration = ox.avinfo(out)['duration'] + if clip['subtitles']: + subtitles.append({ + 'in': position, + 'out': position+duration, + 'value': clip['subtitles'] + }) + position += duration + print(out, duration, (clip['out'] - clip['in'])) txt = output + '.txt' with open(txt, 'w') as fd: @@ -72,3 +97,12 @@ with open(txt, 'w') as fd: cmd = ['ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', txt, '-c', 'copy', output] run(cmd) os.unlink(txt) + +srt = output.replace('.mp4', '.srt') + +with open(srt, 'wb') as fd: + fd.write(ox.srt.encode(subtitles)) + +duration = ox.avinfo(output)['duration'] +print('file is %d, edit should be %d' % (duration, edit_duration)) +