padmafcp/fcp2edit.py

202 lines
6.6 KiB
Python
Executable File

#!/usr/bin/env python
from __future__ import division
import lxml
import lxml.etree
import ox
import os
import json
import sys
import re
source = sys.argv[1]
data = []
tree = lxml.etree.parse(source)
api = ox.API('https://pad.ma/api/')
if os.path.exists('sot.json'):
sot = json.load(open('sot.json'))
else:
sot = api.find({
'query': {
'conditions': [{'key': 'list', 'value': 'zi:SOT'}]
},
'keys': ['id', 'title', 'duration'],
'range': [0, 5000]
})['data']['items']
with open('sot.json', 'w') as f:
json.dump(sot, f, indent=2)
def get_item(id):
id = id.replace('_', ' ').replace('.MOV', '').replace('BT2C0662 3', 'BT2C0662')
for data in sot:
if id in data['title'].replace('/', ' '):
return data['id']
for k in ('STK', 'MNK', 'BT2C'):
id = id.replace(k, '').strip()
for data in sot:
if id in data['title'].replace('/', ' '):
return data['id']
print 'missing', id
durations = {data['id']: data['duration'] for data in sot}
def parse_fps(rate):
if rate.find('ntsc').text == 'TRUE':
rate = int(rate.find('timebase').text) * 1000 / 1001
else:
rate = int(rate.find('timebase').text)
return rate
tracks = []
seq = tree.getroot()[0]
fps = parse_fps(seq.find('rate'))
v = seq.find('media').find('video')
for t in v.xpath('.//track'):
track = []
for clipitem in t.xpath('.//clipitem'):
_id = clipitem.attrib['id'].strip().split('-Apple')[0]
_id = _id.replace('Shampoo flask', '').replace(' HDR', '').replace(' hdr', '').replace('pro res 422', '').replace(' cool', '').strip()
_id = re.sub(' \d\d?$', '', _id)
#start/end - position on timeline
#in/out - in/out in clip
clip_fps = parse_fps(clipitem.find('rate'))
_in = int(clipitem.findall('in')[0].text)
_out = int(clipitem.findall('out')[0].text)
duration = _out - _in
_start = int(clipitem.findall('start')[0].text)
_end = int(clipitem.findall('end')[0].text)
if _start == -1 and _end == -1:
print 'strange', _start, _end, _in, _out, _id
continue
if _start == -1:
_start = _end - duration
elif _end == -1:
_end = _start + duration
if filter(lambda x: x <0, [_start, _end, _in, _out]):
print 'why -?', _start, _end, _in, _out, _id
if _out - _in != _end - _start:
print '??', _in, _out, _out-_in, 'vs', _start, _end, _end-_start, _id
if _start > -1 and _end > -1:
track.append({
'in': _in / fps,
'out': _out / fps,
'start': _start / fps,
'end': _end / fps,
'file': _id,
'id': get_item(_id),
'track': len(tracks)
})
if track:
tracks.append(track)
with open('/tmp/tracks.json', 'w') as f:
json.dump(tracks, f, indent=2, sort_keys=True)
for i, track in enumerate(tracks):
with open('/tmp/tracks%s.json' % i, 'w') as f:
json.dump(track, f, indent=2, sort_keys=True)
def flatten_tracks(tracks):
def split_at_overlaps(clip):
offset_start = clip['start']
offset_clip = clip['in']
points = [clip['start'], clip['end']]
for track in tracks:
for c in track:
if c['track'] != clip['track'] and c['start'] > -1 and c['end'] > -1:
if c['start' ] > clip['start'] and c['start'] < clip['end']:
points.append(c['start'])
if c['end' ] > clip['start'] and c['end'] < clip['end']:
points.append(c['end'])
print clip['track'], points
clips = []
for i, point in enumerate(points[:-1]):
offset_in = point - offset_start
duration = points[i+1] - point
if duration > 0:
clips.append({
'in': offset_clip + offset_in,
'out': offset_clip + offset_in + duration,
'start': point,
'end': points[i+1],
'track': clip['track'],
'id': clip['id']
})
return clips
clips = []
for track in tracks:
for clip in track:
clips += split_at_overlaps(clip)
for clip in clips:
for c in clips:
if c['track'] > clip['track']:
if c['start'] <= clip['start'] and c['end'] >= clip['end']:
clip['delete'] = True
_clips = sorted([c for c in clips if not c.get('delete')], key=lambda a: a['start'])
clips = []
for c in _clips:
if clips and clips[-1]['out'] == c['in'] and clips[-1]['id'] == c['id']:
print 'join', clips[-1], c
clips[-1]['end'] = c['end']
clips[-1]['out'] = c['out']
else:
clips.append(c)
position = None
for c in clips:
if position == None:
position = c['start']
if c['start'] != position:
print 'wrong start', c['start'], position, abs(position - c['start'])
position += c['out'] - c['in']
return clips
timeline = flatten_tracks(tracks)
pandora_edit = []
for c in timeline:
if c['id']:
if c['out'] > durations[c['id']] or c['in'] > durations[c['id']] or c['in'] < 0 or c['out'] <= c['in']:
print 'invalid in/out', c, durations[c['id']]
else:
pandora_edit.append({
'in': c['in'],
'out': c['out'],
'item': c['id']
})
'''
'''
print len(pandora_edit)
#print json.dumps(timeline, indent=2, sort_keys=True)
print json.dumps(pandora_edit, indent=2, sort_keys=True)
with open(os.path.expanduser('~/.ox/client.padma.json')) as f:
settings = json.load(f)
r = api.signin(username=settings['username'], password=settings['password'])
assert(r['status']['code'] == 200)
print 'add clips', len(pandora_edit)
#print pandora_edit
#r = api.addEdit({
# 'name': 'Ship of Theseus',
# 'clips': pandora_edit
#})
#print r['data'].get('id') or r
clips = [c['id'] for c in api.getEdit({'id': 'j:Ship of Theseus', 'keys': ['clips']})['data']['clips']]
if clips:
api.removeClips({
'edit': 'j:Ship of Theseus',
'ids': clips
})
step = 100
while pandora_edit:
clips = pandora_edit[:step]
pandora_edit = pandora_edit[step:]
print 'add', len(clips), 'todo', len(pandora_edit)
r = api.addClips({
'edit': 'j:Ship of Theseus',
'clips': clips
})
print 'total added', len(r and r.get('data', {}).get('clips') or 0)