-- ReaAssist Installer -- https://reaassist.app -- -- Downloads the small ReaAssist bootstrap script, registers it as a -- REAPER action, then launches it. ReaAssist fetches the rest on first run. -- -- To run: REAPER > Actions > Show action list > Load ReaScript, -- choose this file, then click Run. local INSTALL_REL = "Scripts/mbriggs-reaper/ReaAssist/" local SCRIPT_URL = "https://raw.githubusercontent.com/michaelbriggsaudio/mbriggs-reaper/main/ReaAssist/ReaAssist.lua" local function die(msg) reaper.MB(msg, "ReaAssist Installer", 0) end local function read_file(path) local f = io.open(path, "rb") if not f then return nil end local content = f:read("*a") f:close() return content end -- Install into the normal ReaAssist script folder. local target_dir = reaper.GetResourcePath() .. "/" .. INSTALL_REL local final_path = target_dir .. "ReaAssist.lua" local tmp_path = final_path .. ".tmp" reaper.RecursiveCreateDirectory(target_dir, 0) -- Download to .tmp with curl. On macOS, use the system path because -- REAPER-launched scripts may not inherit PATH reliably. local os_name = reaper.GetOS() or "" local is_mac = os_name:match("OSX") or os_name:match("macOS") local curl_bin = is_mac and "/usr/bin/curl" or "curl" local cmd = string.format( '%s -fsSL --connect-timeout 10 --max-time 45 -o "%s" "%s"', curl_bin, tmp_path, SCRIPT_URL) local rc = reaper.ExecProcess(cmd, 60000) if not rc then -- Curl never launched, so there is no exit code to report. os.remove(tmp_path) die("Could not launch curl from REAPER (ExecProcess returned nil).\n\n" .. "Command attempted:\n" .. cmd .. "\n\n" .. "You can install ReaAssist manually from " .. "https://github.com/michaelbriggsaudio/mbriggs-reaper") return end if not rc:match("^0\n") then os.remove(tmp_path) local code = rc:match("^(%d+)") or "?" local detail = rc:sub(1, 300) die(string.format( "Download failed (curl exit %s).\n\n" .. "%s\n\n" .. "Check your network connection and try again.\n\n" .. "If the problem continues, you can install ReaAssist manually " .. "from https://github.com/michaelbriggsaudio/mbriggs-reaper", code, detail)) return end -- Make sure the download is really ReaAssist.lua. local content = read_file(tmp_path) local has_header = content and content:match("ReaAssist %- REAPER Smart Assistant") local has_version = content and content:match('VERSION%s*=%s*"%d+%.%d+%.%d+"') if not (has_header and has_version) then os.remove(tmp_path) die("Downloaded file does not look like ReaAssist. Try again later.") return end -- Replace safely: back up the old script, move the new one in, then -- restore the backup if the final move fails. local backup_path = final_path .. ".bak" local existing = io.open(final_path, "rb") local has_existing = existing ~= nil if existing then existing:close() end if has_existing then os.remove(backup_path) -- remove any stale backup from a prior attempt local ok, err = os.rename(final_path, backup_path) if not ok then os.remove(tmp_path) die("Could not back up existing ReaAssist.lua: " .. tostring(err)) return end end local ok, err = os.rename(tmp_path, final_path) if not ok then -- Roll back if we already moved the old script aside. if has_existing then os.rename(backup_path, final_path) end os.remove(tmp_path) die("Could not move downloaded file into place: " .. tostring(err)) return end -- New script is in place; clean up the backup. if has_existing then os.remove(backup_path) end -- Register it as a permanent REAPER action. local action_id = reaper.AddRemoveReaScript(true, 0, final_path, true) if action_id == 0 then die("ReaAssist was downloaded successfully but could not be " .. "registered as a REAPER action. You can load it manually from:\n\n" .. final_path) return end -- Launch ReaAssist; its bootstrap fetches the remaining files. reaper.Main_OnCommand(action_id, 0)