WS: combine `uptime` and `viewership` into `stats`

If the stream is offline, `stats` is null, otherwise it contains uptime and
viewership.
このコミットが含まれているのは:
n9k 2022-02-28 11:01:24 +00:00
コミット 7962de87e3
5個のファイルの変更66行の追加50行の削除

ファイルの表示

@ -2,7 +2,7 @@ from quart import current_app, request, render_template, redirect, url_for, esca
from anonstream.captcha import get_random_captcha_digest_for
from anonstream.chat import add_chat_message, Rejected
from anonstream.stream import get_stream_title, get_stream_uptime, get_stream_viewership
from anonstream.stream import get_stream_title, get_stream_uptime_and_viewership
from anonstream.user import add_state, pop_state, try_change_appearance, get_users_by_presence, Presence, verify, deverify, BadCaptcha
from anonstream.routes.wrappers import with_user_from, render_template_with_etag
from anonstream.helpers.chat import get_scrollback
@ -16,11 +16,12 @@ USERS_BY_TOKEN = current_app.users_by_token
@current_app.route('/info.html')
@with_user_from(request)
async def nojs_info(user):
uptime, viewership = get_stream_uptime_and_viewership()
return await render_template(
'nojs_info.html',
user=user,
viewership=get_stream_viewership(),
uptime=get_stream_uptime(),
viewership=viewership,
uptime=uptime,
title=await get_stream_title(),
)

ファイルの表示

@ -159,6 +159,8 @@ const create_and_add_chat_message = (object) => {
}
let users = {};
let stats = null;
let stats_received = null;
let default_name = {true: "Broadcaster", false: "Anonymous"};
let max_chat_scrollback = 256;
const tidy_stylesheet = ({stylesheet, selector_regex, ignore_condition}) => {
@ -370,20 +372,14 @@ const set_title = (title) => {
info_title.innerHTML = element.outerHTML;
}
let frozen_uptime = null;
let frozen_uptime_received = null;
const set_frozen_uptime = (x) => {
frozen_uptime = x;
frozen_uptime_received = new Date();
}
const update_uptime = () => {
if (frozen_uptime_received === null) {
if (stats_received === null) {
return;
} else if (frozen_uptime === null) {
} else if (stats === null) {
info_uptime.innerText = "";
} else {
const frozen_uptime_received_ago = (new Date() - frozen_uptime_received) / 1000;
const uptime = Math.round(frozen_uptime + frozen_uptime_received_ago);
const stats_received_ago = (new Date() - stats_received) / 1000;
const uptime = Math.round(stats.uptime + stats_received_ago);
const s = Math.round(uptime % 60);
const m = Math.floor(uptime / 60) % 60
@ -400,8 +396,18 @@ const update_uptime = () => {
}
setInterval(update_uptime, 1000); // always update uptime
const set_viewership = (n) => {
info_viewership.innerText = n === null ? "" : `${n} viewers`;
const update_viewership = () => {
info_viewership.innerText = stats === null ? "" : `${stats.viewership} viewers`;
}
const update_stats = () => {
if (stats === null) {
update_viewership();
update_uptime();
} else {
update_uptime();
update_viewership();
}
}
const update_users_list = () => {
@ -474,12 +480,10 @@ const on_websocket_message = (event) => {
// set title
set_title(receipt.title);
// set viewership
set_viewership(receipt.viewership);
// set uptime
set_frozen_uptime(receipt.uptime);
update_uptime();
// update stats (uptime/viewership)
stats = receipt.stats;
stats_received = new Date();
update_stats();
// chat form nonce
chat_form_nonce.value = receipt.nonce;
@ -530,14 +534,10 @@ const on_websocket_message = (event) => {
if (receipt.title !== undefined) {
set_title(receipt.title);
}
if (receipt.uptime !== undefined) {
set_frozen_uptime(receipt.uptime);
update_uptime();
}
if (receipt.viewership === 0 && frozen_uptime === null) {
set_viewership(null);
} else if (receipt.viewership !== undefined) {
set_viewership(receipt.viewership);
if (receipt.stats !== undefined) {
stats = receipt.stats;
stats_received = new Date();
update_stats();
}
break;

ファイルの表示

@ -37,16 +37,26 @@ def get_stream_uptime(rounded=True):
return uptime
@with_timestamp
def get_stream_viewership(timestamp):
def get_raw_viewership(timestamp):
users = get_watching_users(timestamp)
return max(
map(operator.itemgetter(0), zip(itertools.count(1), users)),
default=0,
)
def get_stream_viewership_or_none(uptime):
viewership = get_stream_viewership()
return uptime and viewership
def get_stream_uptime_and_viewership(for_websocket=False):
uptime = get_stream_uptime()
if not for_websocket:
viewership = None if uptime is None else get_raw_viewership()
result = (uptime, viewership)
elif uptime is None:
result = None
else:
result = {
'uptime': uptime,
'viewership': get_raw_viewership(),
}
return result
def is_online():
try:

ファイルの表示

@ -5,7 +5,7 @@ from functools import wraps
from quart import current_app
from anonstream.broadcast import broadcast, broadcast_users_update
from anonstream.stream import is_online, get_stream_title, get_stream_uptime, get_stream_viewership_or_none
from anonstream.stream import is_online, get_stream_title, get_stream_uptime_and_viewership
from anonstream.user import get_sunsettable_users
from anonstream.wrappers import with_timestamp
@ -94,21 +94,22 @@ async def t_broadcast_users_update(iteration):
async def t_broadcast_stream_info_update(iteration):
if iteration == 0:
title = await get_stream_title()
uptime = get_stream_uptime()
viewership = get_stream_viewership_or_none(uptime)
uptime, viewership = get_stream_uptime_and_viewership()
current_app.stream_title = title
current_app.stream_uptime = uptime
current_app.stream_viewership = viewership
else:
payload = {}
# Check if the stream title has changed
title = await get_stream_title()
uptime, viewership = get_stream_uptime_and_viewership()
# Check if the stream title has changed
if current_app.stream_title != title:
current_app.stream_title = title
payload['title'] = title
# Check if the stream uptime has changed more or less than expected
# Check if the stream uptime has changed unexpectedly
if current_app.stream_uptime is None:
expected_uptime = None
else:
@ -116,20 +117,27 @@ async def t_broadcast_stream_info_update(iteration):
current_app.stream_uptime
+ CONFIG['TASK_PERIOD_BROADCAST_STREAM_INFO_UPDATE']
)
uptime = get_stream_uptime()
current_app.stream_uptime = uptime
if uptime is None and expected_uptime is None:
pass
stats_changed = False
elif uptime is None or expected_uptime is None:
payload['uptime'] = uptime
elif abs(uptime - expected_uptime) >= 0.0625:
payload['uptime'] = uptime
stats_changed = True
else:
stats_changed = abs(uptime - expected_uptime) >= 0.0625
# Check if viewership has changed
viewership = get_stream_viewership_or_none(uptime)
if current_app.stream_viewership != viewership:
current_app.stream_viewership = viewership
payload['viewership'] = viewership
stats_changed = True
if stats_changed:
if uptime is None:
payload['stats'] = None
else:
payload['stats'] = {
'uptime': uptime,
'viewership': viewership,
}
if payload:
broadcast(USERS, payload={'type': 'info', **payload})

ファイルの表示

@ -3,7 +3,7 @@ import json
from quart import current_app, websocket
from anonstream.stream import get_stream_title, get_stream_uptime, get_stream_viewership_or_none
from anonstream.stream import get_stream_title, get_stream_uptime_and_viewership
from anonstream.captcha import get_random_captcha_digest_for
from anonstream.chat import get_all_messages_for_websocket, add_chat_message, Rejected
from anonstream.user import get_all_users_for_websocket, see, verify, deverify, BadCaptcha
@ -13,14 +13,11 @@ from anonstream.utils.websocket import parse_websocket_data, Malformed
CONFIG = current_app.config
async def websocket_outbound(queue, user):
uptime = get_stream_uptime()
viewership = get_stream_viewership_or_none(uptime)
payload = {
'type': 'init',
'nonce': generate_nonce(),
'title': await get_stream_title(),
'uptime': uptime,
'viewership': viewership,
'stats': get_stream_uptime_and_viewership(for_websocket=True),
'messages': get_all_messages_for_websocket(),
'users': get_all_users_for_websocket(),
'default': {