1#!/usr/bin/env python3
2import subprocess
3import os
4import sys
5import re
6import glob
7import pyperclip
8import time
9
10def run_command(cmd, shell=False):
11 """Run a command and return its output, exit on failure"""
12 try:
13 if shell:
14 result = subprocess.run(cmd, shell=True, check=True, text=True, capture_output=True)
15 else:
16 result = subprocess.run(cmd, check=True, text=True, capture_output=True)
17 return result.stdout.strip()
18 except subprocess.CalledProcessError as e:
19 print(f"Error executing command: {cmd}")
20 print(f"Error message: {e.stderr}")
21 sys.exit(1)
22 except Exception as e:
23 print(f"Unexpected error running command: {e}")
24 sys.exit(1)
25
26def find_file(base_name):
27 """Find a file with the given base name in the specified directory structure"""
28 search_path = "/Volumes/Project/*/Film/**/*"
29
30 try:
31 # Expand the glob pattern to find all matching files
32 matching_files = []
33 for project_dir in glob.glob("/Volumes/Project/*/"):
34 for root, dirs, files in os.walk(os.path.join(project_dir, "Film")):
35 # Skip hidden directories
36 dirs[:] = [d for d in dirs if not d.startswith('.')]
37 for file in files:
38 if file == base_name and not file.startswith('.'):
39 matching_files.append(os.path.join(root, file))
40
41 if not matching_files:
42 print(f"Error: Could not find file '{base_name}' in {search_path}")
43 sys.exit(1)
44 elif len(matching_files) > 1:
45 print(f"Warning: Found multiple matches for '{base_name}'. Using the first one.")
46
47 return matching_files[0]
48 except Exception as e:
49 print(f"Error searching for file: {e}")
50 sys.exit(1)
51
52def activate_app(app_name):
53 """Activate an application by name"""
54 try:
55 script = f'tell application "{app_name}" to activate'
56 subprocess.run(["osascript", "-e", script], check=True)
57 except Exception as e:
58 print(f"Error activating {app_name}: {e}")
59 sys.exit(1)
60
61def main():
62 # Step 1: Run Apple Script to get frame and name from QuickTime Player
63 print("Step 1: Getting frame and name from QuickTime Player...")
64 applescript = '''
65 tell application "QuickTime Player" to tell document 1
66 set t to current time
67 step forward
68 set k to current time
69 set r to 1 / (k - t)
70 step backward
71 return "" & (round (r * t) rounding down) & ":" & name
72 end tell
73 '''
74
75 try:
76 result = subprocess.run(["osascript", "-e", applescript],
77 check=True, text=True, capture_output=True)
78 frame_and_name = result.stdout.strip()
79
80 if not frame_and_name or ":" not in frame_and_name:
81 print("Error: AppleScript did not return expected output")
82 sys.exit(1)
83
84 target_frame, name = frame_and_name.split(":", 1)
85 target_frame = int(target_frame)
86
87 print(f"Target frame: {target_frame}")
88 print(f"File name: {name}")
89 except Exception as e:
90 print(f"Error running AppleScript: {e}")
91 sys.exit(1)
92
93 # Step 2: Find the file on disk
94 print("\nStep 2: Finding file on disk...")
95 file_path = find_file(name)
96 print(f"Found file at: {file_path}")
97
98 # Step 3: Run Fusion script to get current frame
99 print("\nStep 3: Getting current frame from Fusion...")
100 fusion_script_cmd = "'/Applications/Blackmagic Fusion 19/Fusion.app/Contents/Libraries/fuscript' -x 'print(\"[[\"..Fusion().CurrentComp.CurrentTime..\"]]\")'"
101 fusion_output = run_command(fusion_script_cmd, shell=True)
102
103 # Extract the frame number from the output
104 match = re.search(r'\[\[(\d+)\]\]', fusion_output)
105 if not match:
106 print(f"Error: Could not parse frame number from Fusion output: {fusion_output}")
107 sys.exit(1)
108
109 current_frame = int(match.group(1))
110 print(f"Current frame: {current_frame}")
111
112 # Step 4: Compute TRIM_IN and EXTEND_FIRST
113 print("\nStep 4: Computing TRIM_IN and EXTEND_FIRST...")
114 trim_in = 0
115 extend_first = 0
116
117 if target_frame > current_frame:
118 trim_in = target_frame - current_frame
119 print(f"Target frame is AFTER current frame. Setting TRIM_IN to {trim_in}")
120 else:
121 extend_first = current_frame - target_frame
122 print(f"Target frame is BEFORE current frame. Setting EXTEND_FIRST to {extend_first}")
123
124 # Step 5: Create the Fusion loader text and copy to clipboard
125 print("\nStep 5: Creating Fusion loader text and copying to clipboard...")
126 fusion_text = f'''{{Tools = ordered() {{Loader = Loader {{Clips = {{Clip {{ID = "Clip1",Filename = "{file_path}",FormatID = "QuickTimeMovies",Length = 9999999,Multiframe = true,TrimIn = {trim_in},TrimOut = 9999999,ExtendFirst = {extend_first},ExtendLast = 0,Loop = 1,AspectMode = 0,Depth = 0,TimeCode = 0,GlobalStart = 0,GlobalEnd = 9999999}}}},CtrlWZoom = false,Inputs = {{["Gamut.SLogVersion"] = Input {{ Value = FuID {{ "SLog2" }}, }}}},}}}},ActiveTool = "Loader"}}'''
127
128 try:
129 pyperclip.copy(fusion_text)
130 print("Text copied to clipboard:")
131 print(fusion_text)
132 except Exception as e:
133 print(f"Error copying to clipboard: {e}")
134 sys.exit(1)
135
136 # Finally, activate Fusion but don't paste
137 print("\nActivating Fusion...")
138 activate_app("Fusion")
139 print("Script completed successfully!")
140
141if __name__ == "__main__":
142 main()