diff --git a/Resolume Column By Name/RB Resolume Column By Name v1_0.lua b/Resolume Column By Name/RB Resolume Column By Name v1_0.lua new file mode 100644 index 0000000..d837bcd --- /dev/null +++ b/Resolume Column By Name/RB Resolume Column By Name v1_0.lua @@ -0,0 +1,447 @@ +local base_url = 'http://localhost:8899' + +-- Maximum amount of time an entire request may take +local request_timeout = 3 + +-- Maximum time a single socket operation may block +local socket_timeout = 0.01 + +-- How long to give MA3 back control between operations +local yield_time = 0.01 + +local socket = require("socket") +local json = require("json") + +local columns = {} + + +---------------------------------------------------------------- +-- UI helpers +---------------------------------------------------------------- + +local function show_message(title, message) + MessageBox({ + title = title, + message = message, + commands = { + { value = 1, name = "Ok" }, + }, + }) +end + + +local function show_error(message) + ErrEcho("Resolume: " .. tostring(message)) + show_message("Resolume Error", tostring(message)) +end + + +---------------------------------------------------------------- +-- Utility +---------------------------------------------------------------- + +local function yield() + coroutine.yield(yield_time) +end + + +local function parse_url(url) + local host, port, path + + host, port, path = url:match( + "^http://([^:/]+):(%d+)(/.*)$" + ) + + if not host then + host, path = url:match( + "^http://([^/]+)(/.*)$" + ) + + if host then + port = 80 + end + end + + if not host then + host = url:match("^http://([^/]+)$") + + if host then + port = 80 + path = "/" + end + end + + if not host then + return nil + end + + return host, tonumber(port), path +end + + +---------------------------------------------------------------- +-- HTTP +---------------------------------------------------------------- + +local function http_request(url, method, body) + + local host, port, path = parse_url(url) + + if not host then + show_error("Invalid URL: " .. tostring(url)) + return nil + end + + local start_time = os.clock() + + ------------------------------------------------------------ + -- Create socket + ------------------------------------------------------------ + + local tcp, err = socket.tcp() + + if not tcp then + show_error("Could not create socket: " .. tostring(err)) + return nil + end + + -- Each individual socket operation can block for at most + -- socket_timeout seconds. + tcp:settimeout(socket_timeout) + + ------------------------------------------------------------ + -- Connect + ------------------------------------------------------------ + + local connected = false + + while not connected do + + local ok, connect_err = tcp:connect(host, port) + + if ok then + connected = true + + elseif connect_err == "timeout" then + -- Connection is still in progress. + -- Give MA3 some time before trying again. + yield() + + else + tcp:close() + + show_error( + "Connection failed: " .. + tostring(connect_err) + ) + + return nil + end + + if os.clock() - start_time >= request_timeout then + tcp:close() + + show_error("Connection timeout") + + return nil + end + end + + + ------------------------------------------------------------ + -- Construct HTTP request + ------------------------------------------------------------ + + local request = + method .. " " .. path .. " HTTP/1.1\r\n" .. + "Host: " .. host .. ":" .. tostring(port) .. "\r\n" .. + "Connection: close\r\n" + + if body then + request = + request .. + "Content-Type: application/json\r\n" .. + "Content-Length: " .. tostring(#body) .. "\r\n" + end + + request = request .. "\r\n" + + if body then + request = request .. body + end + + + ------------------------------------------------------------ + -- Send + ------------------------------------------------------------ + + local send_pos = 1 + + while send_pos <= #request do + + local sent, send_err, last_pos = + tcp:send(request, send_pos) + + if sent then + send_pos = sent + 1 + + elseif send_err == "timeout" then + + if last_pos then + send_pos = last_pos + 1 + end + + yield() + + else + tcp:close() + + show_error( + "Send failed: " .. + tostring(send_err) + ) + + return nil + end + + if os.clock() - start_time >= request_timeout then + tcp:close() + + show_error("Send timeout") + + return nil + end + end + + + ------------------------------------------------------------ + -- Receive + ------------------------------------------------------------ + + local response = {} + + while true do + + local data, recv_err, partial = + tcp:receive(4096) + + if data then + response[#response + 1] = data + end + + if partial then + response[#response + 1] = partial + end + + if recv_err == "closed" then + break + + elseif recv_err == "timeout" then + -- Nothing available yet. + yield() + + elseif recv_err then + tcp:close() + + show_error( + "Receive failed: " .. + tostring(recv_err) + ) + + return nil + end + + if os.clock() - start_time >= request_timeout then + tcp:close() + + show_error("HTTP request timeout") + + return nil + end + end + + + tcp:close() + + + ------------------------------------------------------------ + -- Parse HTTP response + ------------------------------------------------------------ + + local raw_response = table.concat(response) + + local header_end = + raw_response:find("\r\n\r\n", 1, true) + + if not header_end then + show_error("Invalid HTTP response") + return nil + end + + local headers = + raw_response:sub(1, header_end - 1) + + local response_body = + raw_response:sub(header_end + 4) + + local status_code = + tonumber( + headers:match( + "^HTTP/%d%.%d%s+(%d+)" + ) + ) + + if not status_code then + show_error("Could not determine HTTP status") + return nil + end + + if status_code < 200 or status_code >= 300 then + show_error( + "HTTP error " .. + tostring(status_code) + ) + + return nil + end + + return response_body +end + + +---------------------------------------------------------------- +-- JSON HTTP helpers +---------------------------------------------------------------- + +local function http_request_json(url, method, body) + + local response_body = + http_request(url, method, body) + + if not response_body then + return nil + end + + local ok, response = + pcall( + json.decode, + response_body + ) + + if not ok or type(response) ~= "table" then + show_error( + "JSON error decoding: " .. + tostring(response_body) + ) + + return nil + end + + return response +end + + +local function http_request_void(url, method) + return http_request(url, method) +end + + +---------------------------------------------------------------- +-- Resolume +---------------------------------------------------------------- + +local function update_columns() + + -- Echo("Resolume: fetching composition...") + + local comp = + http_request_json( + base_url .. "/api/v1/composition", + "GET" + ) + + if not comp then + return + end + + if not comp.columns then + show_error( + "Resolume response contains no columns" + ) + + return + end + + columns = {} + + for _, column in ipairs(comp.columns) do + + if column.name and column.name.value then + columns[column.name.value] = column.id + end + end + + -- Echo( + -- "Resolume columns: " .. + -- json.encode(columns) + -- ) +end + + +local function connect_column(name) + + local column_id = columns[name] + + if not column_id then + show_error( + "Unknown column: " .. + tostring(name) + ) + + return + end + + http_request_void( + base_url .. + "/api/v1/composition/columns/by-id/" .. + tostring(column_id) .. + "/connect", + + "POST" + ) +end + + +---------------------------------------------------------------- +-- Plugin entry point +---------------------------------------------------------------- + +local function main(_display, rawArgs) + + + + if rawArgs == nil or rawArgs == "" then + + while true do + update_columns() + + coroutine.yield(5) + end + + return + end + + local columnName = rawArgs + + if not columnName then + show_error("No column specified") + return + end + + + connect_column(columnName) +end + +update_columns() + +return main \ No newline at end of file diff --git a/Resolume Column By Name/RB_Resolume_Column_By_Name_v1_0.xml b/Resolume Column By Name/RB_Resolume_Column_By_Name_v1_0.xml new file mode 100644 index 0000000..aaeb5b0 --- /dev/null +++ b/Resolume Column By Name/RB_Resolume_Column_By_Name_v1_0.xml @@ -0,0 +1,6 @@ + + + + + +