Render "Timeline Marks"

  • Author
  • Message
Offline

exworth

  • Posts: 6
  • Joined: Thu Jan 30, 2025 7:01 pm
  • Real Name: Andrew Exworth

Render "Timeline Marks"

PostFri Mar 07, 2025 8:32 pm

In the render page, you have Render: "Entire Timeline" and "In/Out Range"

It would be a nice little thing to include Render "Timeline Marks" for stills

I often do B&A stills during the colour process it would be nice to have a quick way to render out all of the same stills as client changes are being made. Instead of having to render out each still separately each time we do a posting.
Offline

Jim Simon

  • Posts: 34714
  • Joined: Fri Dec 23, 2016 1:47 am

Re: Render "Timeline Marks"

PostSun Mar 09, 2025 3:24 pm

Would not exporting Gallery still serve this purpose?
My Biases:

You NEED training.
You NEED a desktop.
You NEED a calibrated (non-computer) display.
Offline

jjsawdon

  • Posts: 296
  • Joined: Fri Apr 08, 2022 9:19 am
  • Real Name: Jeff Sawdon

Re: Render "Timeline Marks"

PostSun Mar 09, 2025 3:40 pm

I think there's a script someone made that does something like this. Don't have a link offhand, but for stills, that's a great option.

Would not be opposed to something like this for ranges either - I often use, say, Yellow range markers if I need to render something into a DSM. Saves putting In/Outs over those ranges.
Online Editor & Scripter
GitHub: https://github.com/jjsawdon
IMDb: https://www.imdb.com/name/nm5407671
System Specs: Mac Pro (Late 2013)
  • macOS Monterey 12.7.5
  • 3.5 GHz 6-Core Intel Xeon E5
  • 64 GB 1866 MHz DDR3
  • AMD FirePro D500 3 GB
Offline

exworth

  • Posts: 6
  • Joined: Thu Jan 30, 2025 7:01 pm
  • Real Name: Andrew Exworth

Re: Render "Timeline Marks"

PostMon Mar 10, 2025 7:06 pm

Jim Simon wrote:Would not exporting Gallery still serve this purpose?



This is how we currently do it, it becomes a little cumbersome when there is a lot of back and forth with clients. "After_V02, After_V03 etc.." Each round of postings you have to go to each shot to grab a new still then export that new gallery folder. It would be nice to just check "Render timeline marks" in the render page and render the new stills into a new folder "After_V02"

From these stills we build a little wipe link thing where clients can grab a wipe bar and control their own before and after screen.
Offline

Andy Mees

  • Posts: 3938
  • Joined: Wed Aug 22, 2012 7:48 am

Re: Render "Timeline Marks"

PostMon Mar 10, 2025 11:22 pm

jjsawdon wrote:there's a script
Yep, it's here... generously shared by Roger Magnusson
Offline
User avatar

Norbert339

  • Posts: 991
  • Joined: Sun Mar 27, 2022 2:36 pm
  • Location: Hungary
  • Real Name: Norbert Zsolt Szabo

Re: Render "Timeline Marks"

PostThu Mar 13, 2025 9:42 am

I have a script to add to render queue all duration makers on a timeline with their names and use the selected render preset.

import os
import re
import tkinter as tk
from tkinter import scrolledtext, filedialog, messagebox
import threading
import DaVinciResolveScript as dvr

def sanitize_filename(filename):
"""Sanitizes a filename to remove invalid characters for Windows."""
return re.sub(r'[<>:"/\\|?*]', '_', filename)

class RenderQueueApp:
def __init__(self, master):
self.master = master
self.master.title("Render Queue Manager")
self.master.attributes('-topmost', True) # Keep the window on top

self.timeline_name_var = tk.StringVar()
self.export_path_var = tk.StringVar()
self.is_rendering = False # Flag to control rendering process
self.markers = {} # Store markers to restart from the beginning

# Initialize Resolve connection
self.resolve = dvr.scriptapp("Resolve")
if not self.resolve:
self.log("Error: Could not connect to DaVinci Resolve.")
messagebox.showerror("Connection Error", "Could not connect to DaVinci Resolve.")
return

self.project_manager = self.resolve.GetProjectManager()
self.current_project = self.project_manager.GetCurrentProject()

# Create UI elements
self.create_widgets()

def create_widgets(self):
# Timeline name input
tk.Label(self.master, text="Enter Timeline Name:").grid(row=0, column=0, padx=5, pady=5, sticky='e')
tk.Entry(self.master, textvariable=self.timeline_name_var, width=40).grid(row=0, column=1, padx=5, pady=5)

# Find Timeline button
tk.Button(self.master, text="Find Timeline", command=self.find_timeline).grid(row=0, column=2, padx=5, pady=5)

# Export path input
tk.Label(self.master, text="Enter Export Path:").grid(row=1, column=0, padx=5, pady=5, sticky='e')
tk.Entry(self.master, textvariable=self.export_path_var, width=40).grid(row=1, column=1, padx=5, pady=5)

# Browse button
tk.Button(self.master, text="Browse", command=self.browse_path).grid(row=1, column=2, padx=5, pady=5)

# Find Path button
tk.Button(self.master, text="Find Path", command=self.find_path).grid(row=2, column=2, padx=5, pady=5)

# Start and Stop buttons
tk.Button(self.master, text="Start", command=self.start_rendering, width=10).grid(row=3, column=0, padx=5, pady=10)
tk.Button(self.master, text="Stop", command=self.stop_rendering, width=10).grid(row=3, column=1, padx=5, pady=10)

# Clear All button
tk.Button(self.master, text="Clear All", command=self.clear_all, width=10).grid(row=3, column=2, padx=5, pady=10)

# Log window
self.log_window = scrolledtext.ScrolledText(self.master, width=80, height=20, state='disabled')
self.log_window.grid(row=4, column=0, columnspan=3, padx=5, pady=5)

def log(self, message):
"""Log messages to the log window."""
self.log_window.configure(state='normal')
self.log_window.insert(tk.END, message + "\n")
self.log_window.configure(state='disabled')
self.log_window.yview(tk.END) # Auto-scroll to the bottom

def browse_path(self):
"""Open a dialog to browse for the export directory."""
selected_dir = filedialog.askdirectory()
if selected_dir:
self.export_path_var.set(selected_dir)
if self.validate_path(selected_dir):
self.log(f"Selected export path: {selected_dir}")
else:
self.log(f"Warning: The selected path '{selected_dir}' is invalid or does not exist.")
messagebox.showwarning("Invalid Path", f"The selected path '{selected_dir}' is invalid or does not exist.")

def find_path(self):
"""Validate the entered export path."""
path = self.export_path_var.get().strip()
if not path:
self.log("Error: Export path cannot be empty.")
messagebox.showerror("Input Error", "Export path cannot be empty.")
return

if self.validate_path(path):
self.log(f"Export path '{path}' is valid.")
messagebox.showinfo("Path Validated", f"The export path '{path}' is valid.")
else:
self.log(f"Warning: The export path '{path}' is invalid or does not exist.")
messagebox.showwarning("Invalid Path", f"The export path '{path}' is invalid or does not exist.")

def validate_path(self, path):
"""Validate if the provided path exists and is a directory."""
return os.path.isdir(path)

def find_timeline(self):
"""Check if the specified timeline exists."""
timeline_name = sanitize_filename(self.timeline_name_var.get()).strip()
export_path = self.export_path_var.get().strip()

if not timeline_name:
self.log("Error: Timeline name cannot be empty.")
messagebox.showerror("Input Error", "Timeline name cannot be empty.")
return

if not export_path:
self.log("Error: Export path cannot be empty.")
messagebox.showerror("Input Error", "Export path cannot be empty.")
return

if not self.validate_path(export_path):
self.log(f"Warning: The export path '{export_path}' is invalid or does not exist.")
messagebox.showwarning("Invalid Path", f"The export path '{export_path}' is invalid or does not exist.")
return

if not self.current_project:
self.log("Error: No project is currently open.")
messagebox.showerror("Project Error", "No project is currently open.")
return

# Get all timelines and check if the specified one exists
timeline_count = self.current_project.GetTimelineCount()

found_timeline = False
for idx in range(1, timeline_count + 1):
timeline = self.current_project.GetTimelineByIndex(idx)
if timeline and timeline.GetName() == timeline_name:
found_timeline = True
self.current_timeline = timeline
self.markers = timeline.GetMarkers() # Store markers for rendering later
self.log(f"Timeline '{timeline_name}' has been successfully found.")
messagebox.showinfo("Timeline Found", f"Timeline '{timeline_name}' has been successfully found.")
break

if not found_timeline:
self.log(f"Warning: No timeline found with the name '{timeline_name}'.")
messagebox.showwarning("Timeline Not Found", f"No timeline found with the name '{timeline_name}'.")

def start_rendering(self):
"""Start adding render jobs based on duration markers."""
timeline_name = self.timeline_name_var.get().strip()
export_path = self.export_path_var.get().strip()

if not hasattr(self, 'current_timeline'):
self.log("Warning: Please find a timeline first.")
messagebox.showwarning("Timeline Not Found", "Please find a timeline first.")
return

if not self.markers:
self.log("No duration markers found in the timeline.")
messagebox.showinfo("No Markers", "No duration markers found in the timeline.")
return

if not self.validate_path(export_path):
self.log(f"Error: The export path '{export_path}' is invalid or does not exist.")
messagebox.showerror("Invalid Path", f"The export path '{export_path}' is invalid or does not exist.")
return

if not self.is_rendering: # Only start if not already rendering
self.is_rendering = True # Set rendering flag
self.log("Rendering process has started.")

# Start rendering in a separate thread
threading.Thread(target=self.render_jobs, daemon=True).start()
else:
self.log("Info: Rendering is already in progress.")
messagebox.showinfo("Rendering In Progress", "Rendering is already in progress.")

def render_jobs(self):
"""Render jobs based on duration markers."""
export_path = self.export_path_var.get().strip()
try:
for marker_timecode, marker_data in self.markers.items():
if not self.is_rendering: # Check if we should stop rendering
break

if 'duration' not in marker_data or marker_data['duration'] <= 0:
self.log(f"Skipping marker at {marker_timecode}: Invalid or zero duration.")
continue

marker_name = marker_data.get('name', 'Unnamed_Marker')
sanitized_marker_name = sanitize_filename(marker_name) # No .mp4 appended

start_time = marker_timecode
duration_frames = marker_data['duration']
end_time = start_time + duration_frames

render_settings = {
"SelectAllFrames": False,
"MarkIn": start_time,
"MarkOut": end_time,
"TargetDir": export_path,
"CustomName": sanitized_marker_name,
"renderPreset": "YT_EU_DEF"
}

if not self.current_project.SetRenderSettings(render_settings):
self.log(f"Error: Failed to set render settings for marker: {marker_name}")
continue

job_id = self.current_project.AddRenderJob()

if job_id is None:
self.log(f"Failed to add render job for marker: {marker_name}")
else:
self.log(f"Added render job for marker: {marker_name} with job ID: {job_id}")

if not self.is_rendering:
# If rendering was stopped before completion
self.log("Rendering process was stopped before completion.")
messagebox.showinfo("Rendering Stopped", "Rendering process was stopped before completion.")
else:
self.log("All valid markers have been added to the Render Queue.")
messagebox.showinfo("Rendering Complete", "All valid markers have been added to the Render Queue.")

except Exception as e:
self.log(f"Error occurred during rendering: {str(e)}")
messagebox.showerror("Rendering Error", f"An error occurred during rendering: {str(e)}")
self.is_rendering = False # Ensure we stop rendering on error
finally:
self.is_rendering = False # Reset rendering flag after completion or error

def stop_rendering(self):
"""Stop rendering process."""
if not self.is_rendering:
self.log("Info: Rendering is already stopped.")
messagebox.showinfo("Rendering Stopped", "Rendering is already stopped.")
return

try:
# Set rendering flag to False and log stopping action.
self.is_rendering = False

# Log stopping action.
self.log("Rendering process has been stopped.")
messagebox.showinfo("Rendering Stopped", "Rendering process has been stopped.")

except Exception as e:
self.log(f"Error occurred while stopping: {str(e)}")
messagebox.showerror("Stop Error", f"An error occurred while stopping rendering: {str(e)}")

def clear_all(self):
"""Clear all input fields and the log window."""
if self.is_rendering:
response = messagebox.askyesno("Confirm Clear", "Rendering is in progress. Do you want to stop rendering and clear all fields?")
if response:
self.stop_rendering()
else:
return

self.timeline_name_var.set("")
self.export_path_var.set("")
self.markers = {}
self.current_timeline = None
self.log_window.configure(state='normal')
self.log_window.delete('1.0', tk.END)
self.log_window.configure(state='disabled')
self.log("All fields have been cleared.")
messagebox.showinfo("Cleared", "All fields have been cleared.")

if __name__ == "__main__":
root = tk.Tk()
app = RenderQueueApp(root)
root.mainloop()
Offline

exworth

  • Posts: 6
  • Joined: Thu Jan 30, 2025 7:01 pm
  • Real Name: Andrew Exworth

Re: Render "Timeline Marks"

PostThu Mar 13, 2025 3:56 pm

Andy Mees wrote:
jjsawdon wrote:there's a script
Yep, it's here... generously shared by Roger Magnusson



This will do the job.

Thanks again Andy!

Return to DaVinci Resolve Feature Requests

Who is online

Users browsing this forum: No registered users and 22 guests