Update app.py
Browse files
app.py
CHANGED
|
@@ -12,7 +12,6 @@ import math
|
|
| 12 |
import uuid
|
| 13 |
import re
|
| 14 |
from datetime import timedelta
|
| 15 |
-
from urllib.parse import quote
|
| 16 |
|
| 17 |
# --- CRITICAL ENVIRONMENT FIXES ---
|
| 18 |
if os.environ.get("OMP_NUM_THREADS", "").endswith("m"):
|
|
@@ -127,7 +126,11 @@ def generate_cmx_edl(edl_title, segments, source_name, fps=25):
|
|
| 127 |
return "\n".join(edl_lines)
|
| 128 |
|
| 129 |
def generate_xml(sequence_name, segments, source_name, fps=25):
|
| 130 |
-
"""Constructs a
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
# FPS Logic
|
| 133 |
timebase = int(round(fps))
|
|
@@ -168,10 +171,9 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 168 |
tl_end = tl_start + duration_frames
|
| 169 |
|
| 170 |
raw_note = seg.get('note', 'Junior Editor Selection')
|
| 171 |
-
# Truncate for safety
|
| 172 |
clip_note = (raw_note[:75] + '..') if len(raw_note) > 75 else raw_note
|
| 173 |
|
| 174 |
-
# Unique ID for
|
| 175 |
clip_id = f"clip-v-{i}-{uuid.uuid4()}"
|
| 176 |
|
| 177 |
lines.append(f'\t\t\t\t<clipitem id="{clip_id}">')
|
|
@@ -183,9 +185,9 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 183 |
lines.append(f'\t\t\t\t\t<in>{src_in_frames}</in>')
|
| 184 |
lines.append(f'\t\t\t\t\t<out>{src_out_frames}</out>')
|
| 185 |
|
| 186 |
-
# ---
|
| 187 |
-
#
|
| 188 |
-
lines.append(f'\t\t\t\t\t<file id="
|
| 189 |
lines.append(f'\t\t\t\t\t\t<name>{source_name}</name>')
|
| 190 |
lines.append(f'\t\t\t\t\t\t<rate><timebase>{timebase}</timebase></rate>')
|
| 191 |
lines.append(f'\t\t\t\t\t\t<duration>8640000</duration>')
|
|
@@ -195,7 +197,6 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 195 |
lines.append(f'\t\t\t\t\t\t\t<frame>0</frame>')
|
| 196 |
lines.append(f'\t\t\t\t\t\t\t<displayformat>NDF</displayformat>')
|
| 197 |
lines.append(f'\t\t\t\t\t\t</timecode>')
|
| 198 |
-
# Reel name helps Resolve link if file name fails
|
| 199 |
lines.append(f'\t\t\t\t\t\t<reel><name>{source_name}</name></reel>')
|
| 200 |
lines.append(f'\t\t\t\t\t\t<media>')
|
| 201 |
lines.append(f'\t\t\t\t\t\t\t<video>')
|
|
@@ -207,7 +208,7 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 207 |
lines.append(f'\t\t\t\t\t\t\t</audio>')
|
| 208 |
lines.append(f'\t\t\t\t\t\t</media>')
|
| 209 |
lines.append(f'\t\t\t\t\t</file>')
|
| 210 |
-
# -------------------------------
|
| 211 |
|
| 212 |
lines.append(f'\t\t\t\t</clipitem>')
|
| 213 |
|
|
@@ -234,7 +235,7 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 234 |
raw_note = seg.get('note', 'Junior Editor Selection')
|
| 235 |
clip_note = (raw_note[:75] + '..') if len(raw_note) > 75 else raw_note
|
| 236 |
|
| 237 |
-
# Unique ID for audio clip
|
| 238 |
clip_id = f"clip-a-{i}-{uuid.uuid4()}"
|
| 239 |
|
| 240 |
lines.append(f'\t\t\t\t<clipitem id="{clip_id}">')
|
|
@@ -246,8 +247,8 @@ def generate_xml(sequence_name, segments, source_name, fps=25):
|
|
| 246 |
lines.append(f'\t\t\t\t\t<in>{src_in_frames}</in>')
|
| 247 |
lines.append(f'\t\t\t\t\t<out>{src_out_frames}</out>')
|
| 248 |
|
| 249 |
-
#
|
| 250 |
-
lines.append(f'\t\t\t\t\t<file id="
|
| 251 |
lines.append(f'\t\t\t\t\t\t<name>{source_name}</name>')
|
| 252 |
lines.append(f'\t\t\t\t\t\t<rate><timebase>{timebase}</timebase></rate>')
|
| 253 |
lines.append(f'\t\t\t\t\t\t<duration>8640000</duration>')
|
|
@@ -340,7 +341,7 @@ st.markdown("""
|
|
| 340 |
* It will create an EDL or XML to import back into your editing software (Resolve, Premiere).
|
| 341 |
* **For Multicam Workflows:** Use the **XML** option in the sidebar and enter the **exact name** of your Multicam Sequence.
|
| 342 |
* **Resolve Users:** Uncheck "Automatically import source clips into media pool" during import.
|
| 343 |
-
* **Premiere Users:** Import the XML.
|
| 344 |
""")
|
| 345 |
|
| 346 |
st.divider()
|
|
|
|
| 12 |
import uuid
|
| 13 |
import re
|
| 14 |
from datetime import timedelta
|
|
|
|
| 15 |
|
| 16 |
# --- CRITICAL ENVIRONMENT FIXES ---
|
| 17 |
if os.environ.get("OMP_NUM_THREADS", "").endswith("m"):
|
|
|
|
| 126 |
return "\n".join(edl_lines)
|
| 127 |
|
| 128 |
def generate_xml(sequence_name, segments, source_name, fps=25):
|
| 129 |
+
"""Constructs a Robust XML with Unified Source for Premiere/Resolve."""
|
| 130 |
+
|
| 131 |
+
# CONSTANT ID for the source file.
|
| 132 |
+
# This ensures Premiere creates only ONE offline clip in the bin.
|
| 133 |
+
unified_source_id = "source-file-1"
|
| 134 |
|
| 135 |
# FPS Logic
|
| 136 |
timebase = int(round(fps))
|
|
|
|
| 171 |
tl_end = tl_start + duration_frames
|
| 172 |
|
| 173 |
raw_note = seg.get('note', 'Junior Editor Selection')
|
|
|
|
| 174 |
clip_note = (raw_note[:75] + '..') if len(raw_note) > 75 else raw_note
|
| 175 |
|
| 176 |
+
# Unique ID for the CLIP on the timeline
|
| 177 |
clip_id = f"clip-v-{i}-{uuid.uuid4()}"
|
| 178 |
|
| 179 |
lines.append(f'\t\t\t\t<clipitem id="{clip_id}">')
|
|
|
|
| 185 |
lines.append(f'\t\t\t\t\t<in>{src_in_frames}</in>')
|
| 186 |
lines.append(f'\t\t\t\t\t<out>{src_out_frames}</out>')
|
| 187 |
|
| 188 |
+
# --- UNIFIED FILE DEFINITION ---
|
| 189 |
+
# Same ID every time ("source-file-1")
|
| 190 |
+
lines.append(f'\t\t\t\t\t<file id="{unified_source_id}">')
|
| 191 |
lines.append(f'\t\t\t\t\t\t<name>{source_name}</name>')
|
| 192 |
lines.append(f'\t\t\t\t\t\t<rate><timebase>{timebase}</timebase></rate>')
|
| 193 |
lines.append(f'\t\t\t\t\t\t<duration>8640000</duration>')
|
|
|
|
| 197 |
lines.append(f'\t\t\t\t\t\t\t<frame>0</frame>')
|
| 198 |
lines.append(f'\t\t\t\t\t\t\t<displayformat>NDF</displayformat>')
|
| 199 |
lines.append(f'\t\t\t\t\t\t</timecode>')
|
|
|
|
| 200 |
lines.append(f'\t\t\t\t\t\t<reel><name>{source_name}</name></reel>')
|
| 201 |
lines.append(f'\t\t\t\t\t\t<media>')
|
| 202 |
lines.append(f'\t\t\t\t\t\t\t<video>')
|
|
|
|
| 208 |
lines.append(f'\t\t\t\t\t\t\t</audio>')
|
| 209 |
lines.append(f'\t\t\t\t\t\t</media>')
|
| 210 |
lines.append(f'\t\t\t\t\t</file>')
|
| 211 |
+
# -------------------------------
|
| 212 |
|
| 213 |
lines.append(f'\t\t\t\t</clipitem>')
|
| 214 |
|
|
|
|
| 235 |
raw_note = seg.get('note', 'Junior Editor Selection')
|
| 236 |
clip_note = (raw_note[:75] + '..') if len(raw_note) > 75 else raw_note
|
| 237 |
|
| 238 |
+
# Unique ID for audio clip
|
| 239 |
clip_id = f"clip-a-{i}-{uuid.uuid4()}"
|
| 240 |
|
| 241 |
lines.append(f'\t\t\t\t<clipitem id="{clip_id}">')
|
|
|
|
| 247 |
lines.append(f'\t\t\t\t\t<in>{src_in_frames}</in>')
|
| 248 |
lines.append(f'\t\t\t\t\t<out>{src_out_frames}</out>')
|
| 249 |
|
| 250 |
+
# REUSE UNIFIED SOURCE ID
|
| 251 |
+
lines.append(f'\t\t\t\t\t<file id="{unified_source_id}">')
|
| 252 |
lines.append(f'\t\t\t\t\t\t<name>{source_name}</name>')
|
| 253 |
lines.append(f'\t\t\t\t\t\t<rate><timebase>{timebase}</timebase></rate>')
|
| 254 |
lines.append(f'\t\t\t\t\t\t<duration>8640000</duration>')
|
|
|
|
| 341 |
* It will create an EDL or XML to import back into your editing software (Resolve, Premiere).
|
| 342 |
* **For Multicam Workflows:** Use the **XML** option in the sidebar and enter the **exact name** of your Multicam Sequence.
|
| 343 |
* **Resolve Users:** Uncheck "Automatically import source clips into media pool" during import.
|
| 344 |
+
* **Premiere Users:** Import the XML. A single "Offline" clip will appear in your bin. Hold **Alt (Win) / Option (Mac)** and drag your Multicam Sequence from the bin onto this offline clip to link the entire timeline instantly.
|
| 345 |
""")
|
| 346 |
|
| 347 |
st.divider()
|