-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Requires the jslib from the web UI copying in as 'StreamingRemote.js'
- Loading branch information
1 parent
878ec06
commit 37bffe6
Showing
12 changed files
with
3,173 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,309 @@ | ||
<!DOCTYPE HTML> | ||
<html> | ||
|
||
<head> | ||
<title>com.fredemmott.streamingRemote</title> | ||
<script src="StreamingRemote.js"></script> | ||
<meta charset="utf-8" /> | ||
</head> | ||
|
||
<body> | ||
<script> | ||
async function getOutputs(uri, password) { | ||
const {rpc, ws} = await createClient(uri, password); | ||
const outputs = await rpc.getOutputs(); | ||
ws.close(); | ||
return outputs; | ||
} | ||
|
||
function createClient(uri, password) { | ||
const ws = new WebSocket(uri); | ||
ws.binaryType = 'arraybuffer'; | ||
return new Promise((resolve, reject) => { | ||
ws.addEventListener('open', async () => { | ||
const handshakeState = await StreamingRemote.handshake(ws, password); | ||
const rpc = new StreamingRemote.RPCClient(ws, handshakeState); | ||
rpc.onHelloNotification(() => resolve({rpc, ws})); | ||
}); | ||
ws.addEventListener('close', () => { | ||
reject(); | ||
}); | ||
}); | ||
} | ||
|
||
var websocket = null; | ||
var pluginUUID = null; | ||
var images = null; | ||
const rpcStore = {}; | ||
const outputStore = {}; | ||
const settingsStore = {}; | ||
|
||
var DestinationEnum = Object.freeze({ "HARDWARE_AND_SOFTWARE": 0, "HARDWARE_ONLY": 1, "SOFTWARE_ONLY": 2 }) | ||
|
||
var myPlugin = { | ||
|
||
type: "com.fredemmott.streamingremote.action", | ||
|
||
onKeyDown: function (context, settings, coordinates, userDesiredState) { | ||
}, | ||
|
||
onKeyUp: function (context, settings, coordinates, userDesiredState) { | ||
const {rpc} = rpcStore[context]; | ||
const state = outputStore[context].state; | ||
if (state == 'stopped') { | ||
rpc.startOutput(settings.output); | ||
} else { | ||
rpc.stopOutput(settings.output); | ||
} | ||
}, | ||
|
||
updateImage: async function (context, settings) { | ||
const {rpc} = rpcStore[context]; | ||
const outputs = await rpc.getOutputs(); | ||
outputStore[context] = outputs[settings.output]; | ||
const {type, state} = outputs[settings.output]; | ||
if (!type) { | ||
return; | ||
} | ||
this.setImage(context, type, state); | ||
}, | ||
|
||
setImage: function (context, type, state) { | ||
var suffix; | ||
switch (state) { | ||
case 'stopped': | ||
suffix = 'Stopped'; | ||
break; | ||
case 'starting': | ||
case 'stopping': | ||
suffix = 'Changing'; | ||
break; | ||
case 'active': | ||
suffix = 'Active'; | ||
break; | ||
} | ||
const key = (type == 'local_recording' ? 'recording' : 'streaming')+suffix; | ||
const image = images[key]; | ||
websocket.send(JSON.stringify({ | ||
event: 'setImage', | ||
context: context, | ||
payload: { image }, | ||
})); | ||
}, | ||
|
||
onWillAppear: async function (context, settings, coordinates) { | ||
settingsStore[context] = settings; | ||
websocket.send(JSON.stringify({ | ||
event: 'showAlert', | ||
context: context, | ||
})); | ||
if (settings.output) { | ||
await this.connectRemote(context, settings); | ||
await this.updateImage(context, settings); | ||
} | ||
}, | ||
|
||
connectRemote: async function (context, settings) { | ||
const {rpc, ws} = await createClient(settings.uri, settings.password); | ||
rpcStore[context] = {rpc, ws}; | ||
outputStore[context] = undefined; | ||
ws.addEventListener('close', () => { | ||
websocket.send(JSON.stringify({ | ||
event: 'showAlert', | ||
context: context, | ||
})); | ||
rpcStore[context] = undefined; | ||
outputStore[context] = undefined; | ||
}); | ||
ws.addEventListener('error', () => { | ||
websocket.send(JSON.stringify({ | ||
event: 'showAlert', | ||
context: context, | ||
})); | ||
rpcStore[context] = undefined; | ||
outputStore[context] = undefined; | ||
}); | ||
rpc.onOutputStateChanged((id, state) => { | ||
if (id == settings.output) { | ||
this.setImage(context, outputStore[context].type, state); | ||
} | ||
outputStore[context].state = state; | ||
}); | ||
}, | ||
|
||
displayAndRetryBadConnections: async function() { | ||
Object.keys(settingsStore).map(async context => { | ||
if (rpcStore[context] && outputStore[context]) { | ||
return; | ||
} | ||
websocket.send(JSON.stringify({ | ||
event: 'showAlert', | ||
context: context, | ||
})); | ||
if (rpcStore[context]) { | ||
return; | ||
} | ||
try { | ||
const settings = settingsStore[context]; | ||
await this.connectRemote(context, settings); | ||
await this.updateImage(context, settings); | ||
} catch (e) { | ||
} | ||
}); | ||
}, | ||
|
||
onSendToPlugin: async function (context, jsonPayload) { | ||
const event = jsonPayload.event; | ||
if (event == "getData") { | ||
const settings = settingsStore[context]; | ||
var outputs; | ||
try { | ||
outputs = await getOutputs(settings.uri, settings.password); | ||
} catch (e) { | ||
outputs = {}; | ||
} | ||
|
||
const json = { | ||
event: "sendToPropertyInspector", | ||
context: context, | ||
payload: { event: "data", outputs, settings } | ||
}; | ||
websocket.send(JSON.stringify(json)); | ||
return; | ||
} | ||
if (event == 'saveSettings') { | ||
const settings = jsonPayload.settings; | ||
if (settingsStore[context] != settings) { | ||
settingsStore[context] = jsonPayload.settings; | ||
await this.connectRemote(context, settings); | ||
} | ||
websocket.send(JSON.stringify({ | ||
event: 'setSettings', | ||
context, | ||
payload: jsonPayload.settings | ||
})); | ||
this.updateImage(context, settings); | ||
var outputs; | ||
try { | ||
outputs = await getOutputs(settings.uri, settings.password); | ||
} catch (e) { | ||
outputs = {}; | ||
} | ||
websocket.send(JSON.stringify({ | ||
event: 'sendToPropertyInspector', | ||
context, | ||
payload: { event: 'data', outputs, settings } | ||
})); | ||
} | ||
} | ||
}; | ||
|
||
setInterval(() => myPlugin.displayAndRetryBadConnections(), 1000); | ||
|
||
function connectSocket(inPort, inPluginUUID, inRegisterEvent, inInfo) { | ||
pluginUUID = inPluginUUID | ||
|
||
// Open the web socket | ||
websocket = new WebSocket("ws://localhost:" + inPort); | ||
|
||
function registerPlugin(inPluginUUID) { | ||
var json = { | ||
"event": inRegisterEvent, | ||
"uuid": inPluginUUID | ||
}; | ||
|
||
websocket.send(JSON.stringify(json)); | ||
}; | ||
|
||
websocket.onopen = function () { | ||
// WebSocket is connected, send message | ||
registerPlugin(pluginUUID); | ||
}; | ||
|
||
websocket.onmessage = async function (evt) { | ||
// Received message from Stream Deck | ||
var jsonObj = JSON.parse(evt.data); | ||
var event = jsonObj['event']; | ||
var action = jsonObj['action']; | ||
var context = jsonObj['context']; | ||
var jsonPayload = jsonObj['payload'] || {}; | ||
|
||
if (event == "keyDown") { | ||
var settings = jsonPayload['settings']; | ||
var coordinates = jsonPayload['coordinates']; | ||
var userDesiredState = jsonPayload['userDesiredState']; | ||
myPlugin.onKeyDown(context, settings, coordinates, userDesiredState); | ||
} | ||
else if (event == "keyUp") { | ||
var settings = jsonPayload['settings']; | ||
var coordinates = jsonPayload['coordinates']; | ||
var userDesiredState = jsonPayload['userDesiredState']; | ||
myPlugin.onKeyUp(context, settings, coordinates, userDesiredState); | ||
} | ||
else if (event == "willAppear") { | ||
var settings = jsonPayload['settings']; | ||
var coordinates = jsonPayload['coordinates']; | ||
myPlugin.onWillAppear(context, settings, coordinates); | ||
} | ||
else if (event == "sendToPlugin") { | ||
myPlugin.onSendToPlugin(context, jsonPayload); | ||
|
||
} | ||
}; | ||
|
||
websocket.onclose = function () { | ||
// Websocket is closed | ||
}; | ||
}; | ||
|
||
|
||
function loadImageAsDataUri(url) { | ||
return new Promise((resolve, _) => { | ||
var image = new Image(); | ||
|
||
image.onload = function () { | ||
var canvas = document.createElement("canvas"); | ||
|
||
canvas.width = this.naturalWidth; | ||
canvas.height = this.naturalHeight; | ||
|
||
var ctx = canvas.getContext("2d"); | ||
ctx.drawImage(this, 0, 0); | ||
resolve(canvas.toDataURL("image/png")); | ||
}; | ||
|
||
image.src = url; | ||
}); | ||
}; | ||
|
||
async function loadImages() { | ||
const [ | ||
streamingStopped, | ||
streamingChanging, | ||
streamingActive, | ||
recordingStopped, | ||
recordingChanging, | ||
recordingActive | ||
] = await Promise.all([ | ||
loadImageAsDataUri('keys/streaming-stopped.png'), | ||
loadImageAsDataUri('keys/streaming-changing.png'), | ||
loadImageAsDataUri('keys/streaming-active.png'), | ||
loadImageAsDataUri('keys/recording-stopped.png'), | ||
loadImageAsDataUri('keys/recording-changing.png'), | ||
loadImageAsDataUri('keys/recording-active.png'), | ||
]); | ||
images = { | ||
streamingStopped, | ||
streamingChanging, | ||
streamingActive, | ||
recordingStopped, | ||
recordingChanging, | ||
recordingActive, | ||
}; | ||
} | ||
loadImages(); | ||
</script> | ||
|
||
</body> | ||
|
||
</html> |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.