From d113c5d79e6d2c1882a44dfd2960798a4f5aabc8 Mon Sep 17 00:00:00 2001 From: j Date: Tue, 10 Oct 2023 16:16:59 +0100 Subject: [PATCH] render all fragments --- .gitignore | 1 + management/commands/render.py | 35 ++----------- render.py | 95 +++++++++++++++++++++++++++++++++-- render_kdenlive.py | 4 +- 4 files changed, 99 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 0d20b64..c9b568f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +*.swp diff --git a/management/commands/render.py b/management/commands/render.py index 655c577..ace0d61 100644 --- a/management/commands/render.py +++ b/management/commands/render.py @@ -5,7 +5,7 @@ import subprocess from django.core.management.base import BaseCommand from django.conf import settings -from ...render import compose, render +from ...render import render_all class Command(BaseCommand): @@ -13,38 +13,9 @@ class Command(BaseCommand): 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('--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('--no-video', action='store_true', dest='no_video', default=False, help='don\'t render video') def handle(self, **options): - prefix = options['prefix'] - 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)) - + render_all(options) diff --git a/render.py b/render.py index 15712d8..d8d1995 100644 --- a/render.py +++ b/render.py @@ -54,10 +54,12 @@ def compose(clips, target=150, base=1024): } all_clips = clips.copy() seq = random(base) - while target - length > 10 and clips: + while target - length > 0 and clips: clip = random_choice(seq, clips, True) if not clips: clips = [c for c in all_clips if c != clip] + if not clips: + clips = all_clips.copy() if length + clip['duration'] > target: break length += clip['duration'] @@ -119,17 +121,24 @@ def compose(clips, target=150, base=1024): }) 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=''): fps = 24 files = [] for timeline, data in scene.items(): - print(timeline) + #print(timeline) project = KDEnliveProject(root) tracks = [] for track, clips in data.items(): - print(track) + #print(track) for clip in clips: project.append_clip(track, clip) path = os.path.join(root, prefix + "%s.kdenlive" % timeline) @@ -137,3 +146,83 @@ def render(root, scene, prefix=''): fd.write(project.to_xml()) files.append(path) 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)) + diff --git a/render_kdenlive.py b/render_kdenlive.py index 5740b32..0bea58c 100644 --- a/render_kdenlive.py +++ b/render_kdenlive.py @@ -449,7 +449,7 @@ class KDEnliveProject: filters = clip.get("filter", {}) frames = int(self._fps * clip['duration']) self._duration[track_id] += frames - print(path, filters) + #print(path, filters) chain = self.get_chain(path) id = get_propery(chain, "kdenlive:id") if track_id == "V1": @@ -513,3 +513,5 @@ class KDEnliveProject: "out": chain.attrib["out"], }), ) + +