Skip to content

Commit

Permalink
Merge pull request #184 from timotheeg/vdo_ninja_urls_on_admin
Browse files Browse the repository at this point in the history
Improvements to vdoninja support
  • Loading branch information
timotheeg authored Oct 19, 2024
2 parents fb65ca4 + d1747ba commit c2fce02
Show file tree
Hide file tree
Showing 9 changed files with 417 additions and 31 deletions.
23 changes: 13 additions & 10 deletions domains/MatchRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,8 @@ class MatchRoom extends Room {
}
}

sendPlayerInfoToViews(pidx) {
const player = this.state.players[pidx];
sendPlayerInfoToViews(pidx, data = null) {
const player = data || this.state.players[pidx];

this.sendToViews(['setId', pidx, player.id]); // resets the player and game in frontend
this.sendToViews(['setLogin', pidx, player.login]);
Expand Down Expand Up @@ -639,7 +639,10 @@ class MatchRoom extends Room {
// finally send dummy data to clear last player
// warning: this clears the player data, but it doens't cler the player object itself :(
// TODO: implement an actual removePlayer() API in views
updatePlayer(getBasePlayerData(), this.state.players.length);
this.sendPlayerInfoToViews(
this.state.players.length,
getBasePlayerData()
);

forward_to_views = false;
break;
Expand Down Expand Up @@ -690,11 +693,11 @@ class MatchRoom extends Room {

if (Array.isArray(message) && message[0] === 'setVdoNinjaURL') {
user.vdo_ninja_url = message[1];
this.state.players
.filter(p => p.id === user.id)
.forEach(p => {
p.vdo_ninja_url = message[1];
});
this.state.players.forEach((p, p_idx) => {
if (p.id !== user.id) return;
p.vdo_ninja_url = user.vdo_ninja_url;
this.tellAdmin(['setVdoNinjaURL', p_idx, user.vdo_ninja_url]);
});
}

this.state.players.forEach((player, p_idx) => {
Expand All @@ -709,13 +712,13 @@ class MatchRoom extends Room {
message = new Uint8Array(message);
}
message[0] = (message[0] & 0b11111000) | p_idx; // sets player number in header byte of binary message
this.sendToViews(message);
this.sendGameFrameToViews(message);
} else if (Array.isArray(message)) {
this.sendToViews([message[0], p_idx, ...message.slice(1)]);
// TODO: send message to admin page as well?
} else {
// assume frame
this.sendToViews(['frame', p_idx, message]);
this.sendGameFrameToViews(['frame', p_idx, message]);
}
});
}
Expand Down
12 changes: 10 additions & 2 deletions domains/Room.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,29 @@ class Room extends EventEmitter {
this.views.forEach(connection => connection.send(message));
}

sendGameFrameToViews(message) {
this.views.forEach(connection => {
if (!connection.meta._no_game_frames) {
connection => connection.send(message);
}
});
}

close(reason) {
this.views.forEach(connection => connection.kick(reason));
this.views.clear();
}

handleProducerMessage(user, message) {
if (message instanceof Uint8Array) {
this.sendToViews(message);
this.sendGameFrameToViews(message);
} else if (Array.isArray(message)) {
if (message[0] === 'setVdoNinjaURL') {
user.vdo_ninja_url = message[1];
}
this.sendToViews([message[0], 0, ...message.slice(1)]);
} else {
this.sendToViews(['frame', 0, message]);
this.sendGameFrameToViews(['frame', 0, message]);
}
}
}
Expand Down
6 changes: 1 addition & 5 deletions modules/Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ class Connection extends EventEmitter {
id: this.id,
},
]);

// for backward compatibility
// TODO: remove after a while
this.send(['_id', this.id]);
}

send(message) {
Expand All @@ -83,7 +79,7 @@ class Connection extends EventEmitter {

ping() {
this.is_alive = false;
this.socket.ping(_.noop); // TODO: handle pingt timeout (pong not coming back)
this.socket.ping(_.noop); // TODO: handle ping timeout (pong not coming back)
}

doClose(code, reason) {
Expand Down
Binary file added public/images/Spinner@1x-1.0s-60px-60px.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 0 additions & 13 deletions public/js/connection.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import BinaryFrame from '/js/BinaryFrame.js';

const ID_MIN_RESET_TIME_IN_SECONDS = 5;

export default class Connection {
constructor(uri = null, extra_search_params = null) {
let url;
Expand Down Expand Up @@ -70,17 +68,6 @@ export default class Connection {
this.onInit();
return;
}
case '_id': {
if (
data[1] !== this.id ||
Date.now() - this.id_ts > ID_MIN_RESET_TIME_IN_SECONDS * 1000
) {
this.id = data[1];
this.id_ts = Date.now();
this.onInit();
}
return;
}
case '_kick': {
const reason = data[1];
console.log('Socket kicked', reason);
Expand Down
107 changes: 107 additions & 0 deletions public/views/competition_admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,71 @@ class Player {
};
}

let copying = false;
this.dom.vdo_ninja_url.querySelector('svg.action.clipboard-copy').onclick =
async () => {
if (copying) return;
copying = true;

const icon = this.dom.vdo_ninja_url.querySelector(
'svg.action.clipboard-copy'
);

[...this.dom.vdo_ninja_url.querySelectorAll('svg.result')].forEach(
icon => (icon.style.display = 'none')
);

try {
await navigator.clipboard.writeText(
this.dom.vdo_ninja_url.querySelector('a').href
);
icon.style.display = 'none';
this.dom.vdo_ninja_url.querySelector('svg.success').style.display =
'inline';
} catch (err) {
icon.display = 'none';
this.dom.vdo_ninja_url.querySelector('svg.failure').style.display =
'inline';
console.error('Umable to write to clipboard');
}

setTimeout(() => {
[...this.dom.vdo_ninja_url.querySelectorAll('svg.result')].forEach(
icon => (icon.style.display = 'none')
);
icon.style.display = 'inline';
copying = false;
}, 650);
};

let playing;
this.dom.vdo_ninja_url.querySelector('svg.action.vdo-play').onclick =
async () => {
if (playing) return;
playing = true;

const icon = this.dom.vdo_ninja_url.querySelector(
'svg.action.vdo-play'
);

icon.style.display = 'none';

const iframe = document.createElement('iframe');
iframe.setAttribute(
'allow',
'autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;midi;geolocation;gyroscope;'
);
iframe.src = this.dom.vdo_ninja_url.querySelector('a').href;

this.dom.images.append(iframe);

setTimeout(() => {
iframe.remove();
icon.style.display = 'inline';
playing = false;
}, 30000);
};

this.dom.win_btn.onclick = () => {
remoteAPI.setWinner(this.idx);
};
Expand Down Expand Up @@ -124,6 +189,36 @@ class Player {
this.dom.country_code_img.dataset.url.replace('{code}', country_code);
}

setVdoNinjaURL(url) {
if (!url) {
this.dom.vdo_ninja_url.querySelector('span').replaceChildren();
this.dom.vdo_ninja_url.style.display = 'none';
return;
}

const u = new URL(url);

const streamId = u.searchParams.get('view') || u.searchParams.get('push'); // just in case someone passed the push url

u.searchParams.delete('push');
u.searchParams.set('view', streamId);
u.searchParams.set('cover', 1);
u.searchParams.set('cleanviewer', 1);
u.searchParams.set('cleanoutput', 1);
u.searchParams.set('transparent', 1);
u.searchParams.set('autostart', 1);

const full_url = u.toString();

const a = document.createElement('a');
a.href = full_url;
a.target = '_blank';
a.textContent = url.replace(/^https?:\/\//, '').replace(/&.+$/, '');

this.dom.vdo_ninja_url.querySelector('span').replaceChildren(a);
this.dom.vdo_ninja_url.style.display = 'block';
}

setProducers(producers) {
this.dom.producers.innerHTML = '';

Expand Down Expand Up @@ -210,6 +305,10 @@ class Player {
this.dom.avatar_url.value = state.profile_image_url;
this.dom.avatar_img.src = state.profile_image_url;

if (state.vdo_ninja_url) {
this.setVdoNinjaURL(state.vdo_ninja_url);
}

this.dom.country_code_select.value = state.country_code;
this.setFlag(state.country_code);
}
Expand Down Expand Up @@ -295,6 +394,7 @@ function addPlayer() {
users: player_node.querySelector('.users select'),
name: player_node.querySelector('.name'),
avatar_url: player_node.querySelector('input.avatar'),
images: player_node.querySelector('.images'),
avatar_img: player_node.querySelector('img.avatar'),
country_code_select: player_node.querySelector('select.country_code'),
country_code_img: player_node.querySelector('img.country_code'),
Expand All @@ -306,6 +406,7 @@ function addPlayer() {
camera_restart_btn: player_node.querySelector('.camera_restart'),
camera_mirror_btn: player_node.querySelector('.camera_mirror'),
focus_player_btn: player_node.querySelector('.focus_player'),
vdo_ninja_url: player_node.querySelector('.vdo_ninja_url'),
});

players_node.appendChild(player_node);
Expand Down Expand Up @@ -393,6 +494,12 @@ function bootstrap() {
break;
}

case 'setVdoNinjaURL': {
const [pidx, url] = args;
players[pidx].setVdoNinjaURL(url);
break;
}

case 'setOwner': {
const owner = args[0];
const player_url = `${location.protocol}//${location.host}/room/u/${owner.login}/producer`;
Expand Down
26 changes: 25 additions & 1 deletion public/views/mp/league.html
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,15 @@
right: var(--offset);
}

.player_vid.focused {
width: 1920px;
height: 1080px;
top: 0;
left: -960px;
right: 960px;
z-index: 10;
}

#ticker {
position: absolute;
width: 1920px;
Expand Down Expand Up @@ -508,7 +517,22 @@
}, CYCLE_TDIFF * 1000);
}

const competition = new Competition(players);
let focusedPlayer = null;

const competition = new Competition(players, {
focusPlayer: pidx => {
if (focusedPlayer?.dom?._video_iframe) {
focusedPlayer.dom._video_iframe.classList.remove('focused');
}
if (pidx === null) {
focusedPlayer = null;
return;
} else if (players[pidx]) {
focusedPlayer = players[pidx];
focusedPlayer.dom._video_iframe?.classList.add('focused');
}
},
});

// expose API into window for debugging
window._players = players;
Expand Down
Loading

0 comments on commit c2fce02

Please sign in to comment.