diff --git a/.gitignore b/.gitignore index 829e6cc..9f73748 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ __pycache__/ stream/ *~ +title.txt diff --git a/anonstream/__init__.py b/anonstream/__init__.py index 313910c..9545afe 100644 --- a/anonstream/__init__.py +++ b/anonstream/__init__.py @@ -32,6 +32,8 @@ def create_app(config_file): 'SEGMENT_SEARCH_COOLDOWN': config['segments']['search_cooldown'], 'SEGMENT_SEARCH_TIMEOUT': config['segments']['search_timeout'], 'SEGMENT_STREAM_INITIAL_BUFFER': config['segments']['stream_initial_buffer'], + 'STREAM_TITLE': config['title']['file'], + 'STREAM_TITLE_CACHE_LIFETIME': config['title']['file_cache_lifetime'], 'DEFAULT_HOST_NAME': config['names']['broadcaster'], 'DEFAULT_ANON_NAME': config['names']['anonymous'], 'MAX_STATES': config['memory']['states'], diff --git a/anonstream/routes/nojs.py b/anonstream/routes/nojs.py index 7aacda9..5f70556 100644 --- a/anonstream/routes/nojs.py +++ b/anonstream/routes/nojs.py @@ -19,7 +19,7 @@ async def nojs_info(user): return await render_template( 'nojs_info.html', user=user, - title=get_stream_title(), + title=await get_stream_title(), uptime=get_stream_uptime(), ) diff --git a/anonstream/static/anonstream.js b/anonstream/static/anonstream.js index 7d4a8ca..2e23847 100644 --- a/anonstream/static/anonstream.js +++ b/anonstream/static/anonstream.js @@ -316,6 +316,12 @@ const disable_captcha = () => { chat_form_captcha_image.removeAttribute("src"); } +const set_title = (title) => { + const element = document.createElement("h1"); + element.innerText = title.replaceAll(/\r?\n/g, " "); + info_title.innerHTML = element.outerHTML; +} + let frozen_uptime = null; let frozen_uptime_received = null; const set_frozen_uptime = (x) => { @@ -357,7 +363,7 @@ const on_websocket_message = (event) => { case "init": console.log("ws init", receipt); - info_title.innerText = receipt.title; + set_title(receipt.title); set_frozen_uptime(receipt.uptime); update_uptime(); @@ -395,7 +401,7 @@ const on_websocket_message = (event) => { case "title": console.log("ws title", receipt); - info_title.innerText = receipt.title; + set_title(receipt.title); break; case "ack": diff --git a/anonstream/static/style.css b/anonstream/static/style.css index 93b84c5..227890b 100644 --- a/anonstream/static/style.css +++ b/anonstream/static/style.css @@ -68,8 +68,10 @@ noscript { float: right; font-size: 11pt; } -#info_js__title { +#info_js__title > h1 { + margin: 0; font-size: 18pt; + line-height: 1.125; overflow-wrap: anywhere; } #info_nojs { diff --git a/anonstream/stream.py b/anonstream/stream.py index d14942e..07472f7 100644 --- a/anonstream/stream.py +++ b/anonstream/stream.py @@ -1,9 +1,21 @@ import time -from anonstream.segments import get_playlist, Offline +import aiofiles +from quart import current_app -def get_stream_title(): - return 'Stream title' +from anonstream.segments import get_playlist, Offline +from anonstream.wrappers import ttl_cache_async + +CONFIG = current_app.config + +@ttl_cache_async(CONFIG['STREAM_TITLE_CACHE_LIFETIME']) +async def get_stream_title(): + try: + async with aiofiles.open(CONFIG['STREAM_TITLE']) as fp: + title = await fp.read(8192) + except FileNotFoundError: + title = '' + return title def get_stream_uptime(rounded=True): try: diff --git a/anonstream/templates/nojs_info.html b/anonstream/templates/nojs_info.html index 16fb18d..b9785a7 100644 --- a/anonstream/templates/nojs_info.html +++ b/anonstream/templates/nojs_info.html @@ -16,6 +16,7 @@ #title > h1 { margin: 0; font-size: 18pt; + line-height: 1.125; overflow-wrap: anywhere; } diff --git a/anonstream/websocket.py b/anonstream/websocket.py index 94e0bc4..b575aca 100644 --- a/anonstream/websocket.py +++ b/anonstream/websocket.py @@ -15,7 +15,7 @@ async def websocket_outbound(queue, user): payload = { 'type': 'init', 'nonce': generate_nonce(), - 'title': get_stream_title(), + 'title': await get_stream_title(), 'uptime': get_stream_uptime(), 'messages': get_all_messages_for_websocket(), 'users': get_all_users_for_websocket(), diff --git a/anonstream/wrappers.py b/anonstream/wrappers.py index 09d2769..a9756af 100644 --- a/anonstream/wrappers.py +++ b/anonstream/wrappers.py @@ -53,3 +53,24 @@ def ttl_cache(ttl): return wrapper return ttl_cache_specific + +def ttl_cache_async(ttl): + ''' + Async version of `ttl_cache`. Wraps zero-argument coroutines. + ''' + def ttl_cache_specific(f): + value, expires = None, None + + @wraps(f) + async def wrapper(): + nonlocal value, expires + + if expires is None or time.monotonic() >= expires: + value = await f() + expires = time.monotonic() + ttl + + return value + + return wrapper + + return ttl_cache_specific diff --git a/config.toml b/config.toml index 3d44def..47f07ee 100644 --- a/config.toml +++ b/config.toml @@ -12,6 +12,10 @@ search_cooldown = 0.25 search_timeout = 5.0 stream_initial_buffer = 3 +[title] +file = "title.txt" +file_cache_lifetime = 0.5 + [captcha] lifetime = 1800 fonts = [] diff --git a/title.txt b/title.txt new file mode 100644 index 0000000..dc8344c --- /dev/null +++ b/title.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet