202 lines
6.6 KiB
Python
202 lines
6.6 KiB
Python
|
#!/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)
|