render all fragments
This commit is contained in:
parent
a5957fc3b2
commit
d113c5d79e
4 changed files with 99 additions and 36 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
|
|
@ -5,7 +5,7 @@ import subprocess
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from ...render import compose, render
|
from ...render import render_all
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
@ -13,38 +13,9 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('--prefix', action='store', dest='prefix', default="/srv/t_for_time", help='prefix to build clips in')
|
parser.add_argument('--prefix', action='store', dest='prefix', default="/srv/t_for_time", help='prefix to build clips in')
|
||||||
parser.add_argument('--duration', action='store', dest='duration', default="150", help='target duration in seconds')
|
parser.add_argument('--duration', action='store', dest='duration', default="3600", help='target duration of all fragments in seconds')
|
||||||
parser.add_argument('--offset', action='store', dest='offset', default="1024", help='inital offset in pi')
|
parser.add_argument('--offset', action='store', dest='offset', default="1024", help='inital offset in pi')
|
||||||
parser.add_argument('--no-video', action='store_true', dest='no_video', default=False, help='don\'t render video')
|
parser.add_argument('--no-video', action='store_true', dest='no_video', default=False, help='don\'t render video')
|
||||||
|
|
||||||
def handle(self, **options):
|
def handle(self, **options):
|
||||||
prefix = options['prefix']
|
render_all(options)
|
||||||
target = int(options['duration'])
|
|
||||||
base = int(options['offset'])
|
|
||||||
|
|
||||||
with open(os.path.join(prefix, "clips.json")) as fd:
|
|
||||||
clips = json.load(fd)
|
|
||||||
scene = compose(clips, target=target, base=base)
|
|
||||||
timelines = render(prefix, scene, 'scene-%s-' % base)
|
|
||||||
with open(os.path.join(prefix, 'scene-%s.json' % base), 'w') as fd:
|
|
||||||
json.dump(scene, fd, indent=2, ensure_ascii=False)
|
|
||||||
if not options['no_video']:
|
|
||||||
for timeline in timelines:
|
|
||||||
ext = '.mp4'
|
|
||||||
if '-audio.kdenlive' in timeline:
|
|
||||||
ext = '.wav'
|
|
||||||
cmd = [
|
|
||||||
'xvfb-run', '-a',
|
|
||||||
'melt', timeline,
|
|
||||||
'-consumer', 'avformat:%s' % timeline.replace('.kdenlive', ext)
|
|
||||||
]
|
|
||||||
subprocess.call(cmd)
|
|
||||||
if ext == '.wav':
|
|
||||||
cmd = [
|
|
||||||
'ffmpeg', '-i',
|
|
||||||
timeline.replace('.kdenlive', ext),
|
|
||||||
timeline.replace('.kdenlive', '.mp4')
|
|
||||||
]
|
|
||||||
subprocess.call(cmd)
|
|
||||||
os.unlink(timeline.replace('.kdenlive', ext))
|
|
||||||
|
|
||||||
|
|
95
render.py
95
render.py
|
@ -54,10 +54,12 @@ def compose(clips, target=150, base=1024):
|
||||||
}
|
}
|
||||||
all_clips = clips.copy()
|
all_clips = clips.copy()
|
||||||
seq = random(base)
|
seq = random(base)
|
||||||
while target - length > 10 and clips:
|
while target - length > 0 and clips:
|
||||||
clip = random_choice(seq, clips, True)
|
clip = random_choice(seq, clips, True)
|
||||||
if not clips:
|
if not clips:
|
||||||
clips = [c for c in all_clips if c != clip]
|
clips = [c for c in all_clips if c != clip]
|
||||||
|
if not clips:
|
||||||
|
clips = all_clips.copy()
|
||||||
if length + clip['duration'] > target:
|
if length + clip['duration'] > target:
|
||||||
break
|
break
|
||||||
length += clip['duration']
|
length += clip['duration']
|
||||||
|
@ -119,17 +121,24 @@ def compose(clips, target=150, base=1024):
|
||||||
})
|
})
|
||||||
return scene
|
return scene
|
||||||
|
|
||||||
|
def get_scene_duration(scene):
|
||||||
|
duration = 0
|
||||||
|
for key, value in scene.items():
|
||||||
|
for name, clips in value.items():
|
||||||
|
for clip in clips:
|
||||||
|
duration += clip['duration']
|
||||||
|
return duration
|
||||||
|
|
||||||
def render(root, scene, prefix=''):
|
def render(root, scene, prefix=''):
|
||||||
fps = 24
|
fps = 24
|
||||||
files = []
|
files = []
|
||||||
for timeline, data in scene.items():
|
for timeline, data in scene.items():
|
||||||
print(timeline)
|
#print(timeline)
|
||||||
project = KDEnliveProject(root)
|
project = KDEnliveProject(root)
|
||||||
|
|
||||||
tracks = []
|
tracks = []
|
||||||
for track, clips in data.items():
|
for track, clips in data.items():
|
||||||
print(track)
|
#print(track)
|
||||||
for clip in clips:
|
for clip in clips:
|
||||||
project.append_clip(track, clip)
|
project.append_clip(track, clip)
|
||||||
path = os.path.join(root, prefix + "%s.kdenlive" % timeline)
|
path = os.path.join(root, prefix + "%s.kdenlive" % timeline)
|
||||||
|
@ -137,3 +146,83 @@ def render(root, scene, prefix=''):
|
||||||
fd.write(project.to_xml())
|
fd.write(project.to_xml())
|
||||||
files.append(path)
|
files.append(path)
|
||||||
return files
|
return files
|
||||||
|
|
||||||
|
def get_fragments(clips):
|
||||||
|
import itemlist.models
|
||||||
|
fragments = []
|
||||||
|
for l in itemlist.models.List.objects.filter(status='featured').order_by('name'):
|
||||||
|
if l.name.split(' ')[0].isdigit():
|
||||||
|
fragment = {
|
||||||
|
'name': l.name,
|
||||||
|
'tags': [t['value'] for t in l.query['conditions'][1]['conditions']],
|
||||||
|
'description': l.description
|
||||||
|
}
|
||||||
|
fragment['clips'] = []
|
||||||
|
for clip in clips:
|
||||||
|
if set(clip['tags']) & set(fragment['tags']):
|
||||||
|
fragment['clips'].append(clip)
|
||||||
|
fragments.append(fragment)
|
||||||
|
fragments.sort(key=lambda f: ox.sort_string(f['name']))
|
||||||
|
return fragments
|
||||||
|
|
||||||
|
|
||||||
|
def render_all(options):
|
||||||
|
prefix = options['prefix']
|
||||||
|
duration = int(options['duration'])
|
||||||
|
base = int(options['offset'])
|
||||||
|
|
||||||
|
with open(os.path.join(prefix, "clips.json")) as fd:
|
||||||
|
clips = json.load(fd)
|
||||||
|
|
||||||
|
fragments = get_fragments(clips)
|
||||||
|
position = target_position = 0
|
||||||
|
target = fragment_target = duration / len(fragments)
|
||||||
|
base_prefix = os.path.join(prefix, 'render', str(base))
|
||||||
|
for fragment in fragments:
|
||||||
|
n = int(fragment['name'].split(' ')[0])
|
||||||
|
name = fragment['name'].replace(' ', '_')
|
||||||
|
if n < 10:
|
||||||
|
name = '0' + name
|
||||||
|
if not fragment['clips']:
|
||||||
|
print("skipping empty fragment", name)
|
||||||
|
continue
|
||||||
|
fragment_prefix = os.path.join(base_prefix, name)
|
||||||
|
os.makedirs(fragment_prefix, exist_ok=True)
|
||||||
|
|
||||||
|
scene = compose(fragment['clips'], target=target, base=base)
|
||||||
|
scene_duration = get_scene_duration(scene)
|
||||||
|
print("%s %s -> %s (%s)" % (name, target, scene_duration, fragment_target))
|
||||||
|
position += scene_duration
|
||||||
|
target_position += fragment_target
|
||||||
|
if position > target_position:
|
||||||
|
target = fragment_target - (position-target_position)
|
||||||
|
print("adjusting target from", fragment_target, target)
|
||||||
|
elif position < target_position:
|
||||||
|
target = target + 0.1 * fragment_target
|
||||||
|
|
||||||
|
timelines = render(prefix, scene, fragment_prefix[len(prefix) + 1:] + '/')
|
||||||
|
|
||||||
|
with open(os.path.join(fragment_prefix, 'scene.json'), 'w') as fd:
|
||||||
|
json.dump(scene, fd, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
if not options['no_video']:
|
||||||
|
for timeline in timelines:
|
||||||
|
ext = '.mp4'
|
||||||
|
if '-audio.kdenlive' in timeline:
|
||||||
|
ext = '.wav'
|
||||||
|
cmd = [
|
||||||
|
'xvfb-run', '-a',
|
||||||
|
'melt', timeline,
|
||||||
|
'-consumer', 'avformat:%s' % timeline.replace('.kdenlive', ext)
|
||||||
|
]
|
||||||
|
subprocess.call(cmd)
|
||||||
|
if ext == '.wav':
|
||||||
|
cmd = [
|
||||||
|
'ffmpeg', '-i',
|
||||||
|
timeline.replace('.kdenlive', ext),
|
||||||
|
timeline.replace('.kdenlive', '.mp4')
|
||||||
|
]
|
||||||
|
subprocess.call(cmd)
|
||||||
|
os.unlink(timeline.replace('.kdenlive', ext))
|
||||||
|
print("Duration - Target: %s Actual: %s" % (target_position, position))
|
||||||
|
|
||||||
|
|
|
@ -449,7 +449,7 @@ class KDEnliveProject:
|
||||||
filters = clip.get("filter", {})
|
filters = clip.get("filter", {})
|
||||||
frames = int(self._fps * clip['duration'])
|
frames = int(self._fps * clip['duration'])
|
||||||
self._duration[track_id] += frames
|
self._duration[track_id] += frames
|
||||||
print(path, filters)
|
#print(path, filters)
|
||||||
chain = self.get_chain(path)
|
chain = self.get_chain(path)
|
||||||
id = get_propery(chain, "kdenlive:id")
|
id = get_propery(chain, "kdenlive:id")
|
||||||
if track_id == "V1":
|
if track_id == "V1":
|
||||||
|
@ -513,3 +513,5 @@ class KDEnliveProject:
|
||||||
"out": chain.attrib["out"],
|
"out": chain.attrib["out"],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue