integrate some random hardware control surfaces to random creative apps
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: add some other vibe scripts i happen to have

clover caruso (Mar 15, 2026, 9:09 PM -0700) c7e1a06c 08668fa9

+219 -7
+66
bin/exr_flip_z.py
··· 1 + # Fusion uses negative Z values for the depth buffer, while Blender denotes 2 + # this with positive values. For "Depth Merge" and other nodes to work 3 + # correctly, Blender's output must be flipped. I am unaware of how to do 4 + # this in Blender itself, hence this simple post processor. 5 + import OpenEXR 6 + import Imath 7 + import numpy as np 8 + import argparse 9 + import os 10 + 11 + Z_FLIPPED_METADATA_KEY = "zBufferFlipped" 12 + 13 + def invert_z_buffer(exr_input_path): 14 + exr_file = OpenEXR.InputFile(exr_input_path) 15 + 16 + header = exr_file.header() 17 + channels = header['channels']; 18 + part_names = channels.keys() 19 + 20 + if Z_FLIPPED_METADATA_KEY in header: 21 + print(f"Skipping {exr_input_path}") 22 + exr_file.close() 23 + return 24 + 25 + processed_parts = {} 26 + 27 + for part_name in part_names: 28 + pixel_type = header['channels'][part_name].type 29 + if pixel_type == Imath.PixelType(Imath.PixelType.HALF): 30 + dtype = np.float16 31 + elif pixel_type == Imath.PixelType(Imath.PixelType.FLOAT): 32 + dtype = np.float32 33 + else: 34 + raise ValueError(f"Unsupported pixel type {pixel_type} for channel {part_name}.") 35 + 36 + channel_data = exr_file.channel(part_name, pixel_type) 37 + channel_data_array = np.frombuffer(channel_data, dtype=dtype) 38 + 39 + if "Depth.Z" in part_name: 40 + channel_data_array = -channel_data_array 41 + 42 + processed_parts[part_name] = channel_data_array.tobytes() 43 + 44 + header[Z_FLIPPED_METADATA_KEY] = 1 45 + 46 + exr_output = OpenEXR.OutputFile(exr_input_path, header) 47 + 48 + exr_output.writePixels(processed_parts) 49 + 50 + exr_file.close() 51 + exr_output.close() 52 + 53 + print(f"Processed: {exr_input_path}") 54 + 55 + if __name__ == "__main__": 56 + parser = argparse.ArgumentParser(description="Invert the Z-buffer in multipart EXR files.") 57 + 58 + parser.add_argument('exr_files', nargs='+', help="List of EXR files to process.") 59 + 60 + args = parser.parse_args() 61 + 62 + for exr_file in args.exr_files: 63 + if os.path.exists(exr_file): 64 + invert_z_buffer(exr_file) 65 + else: 66 + print(f"File not found: {exr_file}")
+142
bin/import_quicktime_to_fusion.py
··· 1 + #!/usr/bin/env python3 2 + import subprocess 3 + import os 4 + import sys 5 + import re 6 + import glob 7 + import pyperclip 8 + import time 9 + 10 + def 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 + 26 + def 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 + 52 + def 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 + 61 + def 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 + 141 + if __name__ == "__main__": 142 + main()
+11 -7
readme.md
··· 1 1 # Clover's Creative Control 2 2 3 - This is a set of tools to let additional hardware surfaces integrate with 4 - creative applications on a Mac device. In addition to the primary keybinding 5 - system, this project can be used as a library to use the control primitves 6 - directly (either to build your own hardware integrations, or to control the 7 - software). 3 + This is a set of tools to let additional hardware devices integrate with 4 + creative applications on a Mac device. Additionally, this repo contains a lot of 5 + my own tools and scripts I use in the Music/Video creative processes. 8 6 9 - These tools only work on macOS. I don't have interest in maintaining other 10 - configurations. 7 + In addition to the primary keybinding system and personal software 8 + configurations, this project can be used as a library to use the control 9 + primitives directly (either to build your own hardware integrations, or to 10 + control the software). This can be done by installing this repo as a `pnpm` git 11 + dependency in your project. 12 + 13 + **NOTE**: These tools only work on macOS. I don't have interest in maintaining 14 + other configurations. 11 15 12 16 ## Hardware 13 17