2021-07-05 20:45:32 +09:00
|
|
|
from flask import current_app, render_template, send_from_directory, request, abort, Response, redirect, url_for, make_response, send_file
|
2021-04-13 22:13:40 +09:00
|
|
|
from werkzeug import wrap_file
|
2021-04-10 02:26:29 +09:00
|
|
|
import os
|
|
|
|
import time
|
|
|
|
import secrets
|
2021-04-12 00:15:25 +09:00
|
|
|
import json
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
import datetime
|
2021-05-16 09:54:21 +09:00
|
|
|
import re
|
2021-07-05 20:45:32 +09:00
|
|
|
import toml
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
import website.chat as chat
|
|
|
|
import website.viewership as viewership
|
|
|
|
import website.utils.stream as stream
|
2021-07-05 20:45:32 +09:00
|
|
|
from website.constants import DIR_STATIC, DIR_STATIC_EXTERNAL, VIDEOJS_ENABLED_BY_DEFAULT, SEGMENT_INIT, CHAT_SCROLLBACK, BROADCASTER_COLOUR, BROADCASTER_TOKEN, SEGMENTS_DIR, VIEW_COUNTING_PERIOD, CONFIG, CONFIG_FILE, NOTES, N_NONE, MESSAGE_MAX_LENGTH
|
2021-04-16 14:53:25 +09:00
|
|
|
from website.concatenate import ConcatenatedSegments, resolve_segment_offset
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-05-16 09:54:21 +09:00
|
|
|
RE_WHITESPACE = re.compile(r'\s+')
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
viewers = viewership.viewers
|
2021-04-10 14:42:44 +09:00
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
def new_token():
|
|
|
|
return secrets.token_hex(8)
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-15 20:37:04 +09:00
|
|
|
def get_token(form=False):
|
|
|
|
token = (request.form if form else request.args).get('token')
|
|
|
|
if token == None or len(token) >= 256 or len(token) < 4:
|
|
|
|
token = request.cookies.get('token')
|
|
|
|
if token and (len(token) >= 256 or len(token) < 4):
|
|
|
|
token = None
|
|
|
|
return token
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/')
|
2021-04-10 02:26:29 +09:00
|
|
|
def index(token=None):
|
2021-04-15 20:37:04 +09:00
|
|
|
token = token or get_token() or new_token()
|
2021-04-14 01:53:16 +09:00
|
|
|
try:
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
viewership.video_was_corrupted.remove(token)
|
2021-04-14 01:53:16 +09:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2021-05-15 13:35:48 +09:00
|
|
|
use_videojs = bool(request.args.get('videojs', default=int(VIDEOJS_ENABLED_BY_DEFAULT), type=int))
|
2021-04-15 20:37:04 +09:00
|
|
|
viewership.made_request(token)
|
2021-04-16 14:53:25 +09:00
|
|
|
|
|
|
|
response = render_template('index.html',
|
|
|
|
token=token,
|
|
|
|
use_videojs=use_videojs,
|
2021-05-19 15:16:13 +09:00
|
|
|
start_number=resolve_segment_offset() if stream.is_online() else 0,
|
2021-07-05 20:45:32 +09:00
|
|
|
hls_time=CONFIG['stream']['hls_time'])
|
2021-04-16 14:53:25 +09:00
|
|
|
response = Response(response) # TODO: add a view of the chat only, either as an arg here or another route
|
2021-04-10 02:26:29 +09:00
|
|
|
response.set_cookie('token', token)
|
|
|
|
return response
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/broadcaster')
|
|
|
|
@current_app.auth.login_required
|
2021-04-10 02:26:29 +09:00
|
|
|
def broadcaster():
|
2021-04-13 22:13:40 +09:00
|
|
|
return index(token=BROADCASTER_TOKEN)
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/stream.m3u8')
|
2021-04-10 02:26:29 +09:00
|
|
|
def playlist():
|
2021-04-14 01:53:16 +09:00
|
|
|
if not stream.is_online():
|
2021-04-12 21:00:26 +09:00
|
|
|
return abort(404)
|
2021-04-16 19:37:12 +09:00
|
|
|
token = get_token()
|
2021-04-14 01:53:16 +09:00
|
|
|
try:
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
viewership.video_was_corrupted.remove(token)
|
2021-04-14 01:53:16 +09:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2021-05-19 12:00:36 +09:00
|
|
|
viewership.made_request(token)
|
2021-04-15 20:37:04 +09:00
|
|
|
|
|
|
|
try:
|
2021-07-05 20:45:32 +09:00
|
|
|
token_playlist = stream.TokenPlaylist(token)
|
2021-04-15 20:37:04 +09:00
|
|
|
except FileNotFoundError:
|
|
|
|
return abort(404)
|
2021-07-05 20:45:32 +09:00
|
|
|
response = send_file(token_playlist, mimetype='application/x-mpegURL', add_etags=False)
|
2021-04-10 02:26:29 +09:00
|
|
|
response.headers['Cache-Control'] = 'no-cache'
|
|
|
|
return response
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route(f'/{SEGMENT_INIT}')
|
2021-04-10 23:36:16 +09:00
|
|
|
def segment_init():
|
2021-04-14 01:53:16 +09:00
|
|
|
if not stream.is_online():
|
2021-04-12 21:00:26 +09:00
|
|
|
return abort(404)
|
2021-04-14 01:53:16 +09:00
|
|
|
|
2021-04-15 20:37:04 +09:00
|
|
|
token = get_token() or new_token()
|
2021-04-14 01:53:16 +09:00
|
|
|
try:
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
viewership.video_was_corrupted.remove(token)
|
2021-04-14 01:53:16 +09:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2021-05-19 12:00:36 +09:00
|
|
|
viewership.made_request(token)
|
2021-04-10 23:36:16 +09:00
|
|
|
response = send_from_directory(SEGMENTS_DIR, f'init.mp4', add_etags=False)
|
|
|
|
response.headers['Cache-Control'] = 'no-cache'
|
2021-04-14 01:53:16 +09:00
|
|
|
response.set_cookie('token', token)
|
2021-04-10 23:36:16 +09:00
|
|
|
return response
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/stream<int:n>.m4s')
|
2021-04-10 23:36:16 +09:00
|
|
|
def segment_arbitrary(n):
|
2021-04-14 01:53:16 +09:00
|
|
|
if not stream.is_online():
|
2021-04-12 21:00:26 +09:00
|
|
|
return abort(404)
|
2021-04-14 01:53:16 +09:00
|
|
|
|
2021-04-15 20:37:04 +09:00
|
|
|
token = get_token()
|
2021-04-14 01:53:16 +09:00
|
|
|
try:
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
viewership.video_was_corrupted.remove(token)
|
2021-04-14 01:53:16 +09:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2021-04-13 22:13:40 +09:00
|
|
|
viewership.view_segment(n, token)
|
2021-04-10 23:36:16 +09:00
|
|
|
response = send_from_directory(SEGMENTS_DIR, f'stream{n}.m4s', add_etags=False)
|
2021-04-14 23:31:59 +09:00
|
|
|
response.headers['Cache-Control'] = 'no-cache'
|
2021-04-10 02:26:29 +09:00
|
|
|
return response
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/stream.mp4')
|
|
|
|
def segments():
|
2021-04-14 01:53:16 +09:00
|
|
|
if not stream.is_online():
|
2021-04-12 21:00:26 +09:00
|
|
|
return abort(404)
|
2021-04-15 21:45:22 +09:00
|
|
|
token = get_token() or new_token()
|
2021-04-14 01:53:16 +09:00
|
|
|
try:
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
viewership.video_was_corrupted.remove(token)
|
2021-04-14 01:53:16 +09:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2021-05-19 12:00:36 +09:00
|
|
|
viewership.made_request(token)
|
2021-04-16 14:53:25 +09:00
|
|
|
|
2021-07-05 20:45:32 +09:00
|
|
|
start_number = request.args.get('segment', type=int)
|
|
|
|
if start_number == None:
|
|
|
|
start_number = resolve_segment_offset()
|
2021-04-16 14:53:25 +09:00
|
|
|
try:
|
|
|
|
concatenated_segments = ConcatenatedSegments(start_number=start_number,
|
|
|
|
segment_hook=lambda n: viewership.view_segment(n, token, check_exists=False),
|
|
|
|
corrupt_hook=lambda: viewership.video_was_corrupted.add(token), # lock?
|
|
|
|
should_close_connection=lambda: not stream.is_online())
|
|
|
|
except FileNotFoundError:
|
|
|
|
return abort(404)
|
|
|
|
|
2021-07-05 20:45:32 +09:00
|
|
|
response = send_file(concatenated_segments, mimetype='video/mp4', add_etags=False)
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
response.headers['Cache-Control'] = 'no-store'
|
2021-04-15 21:45:22 +09:00
|
|
|
response.set_cookie('token', token)
|
2021-04-12 13:32:43 +09:00
|
|
|
return response
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/chat')
|
2021-04-10 02:26:29 +09:00
|
|
|
def chat_iframe():
|
2021-04-16 19:37:12 +09:00
|
|
|
token = get_token()
|
2021-04-15 20:37:04 +09:00
|
|
|
viewership.made_request(token)
|
|
|
|
|
2021-04-16 01:57:11 +09:00
|
|
|
include_user_list = bool(request.args.get('users', default=1, type=int))
|
2021-05-16 09:24:50 +09:00
|
|
|
with viewership.lock: # required because another thread can change chat.messages while we're iterating through it
|
|
|
|
messages = (message for message in chat.messages if not message['hidden'])
|
|
|
|
messages = zip(messages, range(CHAT_SCROLLBACK)) # show at most CHAT_SCROLLBACK messages
|
|
|
|
messages = [message for message, _ in messages]
|
2021-05-18 13:29:47 +09:00
|
|
|
|
|
|
|
chat.decorate(messages)
|
2021-04-15 20:37:04 +09:00
|
|
|
return render_template('chat-iframe.html',
|
|
|
|
token=token,
|
|
|
|
messages=messages,
|
2021-04-16 02:10:24 +09:00
|
|
|
include_user_list=include_user_list,
|
2021-04-15 20:37:04 +09:00
|
|
|
people=viewership.get_people_list(),
|
|
|
|
default_nickname=viewership.default_nickname,
|
|
|
|
broadcaster=token == BROADCASTER_TOKEN,
|
|
|
|
broadcaster_colour=BROADCASTER_COLOUR,
|
|
|
|
debug=request.args.get('debug'),
|
2021-05-16 09:54:21 +09:00
|
|
|
RE_WHITESPACE=RE_WHITESPACE,
|
|
|
|
len=len,
|
|
|
|
chr=chr)
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/heartbeat')
|
2021-04-10 02:26:29 +09:00
|
|
|
def heartbeat():
|
2021-04-15 20:37:04 +09:00
|
|
|
token = get_token()
|
|
|
|
viewership.made_request(token)
|
2021-04-13 22:13:40 +09:00
|
|
|
online = stream.is_online()
|
|
|
|
start_abs, start_rel = stream.get_start(absolute=True, relative=True)
|
2021-04-16 14:53:25 +09:00
|
|
|
|
|
|
|
response = {'viewers': viewership.count(),
|
|
|
|
'online': online,
|
|
|
|
'current_segment': stream.current_segment(),
|
|
|
|
'title': stream.get_title(),
|
|
|
|
'start_abs': start_abs if online else None,
|
|
|
|
'start_rel': start_rel if online else None}
|
2021-05-18 13:29:47 +09:00
|
|
|
# commented out because we should be able to tell if we're not receiving the stream already
|
|
|
|
# if token in viewership.video_was_corrupted:
|
|
|
|
# response['corrupted'] = True
|
2021-04-16 14:53:25 +09:00
|
|
|
|
|
|
|
return response
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-05-14 15:59:50 +09:00
|
|
|
@current_app.route('/comment-box', methods=['GET', 'POST'])
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
def comment_iframe(token=None):
|
2021-04-15 20:37:04 +09:00
|
|
|
token = token or get_token() or new_token()
|
|
|
|
viewership.made_request(token)
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-10 14:42:44 +09:00
|
|
|
try:
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
preset = viewership.preset_comment_iframe.pop(token)
|
2021-04-10 14:42:44 +09:00
|
|
|
except KeyError:
|
2021-04-13 22:13:40 +09:00
|
|
|
preset = {}
|
2021-05-14 15:59:50 +09:00
|
|
|
|
|
|
|
# a new captcha was requested; fill in the message that the user has typed so far
|
|
|
|
if preset == {} and request.method == 'POST':
|
|
|
|
message = request.form.get('message', '')
|
|
|
|
if len(message) < MESSAGE_MAX_LENGTH:
|
|
|
|
preset['message'] = message
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
captcha = chat.get_captcha(token)
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
response = render_template('comment-iframe.html',
|
|
|
|
token=token,
|
|
|
|
captcha=captcha,
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
nonce=chat.new_nonce(),
|
2021-04-13 22:13:40 +09:00
|
|
|
note=NOTES[preset.get('note', N_NONE)],
|
|
|
|
message=preset.get('message', ''),
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
default=viewership.default_nickname(token),
|
|
|
|
nickname=viewers[token]['nickname'],
|
2021-04-13 22:13:40 +09:00
|
|
|
viewer=viewers[token],
|
|
|
|
show_settings=preset.get('show_settings', False))
|
|
|
|
response = Response(response)
|
2021-04-10 02:26:29 +09:00
|
|
|
response.set_cookie('token', token)
|
|
|
|
return response
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/comment', methods=['POST'])
|
2021-04-10 02:26:29 +09:00
|
|
|
def comment():
|
2021-04-15 20:37:04 +09:00
|
|
|
token = get_token(form=True) or new_token()
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
nonce = request.form.get('nonce')
|
2021-04-10 02:26:29 +09:00
|
|
|
message = request.form.get('message', '').replace('\r', '').replace('\n', ' ').strip()
|
|
|
|
c_response = request.form.get('captcha')
|
2021-05-19 12:00:36 +09:00
|
|
|
c_ciphertext = request.form.get('captcha-ciphertext')
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-15 20:37:04 +09:00
|
|
|
viewership.made_request(token)
|
|
|
|
|
2021-05-19 12:00:36 +09:00
|
|
|
failure_reason = chat.comment(message, token, c_response, c_ciphertext, nonce)
|
2021-04-10 14:42:44 +09:00
|
|
|
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
viewership.preset_comment_iframe[token] = {'note': failure_reason, 'message': message if failure_reason else ''}
|
|
|
|
return comment_iframe(token=token)
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-11 03:08:13 +09:00
|
|
|
# TODO: make it so your message that you haven't sent yet stays there when you change your appearance
|
2021-04-11 17:58:14 +09:00
|
|
|
# ^ This is possible if you use only one form and change buttons to <input type="submit" formaction="/url">
|
2021-05-15 01:46:24 +09:00
|
|
|
# BUT it's not easy to make sure the formaction is correct when you press enter in any given <input>.
|
|
|
|
# There could be some other way, idk.
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/settings', methods=['POST'])
|
2021-04-10 02:26:29 +09:00
|
|
|
def settings():
|
2021-04-15 20:37:04 +09:00
|
|
|
token = get_token(form=True) or new_token()
|
2021-04-13 22:13:40 +09:00
|
|
|
nickname = request.form.get('nickname', '')
|
|
|
|
password = request.form.get('password', '')
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-15 20:37:04 +09:00
|
|
|
viewership.made_request(token)
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
note, ok = chat.set_nickname(nickname, token)
|
|
|
|
if ok:
|
|
|
|
if request.form.get('remove-tripcode'):
|
|
|
|
note, _ = chat.remove_tripcode(token)
|
|
|
|
elif request.form.get('set-tripcode'):
|
|
|
|
note, _ = chat.set_tripcode(password, token)
|
2021-04-10 14:42:44 +09:00
|
|
|
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
viewership.preset_comment_iframe[token] = {'note': note, 'show_settings': True}
|
2021-04-10 14:42:44 +09:00
|
|
|
return redirect(url_for('comment_iframe', token=token))
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-16 00:18:46 +09:00
|
|
|
# TODO: undo hides; optionally show that a comment was hidden; optionally show bans in chat
|
2021-04-16 00:24:19 +09:00
|
|
|
@current_app.route('/mod/chat', methods=['POST'])
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.auth.login_required
|
2021-04-16 00:24:19 +09:00
|
|
|
def mod_chat():
|
2021-04-10 02:26:29 +09:00
|
|
|
message_ids = request.form.getlist('message_id[]')
|
2021-04-16 00:26:30 +09:00
|
|
|
chat.mod_chat(message_ids, request.form.get('hide'), request.form.get('ban'), request.form.get('ban_purge'))
|
2021-07-05 20:45:32 +09:00
|
|
|
return f'<meta http-equiv="refresh" content="0;url={url_for("chat_iframe")}"><div style="font-weight:bold;color:white;transform:scaleY(-1);">it is done</div>'
|
2021-04-10 02:26:29 +09:00
|
|
|
|
2021-04-16 00:18:46 +09:00
|
|
|
@current_app.route('/mod/users', methods=['POST'])
|
|
|
|
@current_app.auth.login_required
|
|
|
|
def mod_users():
|
|
|
|
tokens = request.form.getlist('token[]')
|
|
|
|
banned = bool(request.form.get('banned', type=int))
|
|
|
|
chat.mod_users(tokens, banned=banned)
|
|
|
|
|
|
|
|
noscript = bool(request.form.get('noscript', type=int))
|
|
|
|
return f'<meta http-equiv="refresh" content="0;url={url_for("users") if noscript else url_for("chat_iframe")}"><div style="font-weight:bold;color:white;">it is done</div>'
|
|
|
|
|
2021-04-17 02:53:06 +09:00
|
|
|
# TODO: "you're not receiving the stream" message if that token isn't receiving the stream; make sure they don't see it when they first load the page
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/stream-info')
|
2021-04-10 02:26:29 +09:00
|
|
|
def stream_info():
|
2021-04-16 19:37:12 +09:00
|
|
|
token = get_token() or new_token()
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
embed_images = bool(request.args.get('embed', type=int))
|
2021-04-15 20:37:04 +09:00
|
|
|
|
|
|
|
viewership.made_request(token)
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
start_abs, start_rel = stream.get_start(absolute=True, relative=True)
|
2021-04-14 01:53:16 +09:00
|
|
|
online = stream.is_online()
|
2021-04-10 02:26:29 +09:00
|
|
|
return render_template('stream-info-iframe.html',
|
2021-04-13 22:13:40 +09:00
|
|
|
title=stream.get_title(),
|
|
|
|
viewer_count=viewership.count(),
|
|
|
|
stream_start_abs_json=json.dumps(start_abs if online else None),
|
|
|
|
stream_start_rel_json=json.dumps(start_rel if online else None),
|
2021-04-12 21:07:58 +09:00
|
|
|
stream_uptime=start_rel if online else None,
|
2021-04-14 01:53:16 +09:00
|
|
|
online=online,
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
video_was_corrupted=token != None and token in viewership.video_was_corrupted,
|
|
|
|
embed_images=embed_images,
|
|
|
|
token=token,
|
|
|
|
broadcaster_colour=BROADCASTER_COLOUR)
|
|
|
|
|
2021-04-15 20:37:04 +09:00
|
|
|
@current_app.route('/users')
|
|
|
|
def users():
|
|
|
|
token = get_token()
|
|
|
|
viewership.made_request(token)
|
2021-04-16 00:18:46 +09:00
|
|
|
return render_template('users-iframe.html',
|
|
|
|
token=token,
|
|
|
|
people=viewership.get_people_list(),
|
|
|
|
default_nickname=viewership.default_nickname,
|
|
|
|
broadcaster=token == BROADCASTER_TOKEN,
|
|
|
|
debug=request.args.get('debug'),
|
|
|
|
broadcaster_colour=BROADCASTER_COLOUR,
|
|
|
|
len=len)
|
2021-04-15 20:37:04 +09:00
|
|
|
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
@current_app.route('/static/radial.apng')
|
|
|
|
def radial():
|
|
|
|
response = send_from_directory(DIR_STATIC, 'radial.apng', mimetype='image/png', add_etags=False)
|
|
|
|
response.headers['Cache-Control'] = 'no-store' # caching this in any way messes with the animation
|
|
|
|
response.expires = response.date
|
|
|
|
return response
|
|
|
|
|
2021-04-15 20:37:04 +09:00
|
|
|
@current_app.route('/static/<fn>')
|
|
|
|
def _static(fn):
|
|
|
|
response = send_from_directory(DIR_STATIC, fn, add_etags=False)
|
|
|
|
response.headers['Cache-Control'] = 'no-cache'
|
|
|
|
return response
|
|
|
|
|
|
|
|
@current_app.route('/static/external/<fn>')
|
|
|
|
def third_party(fn):
|
|
|
|
response = send_from_directory(DIR_STATIC_EXTERNAL, fn, add_etags=False)
|
Update website/static/external/grids-responsive-min.css, website/static/external/pure-min.css, website/static/external/video-js.css, website/static/external/video.js, website/static/external/videojs-contrib-hls.js, website/static/platform.css, website/static/platform.js, website/templates/chat-iframe.html, website/templates/comment-iframe.html, website/templates/index.html, website/templates/stream-info-iframe.html, website/utils/captcha.py, website/utils/colour.py, website/utils/stream.py, website/utils/tripcode.py, website/chat.py, website/concatenate.py, website/routes.py, website/viewership.py files
2021-04-14 23:12:40 +09:00
|
|
|
response.headers['Cache-Control'] = 'public, max-age=604800, immutable'
|
|
|
|
response.expires = response.date + datetime.timedelta(days=7)
|
|
|
|
return response
|
|
|
|
|
|
|
|
@current_app.after_request
|
|
|
|
def add_header(response):
|
|
|
|
try:
|
|
|
|
response.headers.pop('Last-Modified')
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
return response
|
2021-04-11 13:17:11 +09:00
|
|
|
|
2021-05-19 15:16:13 +09:00
|
|
|
@current_app.route('/debug')
|
|
|
|
@current_app.auth.login_required
|
|
|
|
def debug():
|
|
|
|
import copy
|
|
|
|
|
2021-05-22 18:57:15 +09:00
|
|
|
# necessary because we store colours as bytes and json can't bytes;
|
|
|
|
class JSONEncoder(json.encoder.JSONEncoder):
|
|
|
|
def default(self, obj):
|
|
|
|
if isinstance(obj, bytes):
|
|
|
|
return f'#{obj.hex()}'
|
|
|
|
return json.encoder.JSONEncoder.default(obj)
|
|
|
|
|
|
|
|
|
2021-05-19 15:16:13 +09:00
|
|
|
with viewership.lock:
|
2021-05-22 18:57:15 +09:00
|
|
|
# necessary because infinities are allowed by json.dumps but browsers don't like it
|
2021-05-19 15:16:13 +09:00
|
|
|
json_safe_viewers = copy.deepcopy(viewership.viewers)
|
|
|
|
for token in json_safe_viewers:
|
|
|
|
for key in json_safe_viewers[token]:
|
|
|
|
if json_safe_viewers[token][key] == float('-inf'):
|
|
|
|
json_safe_viewers[token][key] = None
|
|
|
|
|
|
|
|
result = {
|
|
|
|
'viewership': {
|
|
|
|
'segment_views': viewership.segment_views,
|
|
|
|
'video_was_corrupted': list(viewership.video_was_corrupted),
|
|
|
|
'viewers': json_safe_viewers,
|
|
|
|
'preset_comment_iframe': viewership.preset_comment_iframe
|
|
|
|
},
|
|
|
|
'chat': {
|
|
|
|
'captchas': chat.captchas,
|
|
|
|
'messages': list(chat.messages),
|
|
|
|
'nonces': chat.nonces
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 02:27:02 +09:00
|
|
|
response = make_response(json.dumps(result, cls=JSONEncoder).replace('": -Infinity', '": null')) # this is such a horrible hack I apologise that you saw it
|
2021-05-22 18:57:15 +09:00
|
|
|
response.mimetype = 'application/json'
|
|
|
|
|
|
|
|
# so that you are logged in if the very first thing you do is go to /debug, then you go to /
|
|
|
|
if get_token() != BROADCASTER_TOKEN:
|
|
|
|
response.set_cookie('token', BROADCASTER_TOKEN)
|
|
|
|
|
|
|
|
return response
|
2021-05-19 15:16:13 +09:00
|
|
|
|
2021-07-05 20:45:32 +09:00
|
|
|
@current_app.route('/reload')
|
|
|
|
@current_app.auth.login_required
|
|
|
|
def reload():
|
|
|
|
'''
|
|
|
|
Re-read config.toml
|
|
|
|
'''
|
|
|
|
with open(CONFIG_FILE) as fp:
|
|
|
|
config = toml.load(fp)
|
|
|
|
CONFIG['captcha']['fonts'] = config['captcha']['fonts']
|
|
|
|
CONFIG['stream']['hls_time'] = config['stream']['hls_time']
|
|
|
|
|
|
|
|
# this exists for the same reason as in /debug
|
|
|
|
response = make_response(CONFIG)
|
|
|
|
if get_token() != BROADCASTER_TOKEN:
|
|
|
|
response.set_cookie('token', BROADCASTER_TOKEN)
|
|
|
|
|
|
|
|
return response
|
|
|
|
|
2021-04-13 22:13:40 +09:00
|
|
|
@current_app.route('/teapot')
|
2021-04-11 13:17:11 +09:00
|
|
|
def teapot():
|
2021-04-16 19:11:47 +09:00
|
|
|
return {'short': True, 'stout': True}, 418
|