103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
#!/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() |