Update app.py
Browse files
app.py
CHANGED
|
@@ -9,6 +9,7 @@ import json
|
|
| 9 |
import requests
|
| 10 |
import gc
|
| 11 |
import math
|
|
|
|
| 12 |
from datetime import timedelta
|
| 13 |
|
| 14 |
# --- CRITICAL ENVIRONMENT FIXES ---
|
|
@@ -113,9 +114,11 @@ def generate_cmx_edl(edl_title, segments, source_name, fps=25):
|
|
| 113 |
return "\n".join(edl_lines)
|
| 114 |
|
| 115 |
def generate_xml(sequence_name, segments, source_name, fps=25):
|
| 116 |
-
"""Constructs a Final Cut Pro 7 XML
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
-
# Use append mode to avoid copy-paste line break errors
|
| 119 |
lines = []
|
| 120 |
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
| 121 |
lines.append('<!DOCTYPE xmeml>')
|
|
@@ -123,7 +126,8 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 123 |
lines.append('<sequence>')
|
| 124 |
lines.append(f'\t<name>{sequence_name}</name>')
|
| 125 |
lines.append('\t<rate>')
|
| 126 |
-
lines.append(f'\t\t<timebase>{fps}</timebase>')
|
|
|
|
| 127 |
lines.append('\t</rate>')
|
| 128 |
lines.append('\t<media>')
|
| 129 |
|
|
@@ -131,15 +135,15 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 131 |
lines.append('\t\t<video>')
|
| 132 |
lines.append('\t\t\t<format>')
|
| 133 |
lines.append('\t\t\t\t<samplecharacteristics>')
|
| 134 |
-
lines.append(f'\t\t\t\t\t<rate><timebase>{fps}</timebase></rate>')
|
| 135 |
lines.append('\t\t\t\t\t<width>1920</width>')
|
| 136 |
lines.append('\t\t\t\t\t<height>1080</height>')
|
|
|
|
| 137 |
lines.append('\t\t\t\t\t<pixelaspectratio>square</pixelaspectratio>')
|
| 138 |
lines.append('\t\t\t\t</samplecharacteristics>')
|
| 139 |
lines.append('\t\t\t</format>')
|
| 140 |
lines.append('\t\t\t<track>')
|
| 141 |
|
| 142 |
-
# --- VIDEO LOOP ---
|
| 143 |
timeline_head_frames = 0
|
| 144 |
for i, seg in enumerate(segments, 1):
|
| 145 |
src_in_frames = seconds_to_frames(seg['src_start'], fps)
|
|
@@ -153,18 +157,18 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 153 |
lines.append(f'\t\t\t\t<clipitem id="clipitem-v-{i}">')
|
| 154 |
lines.append(f'\t\t\t\t\t<name>{clip_note}</name>')
|
| 155 |
lines.append(f'\t\t\t\t\t<duration>{duration_frames}</duration>')
|
| 156 |
-
lines.append(f'\t\t\t\t\t<rate><timebase>{fps}</timebase></rate>')
|
| 157 |
lines.append(f'\t\t\t\t\t<start>{tl_start}</start>')
|
| 158 |
lines.append(f'\t\t\t\t\t<end>{tl_end}</end>')
|
| 159 |
lines.append(f'\t\t\t\t\t<in>{src_in_frames}</in>')
|
| 160 |
lines.append(f'\t\t\t\t\t<out>{src_out_frames}</out>')
|
|
|
|
| 161 |
|
| 162 |
-
# File Reference
|
| 163 |
-
lines.append(f'\t\t\t\t\t<file id="
|
| 164 |
lines.append(f'\t\t\t\t\t\t<name>{source_name}</name>')
|
| 165 |
-
lines.append(f'\t\t\t\t\t\t<
|
| 166 |
-
lines.append(f'\t\t\t\t\t\t<
|
| 167 |
-
lines.append(f'\t\t\t\t\t\t<timecode><string>00:00:00:00</string></timecode>')
|
| 168 |
lines.append(f'\t\t\t\t\t</file>')
|
| 169 |
lines.append(f'\t\t\t\t</clipitem>')
|
| 170 |
|
|
@@ -184,9 +188,8 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 184 |
lines.append('\t\t\t\t\t<samplerate>48000</samplerate>')
|
| 185 |
lines.append('\t\t\t\t</samplecharacteristics>')
|
| 186 |
lines.append('\t\t\t</format>')
|
| 187 |
-
lines.append('\t\t\t<track>')
|
| 188 |
|
| 189 |
-
# --- AUDIO LOOP (Identical Timing to Video) ---
|
| 190 |
timeline_head_frames = 0
|
| 191 |
for i, seg in enumerate(segments, 1):
|
| 192 |
src_in_frames = seconds_to_frames(seg['src_start'], fps)
|
|
@@ -200,16 +203,16 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 200 |
lines.append(f'\t\t\t\t<clipitem id="clipitem-a-{i}">')
|
| 201 |
lines.append(f'\t\t\t\t\t<name>{clip_note}</name>')
|
| 202 |
lines.append(f'\t\t\t\t\t<duration>{duration_frames}</duration>')
|
| 203 |
-
lines.append(f'\t\t\t\t\t<rate><timebase>{fps}</timebase></rate>')
|
| 204 |
lines.append(f'\t\t\t\t\t<start>{tl_start}</start>')
|
| 205 |
lines.append(f'\t\t\t\t\t<end>{tl_end}</end>')
|
| 206 |
lines.append(f'\t\t\t\t\t<in>{src_in_frames}</in>')
|
| 207 |
lines.append(f'\t\t\t\t\t<out>{src_out_frames}</out>')
|
|
|
|
| 208 |
|
| 209 |
-
|
| 210 |
-
lines.append(f'\t\t\t\t\t<file id="multicam_file"/>')
|
| 211 |
|
| 212 |
-
# SOURCE TRACK MAPPING
|
| 213 |
lines.append('\t\t\t\t\t<sourcetrack>')
|
| 214 |
lines.append('\t\t\t\t\t\t<mediatype>audio</mediatype>')
|
| 215 |
lines.append('\t\t\t\t\t\t<trackindex>1</trackindex>')
|
|
@@ -276,7 +279,7 @@ st.markdown("""
|
|
| 276 |
* Set your timeline FPS and transcription quality.
|
| 277 |
* Junior Editor will transcribe and separate speakers. You can then instruct it to find engaging bits or construct a narrative.
|
| 278 |
* It will create an EDL or XML to import back into your editing software (Resolve, Premiere).
|
| 279 |
-
* **For Multicam Workflows:** Use the **XML** option in the sidebar and enter the **exact name** of your Multicam Sequence.
|
| 280 |
""")
|
| 281 |
|
| 282 |
st.divider()
|
|
|
|
| 9 |
import requests
|
| 10 |
import gc
|
| 11 |
import math
|
| 12 |
+
import uuid
|
| 13 |
from datetime import timedelta
|
| 14 |
|
| 15 |
# --- CRITICAL ENVIRONMENT FIXES ---
|
|
|
|
| 114 |
return "\n".join(edl_lines)
|
| 115 |
|
| 116 |
def generate_xml(sequence_name, segments, source_name, fps=25):
|
| 117 |
+
"""Constructs a Premiere-Safe Final Cut Pro 7 XML."""
|
| 118 |
+
|
| 119 |
+
# Generate a persistent ID for the source file
|
| 120 |
+
master_id = f"master-{uuid.uuid4()}"
|
| 121 |
|
|
|
|
| 122 |
lines = []
|
| 123 |
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
| 124 |
lines.append('<!DOCTYPE xmeml>')
|
|
|
|
| 126 |
lines.append('<sequence>')
|
| 127 |
lines.append(f'\t<name>{sequence_name}</name>')
|
| 128 |
lines.append('\t<rate>')
|
| 129 |
+
lines.append(f'\t\t<timebase>{int(fps)}</timebase>')
|
| 130 |
+
lines.append(f'\t\t<ntsc>{str(fps % 1 != 0).upper()}</ntsc>')
|
| 131 |
lines.append('\t</rate>')
|
| 132 |
lines.append('\t<media>')
|
| 133 |
|
|
|
|
| 135 |
lines.append('\t\t<video>')
|
| 136 |
lines.append('\t\t\t<format>')
|
| 137 |
lines.append('\t\t\t\t<samplecharacteristics>')
|
| 138 |
+
lines.append(f'\t\t\t\t\t<rate><timebase>{int(fps)}</timebase></rate>')
|
| 139 |
lines.append('\t\t\t\t\t<width>1920</width>')
|
| 140 |
lines.append('\t\t\t\t\t<height>1080</height>')
|
| 141 |
+
lines.append('\t\t\t\t\t<anamorphic>FALSE</anamorphic>')
|
| 142 |
lines.append('\t\t\t\t\t<pixelaspectratio>square</pixelaspectratio>')
|
| 143 |
lines.append('\t\t\t\t</samplecharacteristics>')
|
| 144 |
lines.append('\t\t\t</format>')
|
| 145 |
lines.append('\t\t\t<track>')
|
| 146 |
|
|
|
|
| 147 |
timeline_head_frames = 0
|
| 148 |
for i, seg in enumerate(segments, 1):
|
| 149 |
src_in_frames = seconds_to_frames(seg['src_start'], fps)
|
|
|
|
| 157 |
lines.append(f'\t\t\t\t<clipitem id="clipitem-v-{i}">')
|
| 158 |
lines.append(f'\t\t\t\t\t<name>{clip_note}</name>')
|
| 159 |
lines.append(f'\t\t\t\t\t<duration>{duration_frames}</duration>')
|
| 160 |
+
lines.append(f'\t\t\t\t\t<rate><timebase>{int(fps)}</timebase></rate>')
|
| 161 |
lines.append(f'\t\t\t\t\t<start>{tl_start}</start>')
|
| 162 |
lines.append(f'\t\t\t\t\t<end>{tl_end}</end>')
|
| 163 |
lines.append(f'\t\t\t\t\t<in>{src_in_frames}</in>')
|
| 164 |
lines.append(f'\t\t\t\t\t<out>{src_out_frames}</out>')
|
| 165 |
+
lines.append(f'\t\t\t\t\t<masterclipid>{master_id}</masterclipid>')
|
| 166 |
|
| 167 |
+
# File Reference (Clean for Premiere/Resolve)
|
| 168 |
+
lines.append(f'\t\t\t\t\t<file id="{master_id}">')
|
| 169 |
lines.append(f'\t\t\t\t\t\t<name>{source_name}</name>')
|
| 170 |
+
lines.append(f'\t\t\t\t\t\t<rate><timebase>{int(fps)}</timebase></rate>')
|
| 171 |
+
lines.append(f'\t\t\t\t\t\t<timecode><string>00:00:00:00</string><displayformat>NDF</displayformat></timecode>')
|
|
|
|
| 172 |
lines.append(f'\t\t\t\t\t</file>')
|
| 173 |
lines.append(f'\t\t\t\t</clipitem>')
|
| 174 |
|
|
|
|
| 188 |
lines.append('\t\t\t\t\t<samplerate>48000</samplerate>')
|
| 189 |
lines.append('\t\t\t\t</samplecharacteristics>')
|
| 190 |
lines.append('\t\t\t</format>')
|
| 191 |
+
lines.append('\t\t\t<track>')
|
| 192 |
|
|
|
|
| 193 |
timeline_head_frames = 0
|
| 194 |
for i, seg in enumerate(segments, 1):
|
| 195 |
src_in_frames = seconds_to_frames(seg['src_start'], fps)
|
|
|
|
| 203 |
lines.append(f'\t\t\t\t<clipitem id="clipitem-a-{i}">')
|
| 204 |
lines.append(f'\t\t\t\t\t<name>{clip_note}</name>')
|
| 205 |
lines.append(f'\t\t\t\t\t<duration>{duration_frames}</duration>')
|
| 206 |
+
lines.append(f'\t\t\t\t\t<rate><timebase>{int(fps)}</timebase></rate>')
|
| 207 |
lines.append(f'\t\t\t\t\t<start>{tl_start}</start>')
|
| 208 |
lines.append(f'\t\t\t\t\t<end>{tl_end}</end>')
|
| 209 |
lines.append(f'\t\t\t\t\t<in>{src_in_frames}</in>')
|
| 210 |
lines.append(f'\t\t\t\t\t<out>{src_out_frames}</out>')
|
| 211 |
+
lines.append(f'\t\t\t\t\t<masterclipid>{master_id}</masterclipid>')
|
| 212 |
|
| 213 |
+
lines.append(f'\t\t\t\t\t<file id="{master_id}"/>')
|
|
|
|
| 214 |
|
| 215 |
+
# SOURCE TRACK MAPPING
|
| 216 |
lines.append('\t\t\t\t\t<sourcetrack>')
|
| 217 |
lines.append('\t\t\t\t\t\t<mediatype>audio</mediatype>')
|
| 218 |
lines.append('\t\t\t\t\t\t<trackindex>1</trackindex>')
|
|
|
|
| 279 |
* Set your timeline FPS and transcription quality.
|
| 280 |
* Junior Editor will transcribe and separate speakers. You can then instruct it to find engaging bits or construct a narrative.
|
| 281 |
* It will create an EDL or XML to import back into your editing software (Resolve, Premiere).
|
| 282 |
+
* **For Multicam Workflows:** Use the **XML** option in the sidebar and enter the **exact name** of your Multicam Sequence. Uncheck "**Automatically import source clips into media pool**" when importing into DaVinci Resolve.
|
| 283 |
""")
|
| 284 |
|
| 285 |
st.divider()
|