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
|
#!/usr/bin/python3
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
import ox
|
import ox
|
||||||
import ox.web.auth
|
import ox.web.auth
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ def sort_clips(edit, sort):
|
||||||
'id', 'index', 'in', 'out', 'duration',
|
'id', 'index', 'in', 'out', 'duration',
|
||||||
'title', 'director', 'year', 'videoRatio'
|
'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:
|
if reverse:
|
||||||
s = reversed(s)
|
s = reversed(s)
|
||||||
else:
|
else:
|
||||||
|
@ -77,6 +78,15 @@ if __name__ == '__main__':
|
||||||
position = 0
|
position = 0
|
||||||
for clip in sort_clips(edit, sort_by):
|
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
|
part_pos = 0
|
||||||
for i, duration in enumerate(clip['durations']):
|
for i, duration in enumerate(clip['durations']):
|
||||||
if part_pos + duration < clip['in']:
|
if part_pos + duration < clip['in']:
|
||||||
|
@ -84,6 +94,10 @@ if __name__ == '__main__':
|
||||||
elif part_pos <= clip['in']:
|
elif part_pos <= clip['in']:
|
||||||
stream_in = clip['in'] - part_pos
|
stream_in = clip['in'] - part_pos
|
||||||
stream_out = min(clip['out'] - part_pos, duration)
|
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
|
part_pos += duration
|
||||||
info = get_info(api, clip['streams'][i])
|
info = get_info(api, clip['streams'][i])
|
||||||
videos.append({
|
videos.append({
|
||||||
|
@ -91,11 +105,14 @@ if __name__ == '__main__':
|
||||||
'path': os.path.join(prefix, info['path']),
|
'path': os.path.join(prefix, info['path']),
|
||||||
'resolution': info['resolution'],
|
'resolution': info['resolution'],
|
||||||
'in': stream_in,
|
'in': stream_in,
|
||||||
'out': stream_out
|
'out': stream_out,
|
||||||
|
'subtitles': '\n'.join(clip_subtitles)
|
||||||
})
|
})
|
||||||
elif clip['out'] > part_pos:
|
elif clip['out'] > part_pos:
|
||||||
stream_in = part_pos
|
stream_in = part_pos
|
||||||
stream_out = min(clip['out'] - part_pos, duration)
|
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
|
part_pos += duration
|
||||||
info = get_info(api, clip['streams'][i])
|
info = get_info(api, clip['streams'][i])
|
||||||
videos.append({
|
videos.append({
|
||||||
|
@ -103,17 +120,13 @@ if __name__ == '__main__':
|
||||||
'path': info['path'],
|
'path': info['path'],
|
||||||
'resolution': info['resolution'],
|
'resolution': info['resolution'],
|
||||||
'in': stream_in,
|
'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 += clip['duration']
|
||||||
|
position = math.ceil(position / (1/25)) * 1/25
|
||||||
|
|
||||||
name = normalize(edit_id)
|
name = normalize(edit_id)
|
||||||
if sort_by != 'year':
|
if sort_by != 'year':
|
||||||
|
|
38
ffmpeg.py
38
ffmpeg.py
|
@ -3,6 +3,7 @@ import json
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import ox
|
||||||
|
|
||||||
def run(cmd):
|
def run(cmd):
|
||||||
#print(' '.join('"%s"' % c for c in cmd))
|
#print(' '.join('"%s"' % c for c in cmd))
|
||||||
|
@ -12,7 +13,7 @@ def run(cmd):
|
||||||
edit_json = sys.argv[1]
|
edit_json = sys.argv[1]
|
||||||
edit = json.load(open(edit_json))
|
edit = json.load(open(edit_json))
|
||||||
|
|
||||||
render = '/tmp/out'
|
render = './cache'
|
||||||
output = os.path.splitext(edit_json)[0] + '.mp4'
|
output = os.path.splitext(edit_json)[0] + '.mp4'
|
||||||
height = 480
|
height = 480
|
||||||
aspect = 16/9
|
aspect = 16/9
|
||||||
|
@ -23,10 +24,23 @@ if not os.path.exists(render):
|
||||||
os.makedirs(render)
|
os.makedirs(render)
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
|
edit_duration = 0
|
||||||
|
subtitles = []
|
||||||
|
position = 0
|
||||||
for clip in edit:
|
for clip in edit:
|
||||||
out = render + '/%s_%0.3f-%0.3f.ts' % (clip['oshash'], clip['in'], clip['out'])
|
out = render + '/%s_%0.3f-%0.3f.ts' % (clip['oshash'], clip['in'], clip['out'])
|
||||||
|
edit_duration += (clip['out']-clip['in'])
|
||||||
files.append(out)
|
files.append(out)
|
||||||
if os.path.exists(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
|
continue
|
||||||
clip_aspect = clip['resolution'][0] / clip['resolution'][1]
|
clip_aspect = clip['resolution'][0] / clip['resolution'][1]
|
||||||
if clip_aspect < aspect:
|
if clip_aspect < aspect:
|
||||||
|
@ -44,12 +58,14 @@ for clip in edit:
|
||||||
offset = int(((y - height) / 3))
|
offset = int(((y - height) / 3))
|
||||||
vf += ',crop=w=%s:h=%s:x=0:y=%s' % (width, height, offset)
|
vf += ',crop=w=%s:h=%s:x=0:y=%s' % (width, height, offset)
|
||||||
options = [
|
options = [
|
||||||
|
'-map_metadata', '-1',
|
||||||
'-vf', vf,
|
'-vf', vf,
|
||||||
'-aspect', str(aspect),
|
'-aspect', str(aspect),
|
||||||
'-c:v', 'libx264',
|
'-c:v', 'libx264',
|
||||||
'-b:v', '2M',
|
'-b:v', '2M',
|
||||||
'-preset:v', 'medium', '-profile:v', 'high', '-level:v', '4.0',
|
'-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',
|
'-c:a', 'aac',
|
||||||
'-ar', '48000',
|
'-ar', '48000',
|
||||||
'-ac', '2',
|
'-ac', '2',
|
||||||
|
@ -65,6 +81,15 @@ for clip in edit:
|
||||||
out
|
out
|
||||||
]
|
]
|
||||||
run(cmd)
|
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'
|
txt = output + '.txt'
|
||||||
with open(txt, 'w') as fd:
|
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]
|
cmd = ['ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', txt, '-c', 'copy', output]
|
||||||
run(cmd)
|
run(cmd)
|
||||||
os.unlink(txt)
|
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