From ac483b20b95be854bfbadb63659eccc928756959 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sun, 18 Jan 2026 00:22:06 +0100 Subject: [PATCH] Add export_markers.py --- export_markers.py | 103 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 export_markers.py diff --git a/export_markers.py b/export_markers.py new file mode 100644 index 0000000..b095dbd --- /dev/null +++ b/export_markers.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +import sys +import os +import re + +# --- ARCH LINUX PATH FIX --- +module_path = "/opt/resolve/Developer/Scripting/Modules/" +if module_path not in sys.path: + sys.path.append(module_path) + +try: + import DaVinciResolveScript as dvr_script +except ImportError: + print(f"❌ Critical Error: Could not find 'DaVinciResolveScript.py'.") + sys.exit(1) +# --------------------------- + +def export_clip_markers_final(): + try: + resolve = dvr_script.scriptapp("Resolve") + project = resolve.GetProjectManager().GetCurrentProject() + timeline = project.GetCurrentTimeline() + except: + return + + if not timeline: + print("❌ Error: No active timeline found.") + return + + # Base Path + base_output_path = os.path.expanduser("~/Videos/Resolve_Exports") + timeline_name = timeline.GetName() + safe_timeline_name = re.sub(r'[\\/*?:"<>|]', "", timeline_name).replace(" ", "_") + timeline_dir = os.path.join(base_output_path, safe_timeline_name) + + # Get Clips from Video Track 1 + clips = timeline.GetItemListInTrack("video", 1) + if not clips: + print("No clips found.") + return + + job_count = 0 + print(f"Scanning {len(clips)} clips...") + + for clip in clips: + markers = clip.GetMarkers() + if not markers: continue + + # 1. Prepare Folder Name (Clip Name) + clip_name_raw = clip.GetName() + safe_clip_folder = re.sub(r'[\\/*?:"<>|]', "", clip_name_raw) + safe_clip_folder = safe_clip_folder.replace(".mov", "").replace(".mp4", "").replace(".mxf", "").strip() + + clip_target_dir = os.path.join(timeline_dir, safe_clip_folder) + + # Create folder if missing + if not os.path.exists(clip_target_dir): + try: + os.makedirs(clip_target_dir) + except OSError: continue + + # 2. Timing Calculations + clip_start_timeline = clip.GetStart() + clip_left_offset = clip.GetLeftOffset() + + for marker_frame, marker_data in markers.items(): + timeline_frame_pos = (marker_frame - clip_left_offset) + clip_start_timeline + + # 3. Prepare File Name (Marker Name) + raw_name = marker_data['name'] if marker_data['name'] else marker_data['note'] + + if not raw_name: + base_name = f"Frame_{marker_frame}" + else: + base_name = re.sub(r'[\\/*?:"<>|]', "", raw_name).strip() + + # --- THE FIX: Force correct naming --- + # We set UniqueFilenameStyle to 1 (Suffix) but we manually control the name. + # Unfortunately, API limitation: Resolve ALWAYS appends frame number on image sequences. + # The cleanest "trick" is to use the name as is, and accept that Resolve + # adds the frame digits. The previous "underscore" trick is the safest bet. + + final_filename = base_name + "_" + + # 4. Add to Render Queue + project.SetRenderSettings({ + "SelectAllFrames": False, + "MarkIn": timeline_frame_pos, + "MarkOut": timeline_frame_pos, + "TargetDir": clip_target_dir, + "CustomName": final_filename, + "UniqueFilenameStyle": 0 + }) + + project.AddRenderJob() + job_count += 1 + print(f"✅ Queued: {safe_clip_folder}/{final_filename}[####].ext") + + print("-" * 30) + print(f"🎉 Queued {job_count} stills.") + +if __name__ == "__main__": + export_clip_markers_final() \ No newline at end of file