Here’s the progress so far. I have ChromaSDK lighting effects sending from common.js
and broadcasting on the PubSub. Viewers receive the PubSub broadcast messages which display on their Chroma enabled devices.
Common.js
sends compressed data (5kb) every 1 second which is able to fit 10 animation frames in each PubSub broadcast.
I still need to know a few things.
1 Are there common.js
callbacks for time so that I can sync the lighting effects with the video timestamp?
2 Is it possible to include PubSub data in the archived video so that video playback can still play lighting effects?
===
Below was my process for figuring out the examples and getting things to work…
===
Here’s the PubSub example.
Is it possible for an extension’s Viewer.html on the Twitch stream to talk with the PubSub?
It seems when I include the PubSub example, that creates an Auth button in the Viewer.html but the iframe is not allowed to redirect to authenticate.
I’m hoping that the Viewer.html can communicate with the PubSub. The broadcaster will be sending data via PubSub. And then the viewers will receive the data from PubSub which will talk with their viewer.html page.
It looks like the Broadcaster Viewer.html/js needs to communicate with my EBS. And the EBS needs to push to the PubSub.
Can the Viewer.html/js receive PubSub messages without the EBS for the viewer watching the stream?
I just noticed common.js which has auth and context events that I can pass to the EBS to authorize and send to PubSub for the broadcaster.
Looks like with common.js and the auth callback, I can detect if the Twitch viewer is the broadcaster.
if(window.Twitch.ext) {
window.Twitch.ext.onAuthorized(function(auth) {
console.log(auth);
var parts = auth.token.split(".");
var payload = JSON.parse(window.atob(parts[1]));
var streamer_id = payload.channel_id;
console.log("broadcaster", payload.role == 'broadcaster');
});
Okay and here’s a modified common.js that can get pubsub messages. There’s no need to hardcode the clientId/token because that info comes across in the auth event.
/*
Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at
http://aws.amazon.com/apache2.0/
or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
/*
Set Javascript common to all extension views in this file.
*/
// Source: https://www.thepolyglotdeveloper.com/2015/03/create-a-random-nonce-string-using-javascript/
function nonce(length) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
function heartbeat() {
message = {
type: 'PING'
};
$('.ws-output').append('SENT: ' + JSON.stringify(message) + '\n');
ws.send(JSON.stringify(message));
}
function listen(topic, auth) {
message = {
type: 'LISTEN',
nonce: nonce(15),
data: {
topics: [topic],
auth_token: auth.token
}
};
$('.ws-output').append('SENT: ' + JSON.stringify(message) + '\n');
ws.send(JSON.stringify(message));
}
function connect() {
var heartbeatInterval = 1000 * 60; //ms between PING's
var reconnectInterval = 1000 * 3; //ms to wait before reconnect
var heartbeatHandle;
ws = new WebSocket('wss://pubsub-edge.twitch.tv');
ws.onopen = function(event) {
$('.ws-output').append('INFO: Socket Opened\n');
heartbeat();
heartbeatHandle = setInterval(heartbeat, heartbeatInterval);
};
ws.onerror = function(error) {
$('.ws-output').append('ERR: ' + JSON.stringify(error) + '\n');
};
ws.onmessage = function(event) {
message = JSON.parse(event.data);
$('.ws-output').append('RECV: ' + JSON.stringify(message) + '\n');
if (message.type == 'RECONNECT') {
$('.ws-output').append('INFO: Reconnecting...\n');
setTimeout(connect, reconnectInterval);
}
};
ws.onclose = function() {
$('.ws-output').append('INFO: Socket Closed\n');
clearInterval(heartbeatHandle);
$('.ws-output').append('INFO: Reconnecting...\n');
setTimeout(connect, reconnectInterval);
};
}
if(window.Twitch.ext) {
window.Twitch.ext.onAuthorized(function(auth) {
console.log("auth", auth);
var parts = auth.token.split(".");
var payload = JSON.parse(window.atob(parts[1]));
var streamer_id = payload.channel_id;
console.log("broadcaster", payload.role == 'broadcaster');
if (auth.token) {
connect();
$('.socket').show()
$.ajax({
url: "https://api.twitch.tv/kraken/user",
method: "GET",
headers: {
"Client-ID": auth.clientId,
"Authorization": "OAuth " + auth.token
}})
.done(function(user) {
$('#topic-label').text("Enter a topic to listen to. For example, to listen to whispers enter topic 'whispers."+user._id+"'");
});
$('#topic-form').submit(function() {
listen($('#topic-text').val(), auth);
event.preventDefault();
});
}
});
window.Twitch.ext.listen('broadcast', function (topic, contentType, message) {
console.log("topic", topic);
console.log("contentType", contentType);
console.log("message", message);
});
window.Twitch.ext.onContext(function(context, contextFields) {
console.log("context", context);
console.log("contextFields", contextFields);
});
window.Twitch.ext.onError(function(err) {
console.error("err", err);
});
}
Can the window.Twitch.ext.onAuthorized(function(auth) {
authorization be used to authorize the PubSub? It would seem like the Viewer common.js would already be authorized since it is running on the Twitch site???
Next question??? What topic should be used?
And I’ll see about sending data on the PubSub to see if other viewers receive it.
Common.js can broadcast to PubSub directly with the following method:
window.Twitch.ext.send('broadcast', 'application/json', json);
Thanks,
~Tim Graupmann