align subtitles to clips
This commit is contained in:
parent
4f88792491
commit
609a1209ab
2 changed files with 58 additions and 11 deletions
31
edit.py
31
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('<br/>', '\n').replace('<br>', '\n').replace('\n\n', '\n'),
|
||||
})
|
||||
clip_subtitles.append(sub['value'].replace('<br/>', '\n').replace('<br>', '\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('<br/>', '\n').replace('<br>', '\n').replace('\n\n', '\n'),
|
||||
})
|
||||
|
||||
position += clip['duration']
|
||||
position = math.ceil(position / (1/25)) * 1/25
|
||||
|
||||
name = normalize(edit_id)
|
||||
if sort_by != 'year':
|
||||
|
|
38
ffmpeg.py
38
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))
|
||||
|
||||
|
|
Loading…
Reference in a new issue