init resolume column by name

This commit is contained in:
Rik Berkelder 2026-10-06 02:07:02 +02:00
parent 3a4c50f9aa
commit 9f18c78845
2 changed files with 453 additions and 0 deletions

View file

@ -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