A self hosted livestreaming server.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 
 
 
 
 
 
satyr/site/index.js

252 lines
8.8 KiB

async function render(path, s){
var context = await getContext();
if(!s)
history.pushState({}, context.sitename, location.protocol+'//'+location.host+path);
switch(path){
//nothing but context
case (path.match(/^\/about\/?$/) || {}).input:
document.body.innerHTML = nunjucks.render('about.njk', context);
modifyLinks();
break;
case (path.match(/^\/login\/?$/) || {}).input:
document.body.innerHTML = nunjucks.render('login.njk', context);
modifyLinks();
break;
case (path.match(/^\/register\/?$/) || {}).input:
if(!context.registration) window.location = '/';
document.body.innerHTML = nunjucks.render('registration.njk', context);
modifyLinks();
break;
case (path.match(/^\/changepwd\/?$/) || {}).input:
document.body.innerHTML = nunjucks.render('changepwd.njk', context);
modifyLinks();
break;
case (path.match(/^\/chat\/?$/) || {}).input:
document.body.innerHTML = nunjucks.render('chat.html', context);
modifyLinks();
break;
case (path.match(/^\/help\/?$/) || {}).input:
document.body.innerHTML = nunjucks.render('help.njk', context);
modifyLinks();
break;
//need to hit the API
case (path.match(/^\/users\/live\/?$/) || {}).input:
var list = JSON.parse(await makeRequest("POST", "/api/users/live", JSON.stringify({num: 50})));
document.body.innerHTML = nunjucks.render('live.njk', Object.assign({list: list.users}, context));
modifyLinks();
break;
case (path.match(/^\/users\/?$/) || {}).input:
var list = JSON.parse(await makeRequest("POST", "/api/users/all", JSON.stringify({num: 50})));
document.body.innerHTML = nunjucks.render('list.njk', Object.assign({list: list.users}, context));
modifyLinks();
break;
case (path.match(/^\/profile\/chat\/?$/) || {}).input:
if(!context.auth.name) window.location = '/login';
var config = JSON.parse(await makeRequest("GET", '/api/'+context.auth.name+'/config'));
config = {
integ: {
twitch: config.twitch,
xmpp: config.xmpp,
irc: config.irc,
discord: config.discord
}
};
document.body.innerHTML = nunjucks.render('chat_integ.njk', Object.assign(config, context));
modifyLinks();
break;
case (path.match(/^\/profile\/?$/) || {}).input:
if(!context.auth.name) window.location = '/login';
var config = JSON.parse(await makeRequest("GET", '/api/'+context.auth.name+'/config'));
config = {
meta: {
title: config.title,
about: config.about
},
rflag: {record_flag: config.record_flag},
twitch: config.twitch_mirror
};
document.body.innerHTML = nunjucks.render('profile.njk', Object.assign(config, context));
modifyLinks();
break;
//parsing slugs
case (path.match(/^\/invite\//) || {}).input: // /invite/:code
document.body.innerHTML = nunjucks.render('invite.njk', Object.assign({icode: path.substring(8)}, context));
modifyLinks();
break;
//slugs and API
case (path.match(/^\/users\/.+\/?$/) || {}).input: // /users/:user
if(path.substring(path.length - 1).indexOf('/') !== -1)
var usr = path.substring(7, path.length - 1);
else var usr = path.substring(7);
var config = JSON.parse(await makeRequest("GET", '/api/'+usr+'/config'));
if(!config.title){document.body.innerHTML = nunjucks.render('404.njk', context); break;}
document.body.innerHTML = nunjucks.render('user.njk', Object.assign({about: config.about, title: config.title, username: config.username, viewers: config.viewers}, context));
modifyLinks();
initPlayer(usr);
updateViewers();
break;
case (path.match(/^\/vods\/.+\/manage\/?$/) || {}).input: // /vods/:user/manage
var usr = path.substring(6, (path.length - 7));
if(context.auth.name !== usr) window.location = '/vods/'+usr;
var vods = JSON.parse(await makeRequest("GET", '/api/'+usr+'/vods'));
document.body.innerHTML = nunjucks.render('managevods.njk', Object.assign({user: usr, list: vods.vods.filter(fn => fn.name.endsWith('.mp4'))}, context));
modifyLinks();
break;
case (path.match(/^\/vods\/.+\/?$/) || {}).input: // /vods/:user
if(path.substring(path.length - 1).indexOf('/') !== -1)
var usr = path.substring(6, path.length - 1);
else var usr = path.substring(6);
var vods = JSON.parse(await makeRequest("GET", '/api/'+usr+'/vods'));
document.body.innerHTML = nunjucks.render('vods.njk', Object.assign({user: usr, list: vods.vods.filter(fn => fn.name.endsWith('.mp4'))}, context));
modifyLinks();
break;
//root
case "/":
render('/users/live');
modifyLinks();
break;
case "":
render('/users/live');
modifyLinks();
break;
case "/index.html":
render('/users/live');
modifyLinks();
break;
//404
default:
document.body.innerHTML = nunjucks.render('404.njk', context);
modifyLinks();
}
}
window.addEventListener('popstate', (event) => {
render(document.location.pathname, true);
});
async function getContext(){
var info = JSON.parse(await makeRequest('GET', '/api/instance/info'));
info.sitename = info.name;
info.name = null;
info.auth = {
is: document.cookie.match(/^(.*;)?\s*X-Auth-As\s*=\s*[^;]+(.*)?$/) !== null,
name: parseCookie(document.cookie)['X-Auth-As']
}
return info;
}
function makeRequest(method, url, payload) {
return new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
!payload ? xhr.send() : xhr.send(payload);
});
}
function parseCookie(c){
if(typeof(c) !== 'string' || !c.includes('=')) return {};
return Object.assign({[c.split('=')[0].trim()]:c.split('=')[1].split(';')[0].trim()}, parseCookie(c.split(/;(.+)/)[1]));
}
function handleLoad() {
var r = JSON.parse(document.getElementById('responseFrame').contentDocument.documentElement.textContent).success
if (typeof(r) !== 'undefined') window.location.href = '/profile'
}
function modifyLinks() {
for (var ls = document.links, numLinks = ls.length, i=0; i<numLinks; i++){
if(ls[i].href.indexOf(location.protocol+'//'+location.host) !== -1 && ls[i].href.match(new RegExp(/\/[A-Za-z0-9_\-~]+\.[A-Za-z0-9]+$/)) === null && ls[i].href.match(new RegExp(/\/chat\?room=.+$/)) === null) {
//should be a regular link
ls[i].setAttribute('onclick', 'return internalLink(\"'+ls[i].href.substring((location.protocol+'//'+location.host).length)+'\")');
}
}
}
async function updateViewers(){
let vc = document.getElementById('viewercount');
if(!vc) return false;
let path = window.location.pathname;
if(path.substring(path.length - 1).indexOf('/') !== -1)
var usr = path.substring(7, path.length - 1);
else var usr = path.substring(7);
let viewers = JSON.parse(await makeRequest("GET", "/api/"+usr+"/config")).viewers;
vc.innerHTML = "[Viewers: "+viewers+"]";
setTimeout(updateViewers, 2000);
}
function internalLink(path){
this.render(path);
return false;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
var shakaPolyFilled = false;
async function initPlayer(usr) {
var manifestUri = document.location.protocol+'//'+document.location.host+'/live/'+usr+'/index.mpd';
if(!shakaPolyFilled){
shaka.polyfill.installAll();
shakaPolyFilled = true;
}
var live = JSON.parse(await makeRequest("GET", "/api/"+usr+"/config")).live;
if(live){
// Create a Player instance.
const video = document.getElementById('video');
const player = new shaka.Player(video);
// Listen for error events.
player.addEventListener('error', onErrorEvent);
video.addEventListener('play', () => {
document.getElementById('playbtn').style.visibility = 'hidden';
});
video.addEventListener('pause', () => {
document.getElementById('playbtn').style.visibility = 'visible';
});
// Try to load a manifest.
// This is an asynchronous process.
try {
await player.load(manifestUri);
// This runs if the asynchronous load is successful.
console.log('The video has now been loaded!');
} catch (e) {
// onError is executed if the asynchronous load fails.
onError(e);
}
} else {
if(document.getElementById('video') !== null)
setTimeout(initPlayer, 5000, usr);
}
}
function onErrorEvent(event) {
// Extract the shaka.util.Error object from the event.
onError(event.detail);
}
function onError(error) {
// Log the error.
console.error('Error code', error.code, 'object', error);
}
function newPopup(url) {
popupWindow = window.open(
url,'popUpWindow','height=700,width=450,scrollbars=yes,toolbar=no,menubar=no,location=no,directories=no,status=yes')
}