viewercount #28
|
@ -55,7 +55,7 @@ play from: rtmp://example.com/live/username or https://example.com/live/username
|
||||||
|
|
||||||
## /api/users/live/
|
## /api/users/live/
|
||||||
|
|
||||||
Returns the usernames and stream titles of 10 users who are currently streaming
|
Returns the usernames and stream titles of 10 users who are currently streaming, and how many viewers they have.
|
||||||
|
|
||||||
**Method**: POST
|
**Method**: POST
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ The array will be wrapped in a JSON object under the key 'users'.
|
||||||
|
|
||||||
Same as above, but returns all users regardless of whether they are streaming and if they're streaming or not. Also returns a value 'live' indicating whether a user is currently streaming.
|
Same as above, but returns all users regardless of whether they are streaming and if they're streaming or not. Also returns a value 'live' indicating whether a user is currently streaming.
|
||||||
|
|
||||||
**Example**: `{users: [{username:"foo", title:"bar", live:1}] }`
|
**Example**: `{users: [{username:"foo", title:"bar", live:1, viewers:10}] }`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -217,9 +217,9 @@ Get information about the specified user.
|
||||||
|
|
||||||
**Parameters**: user
|
**Parameters**: user
|
||||||
|
|
||||||
**Response**: Returns a JSON object with available information about the user. If the user is authenticated and searching for their own information, will return all available information. Otherwise it will return only the stream title, bio, and whether the stream is live. In the case of searching for a user that does not exist, the returned object will contain only the username searched for.
|
**Response**: Returns a JSON object with available information about the user. If the user is authenticated and searching for their own information, will return all available information. Otherwise it will return only the stream title, bio, viewers and whether the stream is live. In the case of searching for a user that does not exist, the returned object will contain only the username searched for.
|
||||||
|
|
||||||
**Example**: `{username: "foo", title: "bar", about: "This is an example bio", live: 0}`
|
**Example**: `{username: "foo", title: "bar", about: "This is an example bio", live: 0, viewers: 10}`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -80,9 +80,10 @@ async function render(path, s){
|
||||||
else var usr = path.substring(7);
|
else var usr = path.substring(7);
|
||||||
var config = JSON.parse(await makeRequest("GET", '/api/'+usr+'/config'));
|
var config = JSON.parse(await makeRequest("GET", '/api/'+usr+'/config'));
|
||||||
if(!config.title){document.body.innerHTML = nunjucks.render('404.njk', context); break;}
|
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}, context));
|
document.body.innerHTML = nunjucks.render('user.njk', Object.assign({about: config.about, title: config.title, username: config.username, viewers: config.viewers}, context));
|
||||||
modifyLinks();
|
modifyLinks();
|
||||||
initPlayer(usr);
|
initPlayer(usr);
|
||||||
|
updateViewers();
|
||||||
break;
|
break;
|
||||||
case (path.match(/^\/vods\/.+\/manage\/?$/) || {}).input: // /vods/:user/manage
|
case (path.match(/^\/vods\/.+\/manage\/?$/) || {}).input: // /vods/:user/manage
|
||||||
var usr = path.substring(6, (path.length - 7));
|
var usr = path.substring(6, (path.length - 7));
|
||||||
|
@ -177,6 +178,18 @@ function modifyLinks() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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){
|
function internalLink(path){
|
||||||
this.render(path);
|
this.render(path);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -86,7 +86,7 @@ async function getConfig(username: string, all?: boolean): Promise<object>{
|
||||||
if(all) {
|
if(all) {
|
||||||
let users = await db.query('SELECT stream_key,record_flag FROM users WHERE username='+db.raw.escape(username));
|
let users = await db.query('SELECT stream_key,record_flag FROM users WHERE username='+db.raw.escape(username));
|
||||||
if(users[0]) Object.assign(t, users[0]);
|
if(users[0]) Object.assign(t, users[0]);
|
||||||
let usermeta = await db.query('SELECT title,about,live FROM user_meta WHERE username='+db.raw.escape(username));
|
let usermeta = await db.query('SELECT title,about,live,viewers FROM user_meta WHERE username='+db.raw.escape(username));
|
||||||
if(usermeta[0]) Object.assign(t, usermeta[0]);
|
if(usermeta[0]) Object.assign(t, usermeta[0]);
|
||||||
let ci = await db.query('SELECT irc,xmpp,twitch,discord FROM chat_integration WHERE username='+db.raw.escape(username));
|
let ci = await db.query('SELECT irc,xmpp,twitch,discord FROM chat_integration WHERE username='+db.raw.escape(username));
|
||||||
if(ci[0]) Object.assign(t, ci[0]);
|
if(ci[0]) Object.assign(t, ci[0]);
|
||||||
|
@ -94,7 +94,7 @@ async function getConfig(username: string, all?: boolean): Promise<object>{
|
||||||
if(tw[0]) t['twitch_mirror'] = Object.assign({}, tw[0]);
|
if(tw[0]) t['twitch_mirror'] = Object.assign({}, tw[0]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let um = await db.query('SELECT title,about,live FROM user_meta WHERE username='+db.raw.escape(username));
|
let um = await db.query('SELECT title,about,live,viewers FROM user_meta WHERE username='+db.raw.escape(username));
|
||||||
if(um[0]) Object.assign(t, um[0]);
|
if(um[0]) Object.assign(t, um[0]);
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
|
|
|
@ -28,6 +28,7 @@ async function init() {
|
||||||
//it causes problems
|
//it causes problems
|
||||||
//Live flags in the database stay live
|
//Live flags in the database stay live
|
||||||
await db.query('update user_meta set live=false');
|
await db.query('update user_meta set live=false');
|
||||||
|
await db.query('update user_meta set viewers=0');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bringUpToDate(): Promise<void>{
|
async function bringUpToDate(): Promise<void>{
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import * as db from "../database";
|
||||||
|
|
||||||
|
async function run () {
|
||||||
|
await db.query('ALTER TABLE user_meta ADD viewers int UNSIGNED DEFAULT 0 AFTER live');
|
||||||
|
await db.query('INSERT INTO db_meta (version) VALUES (4)');
|
||||||
|
}
|
||||||
|
|
||||||
|
export { run }
|
|
@ -187,7 +187,7 @@ async function initAPI() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.post('/api/users/live', (req, res) => {
|
app.post('/api/users/live', (req, res) => {
|
||||||
let qs = 'SELECT username,title FROM user_meta WHERE live=1';
|
let qs = 'SELECT username,title,viewers FROM user_meta WHERE live=1';
|
||||||
|
|
||||||
if(req.body.sort) {
|
if(req.body.sort) {
|
||||||
switch (req.body.sort) {
|
switch (req.body.sort) {
|
||||||
|
@ -217,7 +217,7 @@ async function initAPI() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.post('/api/users/all', (req, res) => {
|
app.post('/api/users/all', (req, res) => {
|
||||||
let qs = 'SELECT username,title,live FROM user_meta';
|
let qs = 'SELECT username,title,live,viewers FROM user_meta';
|
||||||
|
|
||||||
if(req.body.sort) {
|
if(req.body.sort) {
|
||||||
switch (req.body.sort) {
|
switch (req.body.sort) {
|
||||||
|
@ -476,7 +476,7 @@ async function initSite(openReg) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
app.get('/users/:user', (req, res) => {
|
app.get('/users/:user', (req, res) => {
|
||||||
db.query('select username,title,about from user_meta where username='+db.raw.escape(req.params.user)).then((result) => {
|
db.query('select username,title,about,viewers from user_meta where username='+db.raw.escape(req.params.user)).then((result) => {
|
||||||
if(result[0]){
|
if(result[0]){
|
||||||
if(tryDecode(req.cookies.Authorization)) {
|
if(tryDecode(req.cookies.Authorization)) {
|
||||||
res.render('user.njk', Object.assign(result[0], {auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
res.render('user.njk', Object.assign(result[0], {auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf));
|
||||||
|
@ -602,6 +602,7 @@ async function initChat() {
|
||||||
}
|
}
|
||||||
socket.join(data);
|
socket.join(data);
|
||||||
io.to(data).emit('JOINED', {nick: socket.nick, room: data});
|
io.to(data).emit('JOINED', {nick: socket.nick, room: data});
|
||||||
|
db.query('update user_meta set viewers = viewers + 1 where username='+db.raw.escape(data));
|
||||||
}
|
}
|
||||||
else socket.emit('ALERT', 'Room does not exist');
|
else socket.emit('ALERT', 'Room does not exist');
|
||||||
});
|
});
|
||||||
|
@ -622,12 +623,14 @@ async function initChat() {
|
||||||
});
|
});
|
||||||
socket.on('LEAVEROOM', (data) => {
|
socket.on('LEAVEROOM', (data) => {
|
||||||
io.to(data).emit('LEFT', {nick: socket.nick, room: data});
|
io.to(data).emit('LEFT', {nick: socket.nick, room: data});
|
||||||
|
db.query('update user_meta set viewers = viewers - 1 where username='+db.raw.escape(data));
|
||||||
socket.leave(data);
|
socket.leave(data);
|
||||||
});
|
});
|
||||||
socket.on('disconnecting', (reason) => {
|
socket.on('disconnecting', (reason) => {
|
||||||
let rooms = Object.keys(socket.rooms);
|
let rooms = Object.keys(socket.rooms);
|
||||||
for(let i=1;i<rooms.length;i++){
|
for(let i=1;i<rooms.length;i++){
|
||||||
io.to(rooms[i]).emit('ALERT', socket.nick+' disconnected');
|
io.to(rooms[i]).emit('ALERT', socket.nick+' disconnected');
|
||||||
|
db.query('update user_meta set viewers = viewers - 1 where username='+db.raw.escape(rooms[i]));
|
||||||
}
|
}
|
||||||
if(Array.isArray(store.get(socket.nick))) {
|
if(Array.isArray(store.get(socket.nick))) {
|
||||||
store.set(socket.nick, store.get(socket.nick).filter(item => item !== socket.id))
|
store.set(socket.nick, store.get(socket.nick).filter(item => item !== socket.id))
|
||||||
|
|
|
@ -119,10 +119,19 @@ function init () {
|
||||||
if(app === config['media']['publicEndpoint']) {
|
if(app === config['media']['publicEndpoint']) {
|
||||||
if(keystore[key]){
|
if(keystore[key]){
|
||||||
session.playStreamPath = '/'+config['media']['privateEndpoint']+'/'+keystore[key];
|
session.playStreamPath = '/'+config['media']['privateEndpoint']+'/'+keystore[key];
|
||||||
|
// increment viewer count
|
||||||
|
db.query('update user_meta set viewers = viewers + 1 where username='+db.raw.escape(key));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
nms.on('donePlay', (id, StreamPath, args) => {
|
||||||
|
let session = nms.getSession(id);
|
||||||
|
let app: string = StreamPath.split("/")[1];
|
||||||
|
let key: string = StreamPath.split("/")[2];
|
||||||
|
// decrement viewer count
|
||||||
|
db.query('update user_meta,users set user_meta.viewers = user_meta.viewers - 1 where users.stream_key='+db.raw.escape(key));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function transCommand(user: string, key: string): Promise<string[]>{
|
async function transCommand(user: string, key: string): Promise<string[]>{
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
shakaPolyFilled = false;
|
shakaPolyFilled = false;
|
||||||
var manifestUri = document.location.protocol+'//'+document.location.host+'/live/{{ username }}/index.mpd';
|
var manifestUri = document.location.protocol+'//'+document.location.host+'/live/{{ username }}/index.mpd';
|
||||||
async function initPlayer() {
|
async function initPlayer() {
|
||||||
console.log('Trying to initialize player.');
|
|
||||||
if(!shakaPolyFilled){
|
if(!shakaPolyFilled){
|
||||||
shaka.polyfill.installAll();
|
shaka.polyfill.installAll();
|
||||||
shakaPolyFilled = true;
|
shakaPolyFilled = true;
|
||||||
|
@ -58,9 +57,22 @@ function newPopup(url) {
|
||||||
popupWindow = window.open(
|
popupWindow = window.open(
|
||||||
url,'popUpWindow','height=700,width=450,scrollbars=yes,toolbar=no,menubar=no,location=no,directories=no,status=yes')
|
url,'popUpWindow','height=700,width=450,scrollbars=yes,toolbar=no,menubar=no,location=no,directories=no,status=yes')
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</br>
|
</br>
|
||||||
<span style="float: left;font-size: large;"><a href="/live/{{ username }}/index.mpd">{{ username }}</a> | {{ title | escape }}</b></span><span style="float: right;font-size: large;"> Links | <a href="rtmp://{{ domain }}/live/{{ username }}">Watch</a> <a href="JavaScript:newPopup('/chat?room={{ username }}');">Chat</a> <a href="/vods/{{ username }}">VODs</a></span>
|
<span style="float: left;font-size: large;"><a href="/live/{{ username }}/index.mpd">{{ username }}</a> | {{ title | escape }}<span id="viewercount" onload="updateViewers">[Viewers: {{viewers}}]</span>
|
||||||
|
<script>updateViewers()</script>
|
||||||
|
</b></span><span style="float: right;font-size: large;"> Links | <a href="rtmp://{{ domain }}/live/{{ username }}">Watch</a> <a href="JavaScript:newPopup('/chat?room={{ username }}');">Chat</a> <a href="/vods/{{ username }}">VODs</a></span>
|
||||||
<div id="jscontainer">
|
<div id="jscontainer">
|
||||||
<div id="jschild" style="width: 70%;height: 100%;position: relative;">
|
<div id="jschild" style="width: 70%;height: 100%;position: relative;">
|
||||||
<image id="playbtn" src="/play.svg" alt="Play Stream" style="width:100%;height:100%;width: 950px;height: 534px;position: absolute;" onclick="document.getElementById('video').paused ? document.getElementById('video').play() : document.getElementById('video') .pause();"></image>
|
<image id="playbtn" src="/play.svg" alt="Play Stream" style="width:100%;height:100%;width: 950px;height: 534px;position: absolute;" onclick="document.getElementById('video').paused ? document.getElementById('video').play() : document.getElementById('video') .pause();"></image>
|
||||||
|
|
Reference in New Issue