diff --git a/render.py b/render.py index e62f513..df5c600 100644 --- a/render.py +++ b/render.py @@ -15,40 +15,7 @@ import lxml.etree from .pi import random from .render_kdenlive import KDEnliveProject, _CACHE, melt_xml, get_melt - - -def random_int(seq, length): - n = n_ = length - 1 - #print('len', n) - if n == 0: - return n - r = seq() / 9 * 10 - base = 10 - while n > 10: - n /= 10 - r += seq() / 9 * 10 - base += 10 - r = int(round(n_ * r / base)) - return r - -def random_choice(seq, items, pop=False): - n = random_int(seq, len(items)) - if pop: - return items.pop(n) - return items[n] - -def chance(seq, chance): - return (seq() / 10) < chance - -def get_clip_by_seqid(clips, seqid): - selected = None - for i, clip in enumerate(clips): - if clip['seqid'] == seqid: - selected = i - break - if selected is not None: - return clips.pop(i) - return None +from .render_utils import * def write_if_new(path, data, mode=''): @@ -342,35 +309,6 @@ def compose(clips, target=150, base=1024, voice_over=None, options=None): print(scene['audio-center']['A1'][-1]) return scene, used -def get_track_duration(scene, k, n): - duration = 0 - for key, value in scene.items(): - if key == k: - for name, clips in value.items(): - if name == n: - for clip in clips: - duration += int(clip['duration'] * 24) - return duration / 24 - -def get_scene_duration(scene): - if isinstance(scene, str): - with open(scene) as fd: - scene = json.load(fd) - duration = 0 - for key, value in scene.items(): - for name, clips in value.items(): - for clip in clips: - duration += int(clip['duration'] * 24) - return duration / 24 - -def get_offset_duration(prefix): - duration = 0 - for root, folders, files in os.walk(prefix): - for f in files: - if f == 'scene.json': - duration += get_scene_duration(scene) - return duration - def write_subtitles(data, folder, options): data = fix_overlaps(data) path = folder / "front.srt" @@ -439,12 +377,6 @@ def render(root, scene, prefix='', options=None): files.append(path) return files -def get_project_duration(file): - out = melt_xml(file) - chain = lxml.etree.fromstring(out).xpath('producer')[0] - duration = int(chain.attrib['out']) + 1 - return duration - def get_fragments(clips, voice_over, prefix): import itemlist.models import item.models @@ -496,19 +428,6 @@ def get_fragments(clips, voice_over, prefix): fragments.sort(key=lambda f: ox.sort_string(f['name'])) return fragments -def parse_lang(lang): - if lang and "," in lang: - lang = lang.split(',') - if isinstance(lang, list): - tlang = lang[1:] - lang = lang[0] - else: - tlang = None - if lang == "en": - lang = None - return lang, tlang - - def render_all(options): options = load_defaults(options) prefix = options['prefix'] @@ -861,23 +780,6 @@ def get_srt(sub, offset, lang, tlang): sdata["out"] += offset return sdata -def fix_overlaps(data): - previous = None - for sub in data: - if previous is None: - previous = sub - else: - if sub['in'] < previous['out']: - previous['out'] = sub['in'] - 0.001 - previous = sub - return data - -def shift_clips(data, offset): - for clip in data: - clip['in'] += offset - clip['out'] += offset - return data - def scene_subtitles(scene, options): import item.models offset = 0 @@ -939,57 +841,6 @@ def update_subtitles(options): subs = scene_subtitles(scene, options) write_subtitles(subs, folder, options) -def ass_encode(subs, options): - if "lang" in options: - langs = options["lang"].split(',') - else: - langs = list(subs[0]["values"]) - #print('ass_encode', langs, options) - #print(subs) - - header = '''[Script Info] -ScriptType: v4.00+ -PlayResX: 1920 -PlayResY: 1080 -ScaledBorderAndShadow: yes -YCbCr Matrix: None - -[V4+ Styles] -Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding -''' - ass = header - offset = options.get("sub_margin", 10) - spacing = options.get("sub_spacing", 20) - height = 42 - styles = [] - for lang in reversed(langs): - if isinstance(options.get("font"), list) and lang in options["font"]: - font = options["font"][lang] - else: - font = 'SimHei' if lang in ('zh', 'jp') else 'Menlo' - if isinstance(options.get("font_size"), list) and lang in options["font_size"]: - size = options["font_size"][lang] - else: - size = 46 if font == 'SimHei' else 42 - - styles.append( - f'Style: {lang},{font},{size},&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,{offset},1' - ) - offset += size + spacing - ass += '\n'.join(reversed(styles)) + '\n' - events = [ - 'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text' - ] - for sub in subs: - start = ox.format_timecode(sub['in']).rstrip('0') - stop = ox.format_timecode(sub['out']).rstrip('0') - for lang in reversed(langs): - value = sub['values'][lang] - event = f'Dialogue: 0,{start},{stop},{lang},,0,0,0,,{value}' - events.append(event) - ass += '\n\n[Events]\n' + '\n'.join(events) + '\n' - return ass - def update_m3u(render_prefix, exclude=[]): files = ox.sorted_strings(glob(render_prefix + "*/*/back.mp4")) for ex in exclude: diff --git a/render_utils.py b/render_utils.py new file mode 100644 index 0000000..ed3d2c6 --- /dev/null +++ b/render_utils.py @@ -0,0 +1,154 @@ +import re +import os + +import lxml.etree + +from .render_kdenlive import melt_xml + +def parse_lang(lang): + if lang and "," in lang: + lang = lang.split(',') + if isinstance(lang, list): + tlang = lang[1:] + lang = lang[0] + else: + tlang = None + if lang == "en": + lang = None + return lang, tlang + +def random_int(seq, length): + n = n_ = length - 1 + #print('len', n) + if n == 0: + return n + r = seq() / 9 * 10 + base = 10 + while n > 10: + n /= 10 + r += seq() / 9 * 10 + base += 10 + r = int(round(n_ * r / base)) + return r + +def random_choice(seq, items, pop=False): + n = random_int(seq, len(items)) + if pop: + return items.pop(n) + return items[n] + +def chance(seq, chance): + return (seq() / 10) < chance + +def get_clip_by_seqid(clips, seqid): + selected = None + for i, clip in enumerate(clips): + if clip['seqid'] == seqid: + selected = i + break + if selected is not None: + return clips.pop(i) + return None + +def get_scene_duration(scene): + if isinstance(scene, str): + with open(scene) as fd: + scene = json.load(fd) + duration = 0 + for key, value in scene.items(): + for name, clips in value.items(): + for clip in clips: + duration += int(clip['duration'] * 24) + return duration / 24 + +def get_offset_duration(prefix): + duration = 0 + for root, folders, files in os.walk(prefix): + for f in files: + if f == 'scene.json': + duration += get_scene_duration(scene) + return duration + +def get_track_duration(scene, k, n): + duration = 0 + for key, value in scene.items(): + if key == k: + for name, clips in value.items(): + if name == n: + for clip in clips: + duration += int(clip['duration'] * 24) + return duration / 24 + +def get_project_duration(file): + out = melt_xml(file) + chain = lxml.etree.fromstring(out).xpath('producer')[0] + duration = int(chain.attrib['out']) + 1 + return duration + +def fix_overlaps(data): + previous = None + for sub in data: + if previous is None: + previous = sub + else: + if sub['in'] < previous['out']: + previous['out'] = sub['in'] - 0.001 + previous = sub + return data + +def shift_clips(data, offset): + for clip in data: + clip['in'] += offset + clip['out'] += offset + return data + +def ass_encode(subs, options): + if "lang" in options: + langs = options["lang"].split(',') + else: + langs = list(subs[0]["values"]) + #print('ass_encode', langs, options) + #print(subs) + + header = '''[Script Info] +ScriptType: v4.00+ +PlayResX: 1920 +PlayResY: 1080 +ScaledBorderAndShadow: yes +YCbCr Matrix: None + +[V4+ Styles] +Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding +''' + ass = header + offset = options.get("sub_margin", 10) + spacing = options.get("sub_spacing", 20) + height = 42 + styles = [] + for lang in reversed(langs): + if isinstance(options.get("font"), list) and lang in options["font"]: + font = options["font"][lang] + else: + font = 'SimHei' if lang in ('zh', 'jp') else 'Menlo' + if isinstance(options.get("font_size"), list) and lang in options["font_size"]: + size = options["font_size"][lang] + else: + size = 46 if font == 'SimHei' else 42 + + styles.append( + f'Style: {lang},{font},{size},&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,{offset},1' + ) + offset += size + spacing + ass += '\n'.join(reversed(styles)) + '\n' + events = [ + 'Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text' + ] + for sub in subs: + start = ox.format_timecode(sub['in']).rstrip('0') + stop = ox.format_timecode(sub['out']).rstrip('0') + for lang in reversed(langs): + value = sub['values'][lang] + event = f'Dialogue: 0,{start},{stop},{lang},,0,0,0,,{value}' + events.append(event) + ass += '\n\n[Events]\n' + '\n'.join(events) + '\n' + return ass