mlt generation

This commit is contained in:
j 2013-05-02 13:19:00 +02:00
commit eeaf7d62ce
3 changed files with 362 additions and 0 deletions

4
README Normal file
View file

@ -0,0 +1,4 @@
generate mtl timeline for clips from a pan.do/ra instance
run generate.py to create a temorary json playlist.
and use timeline.py to convert it into a kdenlive project

106
generate.py Executable file
View file

@ -0,0 +1,106 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
import json
import ox
import os
api = ox.API('https://0xdb.org/api/')
config = json.load(open(os.path.expanduser('~/.ox/client.json')))
api.signin(username=config['username'], password=config['password'])
def get_files(item):
files = api.findFiles({
"keys": [
"selected",
#"path",
"part",
"language",
"type",
"duration",
"instances"
],
"query": {
"conditions":[{
"key": "id",
"value": item,
"operator": "=="
}]
},
"range": [0, 100],
"sort": [{"key": "path", "operator": "+"}]
})['data']['items']
files = filter(lambda f: f['type'] == 'video' and f['selected'], files)
for f in files:
f['path'] = f['instances'][0]['path']
del f['instances']
return files
def get_clips(terms):
clips = []
for term in terms:
r = api.findClips({
"keys":["position","annotations","id","in","out","videoRatio", "parts"],
"query":{"conditions":[{"operator":"=","key":"subtitles","value":term}],
"operator":"&"},
"range":[0,1000],
"sort":[{"operator":"+","key":"position"}],
#"sort":[{"operator":"+","key":"year"}],
"itemsQuery":{
"operator":"&",
"conditions":[{"operator":"=","key":"subtitles","value":term}]
}
})
if not 'data' in r or not 'items' in r['data']:
print r
clips += r['data']['items']
cache = {}
playlist = []
clips.sort(key=lambda c: c['in'])
for clip in clips:
id = clip['id'].split('/')[0]
part = 1
if not id in cache:
cache[id] = get_files(id)
data = cache[id]
position = 0
#FIXME: handle clips that span across parts
for part, f in enumerate(data):
if clip['in'] < position + f['duration']:
clip_in = clip['in'] - position
clip_out = clip['out'] - position
break
else:
position += f['duration']
path = f['path']
print path, clip_in, clip_out
subtitles = ''
for a in clip['annotations']:
subtitles += a['value']
subtitles = ox.strip_tags(subtitles)
playlist.append({
"id": id,
"part": part,
"path": path,
"in": clip_in,
"out": clip_out,
"subtitles": subtitles
})
return playlist
if __name__ == "__main__":
import sys
if len(sys.argv) != 3:
print 'usage: %s <term> <output.json>' % sys.argv[0]
sys.exit(1)
terms = [sys.argv[1]]
output = sys.argv[2]
playlist = get_clips(terms)
with open(output, 'w') as f:
json.dump(playlist, f, indent=2)

252
timeline.py Executable file
View file

@ -0,0 +1,252 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division, with_statement
import os
import sys
import json
import ox
MIN_DURATION = 2
profiles = {
'pal': '<profile width="720" display_aspect_den="3" colorspace="601" frame_rate_den="1" description="DV/DVD PAL" height="576" display_aspect_num="4" frame_rate_num="25" progressive="0" sample_aspect_num="16" sample_aspect_den="15"/>'
}
class Video:
position = 0
playlist = []
producers = {}
template = '''<?xml version='1.0' encoding='utf-8'?>
<mlt title="%(title)s" version="0.8.0" root="%(root)s" LC_NUMERIC="en_US.UTF-8">
%(profile)s
<producer in="0" out="72869" id="black">
<property name="mlt_type">producer</property>
<property name="length">72870</property>
<property name="eof">pause</property>
<property name="resource">black</property>
<property name="aspect_ratio">0</property>
<property name="mlt_service">colour</property>
</producer>
<playlist id="black_track">
<entry in="0" out="12047" producer="black"/>
</playlist>
<playlist id="playlist1"/>
<playlist id="playlist2"/>
<playlist id="playlist3"/>
%(producers)s
<playlist id="playlist4">
%(playlist)s
</playlist>
<playlist id="playlist5"/>
<tractor title="%(title)s" global_feed="1" in="0" out="12047" id="maintractor">
<property name="meta.volume">1</property>
<track producer="black_track"/>
<track hide="video" producer="playlist1"/>
<track hide="video" producer="playlist2"/>
<track producer="playlist3"/>
<track producer="playlist4"/>
<track producer="playlist5"/>
<transition id="transition0">
<property name="a_track">1</property>
<property name="b_track">2</property>
<property name="mlt_type">transition</property>
<property name="mlt_service">mix</property>
<property name="always_active">1</property>
<property name="combine">1</property>
<property name="internal_added">237</property>
</transition>
<transition id="transition1">
<property name="a_track">1</property>
<property name="b_track">3</property>
<property name="mlt_type">transition</property>
<property name="mlt_service">mix</property>
<property name="always_active">1</property>
<property name="combine">1</property>
<property name="internal_added">237</property>
</transition>
<transition id="transition2">
<property name="a_track">1</property>
<property name="b_track">4</property>
<property name="mlt_type">transition</property>
<property name="mlt_service">mix</property>
<property name="always_active">1</property>
<property name="combine">1</property>
<property name="internal_added">237</property>
</transition>
<transition id="transition3">
<property name="a_track">1</property>
<property name="b_track">5</property>
<property name="mlt_type">transition</property>
<property name="mlt_service">mix</property>
<property name="always_active">1</property>
<property name="combine">1</property>
<property name="internal_added">237</property>
</transition>
</tractor>
<kdenlivedoc profile="dv_pal" kdenliveversion="0.9.2" version="0.88" projectfolder="%(root)s">
<customeffects/>
<documentproperties proxyimageminsize="2000" zonein="0" enableproxy="0" zoneout="100" generateproxy="0" zoom="9" verticalzoom="1" proxyextension="ts" position="1" documentid="1353193897205" generateimageproxy="0" proxyminsize="1000" proxyparams="-f mpegts -acodec libmp3lame -ac 2 -ab 128k -ar 48000 -vcodec mpeg2video -g 5 -deinterlace -s 480x270 -vb 400k"/>
<documentmetadata/>
<documentnotes>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:8pt; font-weight:400; font-style:normal;">
&lt;p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;br />&lt;/p>&lt;/body>&lt;/html></documentnotes>
<profileinfo width="720" display_aspect_den="3" frame_rate_den="1" description="DV/DVD PAL" height="576" frame_rate_num="25" display_aspect_num="4" progressive="0" sample_aspect_num="16" sample_aspect_den="15"/>
<tracksinfo>
<trackinfo blind="1" mute="0" locked="0" trackname="Audio 2" type="audio"/>
<trackinfo blind="1" mute="0" locked="0" trackname="Audio 1" type="audio"/>
<trackinfo blind="0" mute="0" locked="0" trackname="Video 3"/>
<trackinfo blind="0" mute="0" locked="0" trackname="Video 2"/>
<trackinfo blind="0" mute="0" locked="0" trackname="Video 1"/>
</tracksinfo>
%(kdenlive_producers)s
<markers/>
<groups/>
</kdenlivedoc>
</mlt>
'''
def __init__(self, data, title, url, root):
files = []
items = []
for c in data:
files.append(c['path'])
items.append(c['id'])
files = list(set(files))
items = list(set(items))
self.files = files
self.clips = data
self.fps = 25
profile = profiles[self.fps == 25 and 'pal' or 'ntsc']
self.info = {
'profile': profile,
'root': root,
'title': title,
'url': url,
'producers': '',
'kdenlive_producers': '',
'duration': 0,
'stats': '%d clips from %d films' % (len(self.clips), len(items))
}
self.add_title()
for clip in self.clips:
path = clip['path']
if not path.startswith('/'):
path = os.path.join('/data/Cinema', clip['path'])
out = clip['out']
if out-clip['in'] < MIN_DURATION:
out = clip['in'] + MIN_DURATION
duration = int((out-clip['in']) * self.fps)
self.add_clip(path, clip['in'], out)
self.info['duration'] += duration
def render(self, output):
xml = output
with open(xml, 'w') as f:
self.info['playlist'] = '\n'.join(self.playlist)
data = self.template % self.info
f.write(data.strip())
#os.system('melt -progress "%s" -consumer "avformat:%s"'%(xml, output))
def add_title(self):
self.info['duration'] = 5*self.fps
self.info['producers'] += '''
<producer in="0" out="%(duration)s" id="1">
<property name="mlt_type">producer</property>
<property name="length">%(duration)s</property>
<property name="eof">pause</property>
<property name="resource"/>
<property name="mlt_service">kdenlivetitle</property>
<property name="xmldata">&lt;kdenlivetitle width="768" height="576" out="125" LC_NUMERIC="en_US.UTF-8">
&lt;item z-index="2" type="QGraphicsTextItem">
&lt;position x="86" y="123">
&lt;transform>1,0,0,0,1,0,0,0,1&lt;/transform>
&lt;/position>
&lt;content font-color="255,255,255,255" font-outline-color="0,0,0,255" font-pixel-size="151" font-italic="0" alignment="4" font-underline="0" font-weight="50" font="Arial Black" font-outline="0.5">%(title)s&lt;/content>
&lt;/item>
&lt;item z-index="1" type="QGraphicsTextItem">
&lt;position x="79" y="321">
&lt;transform>1,0,0,0,1,0,0,0,1&lt;/transform>
&lt;/position>
&lt;content font-color="255,255,255,255" font-outline-color="0,0,0,255" font-pixel-size="24" font-italic="0" alignment="4" font-underline="0" font-weight="50" font="Arial Black" font-outline="0.5">%(url)s
%(stats)s&lt;/content>
&lt;/item>
&lt;startviewport rect="0,0,768,576"/>
&lt;endviewport rect="0,0,768,576"/>
&lt;background color="0,0,0,0"/>
&lt;/kdenlivetitle>
</property>
<property name="force_reload">0</property>
</producer>
''' % self.info
self.playlist.append('<entry in="%s" out="%s" producer="1"/>' % (0, self.info['duration']))
self.info['kdenlive_producers'] += '''
<kdenlive_producer audio_max="0" id="1" default_video="0" xmldata="&lt;kdenlivetitle width=&quot;768&quot; height=&quot;576&quot; out=&quot;125&quot; LC_NUMERIC=&quot;en_US.UTF-8&quot;>&#xa; &lt;item z-index=&quot;2&quot; type=&quot;QGraphicsTextItem&quot;>&#xa; &lt;position x=&quot;86&quot; y=&quot;123&quot;>&#xa; &lt;transform>1,0,0,0,1,0,0,0,1&lt;/transform>&#xa; &lt;/position>&#xa; &lt;content font-color=&quot;255,255,255,255&quot; font-outline-color=&quot;0,0,0,255&quot; font-pixel-size=&quot;151&quot; font-italic=&quot;0&quot; alignment=&quot;4&quot; font-underline=&quot;0&quot; font-weight=&quot;50&quot; font=&quot;Arial Black&quot; font-outline=&quot;0.5&quot;>%(title)s&lt;/content>&#xa; &lt;/item>&#xa; &lt;item z-index=&quot;1&quot; type=&quot;QGraphicsTextItem&quot;>&#xa; &lt;position x=&quot;79&quot; y=&quot;321&quot;>&#xa; &lt;transform>1,0,0,0,1,0,0,0,1&lt;/transform>&#xa; &lt;/position>&#xa; &lt;content font-color=&quot;255,255,255,255&quot; font-outline-color=&quot;0,0,0,255&quot; font-pixel-size=&quot;24&quot; font-italic=&quot;0&quot; alignment=&quot;4&quot; font-underline=&quot;0&quot; font-weight=&quot;50&quot; font=&quot;Arial Black&quot; font-outline=&quot;0.5&quot;>%(url)s&#xa;%(stats)s&lt;/content>&#xa; &lt;/item>&#xa; &lt;startviewport rect=&quot;0,0,768,576&quot;/>&#xa; &lt;endviewport rect=&quot;0,0,768,576&quot;/>&#xa; &lt;background color=&quot;0,0,0,0&quot;/>&#xa;&lt;/kdenlivetitle>&#xa;" name="Title" in="0" thumbnail="100" transparency="1" default_audio="0" duration="125" aspect_ratio="0.000000" channels="0" frequency="0" out="125" video_max="0" progressive="0" type="6" frame_size="720x576"/>
''' % self.info
def add_producer(self, uri):
c = len(self.producers.keys()) + 2
self.producers[uri] = c
info = ox.avinfo(uri)
i = {
'id': c,
'duration': int(info['duration'] * self.fps),
'filename': uri.encode('utf-8'),
'basename': os.path.basename(uri).encode('utf-8')
}
i['out'] = i['duration'] - 1
self.info['producers'] += '''
<producer in="0" out="%(out)s" id="%(id)s_1">
<property name="mlt_type">producer</property>
<property name="length">%(duration)s</property>
<property name="eof">pause</property>
<property name="resource">%(filename)s</property>
<property name="seekable">1</property>
<property name="mlt_service">avformat</property>
</producer>
''' % i
self.info['kdenlive_producers'] += '''
<kdenlive_producer
id="%(id)s"
name="%(basename)s"
resource="%(filename)s"
/>
''' % i
def get_producer(self, uri):
if not uri in self.producers:
self.add_producer(uri)
return self.producers[uri]
def add_clip(self, uri, start, end):
start = int(start * self.fps)
end = int(end * self.fps) - 1
o = '<entry in="%s" out="%s" producer="%d_1"/>' % (start, end, self.get_producer(uri))
self.playlist.append(o)
if __name__ == '__main__':
if len(sys.argv) != 5:
print 'usage: %s <json input> <video prefix> <output> <title>' % sys.argv[0]
sys.exit(1)
json_input = sys.argv[1]
video_prefix = sys.argv[2]
root = sys.argv[3]
title = sys.argv[4]
output = json_input + '.kdenlive'
url = 'http://pan.do/ra'
data = json.load(open(json_input))
c = Video(data,
title,
url,
root)
c.render(output)