Read config.toml more organizedly
このコミットが含まれているのは:
コミット
7f2e75bc98
|
@ -1,94 +1,37 @@
|
|||
# SPDX-FileCopyrightText: 2022 n9k [https://git.076.ne.jp/ninya9k]
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import os
|
||||
import secrets
|
||||
import toml
|
||||
from collections import OrderedDict
|
||||
|
||||
import toml
|
||||
from quart_compress import Compress
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from anonstream.quart import Quart
|
||||
from anonstream.config import update_flask_from_toml
|
||||
from anonstream.utils.captcha import create_captcha_factory, create_captcha_signer
|
||||
from anonstream.utils.colour import color_to_colour
|
||||
from anonstream.utils.user import generate_token
|
||||
from anonstream.quart import Quart
|
||||
|
||||
compress = Compress()
|
||||
|
||||
def create_app(config_file):
|
||||
with open(config_file) as fp:
|
||||
config = toml.load(fp)
|
||||
app = Quart('anonstream')
|
||||
app.jinja_options['trim_blocks'] = True
|
||||
app.jinja_options['lstrip_blocks'] = True
|
||||
|
||||
auth_password = secrets.token_urlsafe(6)
|
||||
auth_pwhash = generate_password_hash(auth_password)
|
||||
print('Broadcaster username:', config['auth']['username'])
|
||||
with open(config_file) as fp:
|
||||
toml_config = toml.load(fp)
|
||||
auth_password = update_flask_from_toml(toml_config, app.config)
|
||||
|
||||
print('Broadcaster username:', app.config['AUTH_USERNAME'])
|
||||
print('Broadcaster password:', auth_password)
|
||||
|
||||
app = Quart('anonstream')
|
||||
app.jinja_options.update({
|
||||
'trim_blocks': True,
|
||||
'lstrip_blocks': True,
|
||||
})
|
||||
# Compress some responses
|
||||
compress.init_app(app)
|
||||
app.config.update({
|
||||
'SECRET_KEY_STRING': config['secret_key'],
|
||||
'SECRET_KEY': config['secret_key'].encode(),
|
||||
'AUTH_USERNAME': config['auth']['username'],
|
||||
'AUTH_PWHASH': auth_pwhash,
|
||||
'AUTH_TOKEN': generate_token(),
|
||||
'SEGMENT_DIRECTORY': os.path.realpath(config['segments']['directory']),
|
||||
'SEGMENT_PLAYLIST': os.path.join(os.path.realpath(config['segments']['directory']), config['segments']['playlist']),
|
||||
'SEGMENT_PLAYLIST_CACHE_LIFETIME': config['segments']['playlist_cache_lifetime'],
|
||||
'SEGMENT_PLAYLIST_STALE_THRESHOLD': config['segments']['playlist_stale_threshold'],
|
||||
'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'],
|
||||
'MAX_CAPTCHAS': config['memory']['captchas'],
|
||||
'MAX_CHAT_MESSAGES': config['memory']['chat_messages'],
|
||||
'MAX_CHAT_SCROLLBACK': config['memory']['chat_scrollback'],
|
||||
'TASK_PERIOD_ROTATE_USERS': config['tasks']['rotate_users'],
|
||||
'TASK_PERIOD_ROTATE_CAPTCHAS': config['tasks']['rotate_captchas'],
|
||||
'TASK_PERIOD_ROTATE_WEBSOCKETS': config['tasks']['rotate_websockets'],
|
||||
'TASK_PERIOD_BROADCAST_PING': config['tasks']['broadcast_ping'],
|
||||
'TASK_PERIOD_BROADCAST_USERS_UPDATE': config['tasks']['broadcast_users_update'],
|
||||
'TASK_PERIOD_BROADCAST_STREAM_INFO_UPDATE': config['tasks']['broadcast_stream_info_update'],
|
||||
'THRESHOLD_USER_NOTWATCHING': config['thresholds']['user_notwatching'],
|
||||
'THRESHOLD_USER_TENTATIVE': config['thresholds']['user_tentative'],
|
||||
'THRESHOLD_USER_ABSENT': config['thresholds']['user_absent'],
|
||||
'THRESHOLD_NOJS_CHAT_TIMEOUT': config['thresholds']['nojs_chat_timeout'],
|
||||
'CHAT_COMMENT_MAX_LENGTH': config['chat']['max_name_length'],
|
||||
'CHAT_NAME_MAX_LENGTH': config['chat']['max_name_length'],
|
||||
'CHAT_NAME_MIN_CONTRAST': config['chat']['min_name_contrast'],
|
||||
'CHAT_BACKGROUND_COLOUR': color_to_colour(config['chat']['background_color']),
|
||||
'CHAT_LEGACY_TRIPCODE_ALGORITHM': config['chat']['legacy_tripcode_algorithm'],
|
||||
'FLOOD_MESSAGE_DURATION': config['flood']['messages']['duration'],
|
||||
'FLOOD_MESSAGE_THRESHOLD': config['flood']['messages']['threshold'],
|
||||
'FLOOD_LINE_DURATION': config['flood']['lines']['duration'],
|
||||
'FLOOD_LINE_THRESHOLD': config['flood']['lines']['threshold'],
|
||||
'CAPTCHA_LIFETIME': config['captcha']['lifetime'],
|
||||
'CAPTCHA_FONTS': config['captcha']['fonts'],
|
||||
'CAPTCHA_ALPHABET': config['captcha']['alphabet'],
|
||||
'CAPTCHA_LENGTH': config['captcha']['length'],
|
||||
'CAPTCHA_BACKGROUND_COLOUR': color_to_colour(config['captcha']['background_color']),
|
||||
'CAPTCHA_FOREGROUND_COLOUR': color_to_colour(config['captcha']['foreground_color']),
|
||||
"COMPRESS_MIN_SIZE": 2048,
|
||||
"COMPRESS_LEVEL": 9,
|
||||
})
|
||||
|
||||
assert app.config['MAX_STATES'] >= 0
|
||||
assert app.config['MAX_CHAT_SCROLLBACK'] >= 0
|
||||
assert (
|
||||
app.config['MAX_CHAT_MESSAGES'] >= app.config['MAX_CHAT_SCROLLBACK']
|
||||
)
|
||||
assert (
|
||||
app.config['THRESHOLD_USER_ABSENT']
|
||||
>= app.config['THRESHOLD_USER_TENTATIVE']
|
||||
>= app.config['THRESHOLD_USER_NOTWATCHING']
|
||||
)
|
||||
|
||||
# Global state: messages, users, captchas
|
||||
app.messages_by_id = OrderedDict()
|
||||
app.messages = app.messages_by_id.values()
|
||||
|
||||
|
@ -110,7 +53,7 @@ def create_app(config_file):
|
|||
|
||||
@app.after_serving
|
||||
async def shutdown():
|
||||
# make all background tasks finish
|
||||
# Force all background tasks to finish
|
||||
for task in app.background_sleep:
|
||||
task.cancel()
|
||||
|
||||
|
@ -119,11 +62,4 @@ def create_app(config_file):
|
|||
import anonstream.routes
|
||||
import anonstream.tasks
|
||||
|
||||
# Compress some responses
|
||||
compress.init_app(app)
|
||||
app.config.update({
|
||||
"COMPRESS_MIN_SIZE": 2048,
|
||||
"COMPRESS_LEVEL": 9,
|
||||
})
|
||||
|
||||
return app
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
import os
|
||||
import secrets
|
||||
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from anonstream.utils.colour import color_to_colour
|
||||
from anonstream.utils.user import generate_token
|
||||
|
||||
def update_flask_from_toml(toml_config, flask_config):
|
||||
auth_password = secrets.token_urlsafe(6)
|
||||
auth_pwhash = generate_password_hash(auth_password)
|
||||
|
||||
flask_config.update({
|
||||
'SECRET_KEY_STRING': toml_config['secret_key'],
|
||||
'SECRET_KEY': toml_config['secret_key'].encode(),
|
||||
'AUTH_USERNAME': toml_config['auth']['username'],
|
||||
'AUTH_PWHASH': auth_pwhash,
|
||||
'AUTH_TOKEN': generate_token(),
|
||||
})
|
||||
for flask_section in toml_to_flask_sections(toml_config):
|
||||
flask_config.update(flask_section)
|
||||
|
||||
return auth_password
|
||||
|
||||
def toml_to_flask_sections(config):
|
||||
TOML_TO_FLASK_SECTIONS = (
|
||||
toml_to_flask_section_segments,
|
||||
toml_to_flask_section_title,
|
||||
toml_to_flask_section_names,
|
||||
toml_to_flask_section_memory,
|
||||
toml_to_flask_section_tasks,
|
||||
toml_to_flask_section_thresholds,
|
||||
toml_to_flask_section_chat,
|
||||
toml_to_flask_section_flood,
|
||||
toml_to_flask_section_captcha,
|
||||
)
|
||||
for toml_to_flask_section in TOML_TO_FLASK_SECTIONS:
|
||||
yield toml_to_flask_section(config)
|
||||
|
||||
def toml_to_flask_section_segments(config):
|
||||
cfg = config['segments']
|
||||
return {
|
||||
'SEGMENT_DIRECTORY': os.path.realpath(cfg['directory']),
|
||||
'SEGMENT_PLAYLIST': os.path.join(
|
||||
os.path.realpath(cfg['directory']),
|
||||
cfg['playlist'],
|
||||
),
|
||||
'SEGMENT_PLAYLIST_CACHE_LIFETIME': cfg['playlist_cache_lifetime'],
|
||||
'SEGMENT_PLAYLIST_STALE_THRESHOLD': cfg['playlist_stale_threshold'],
|
||||
'SEGMENT_SEARCH_COOLDOWN': cfg['search_cooldown'],
|
||||
'SEGMENT_SEARCH_TIMEOUT': cfg['search_timeout'],
|
||||
'SEGMENT_STREAM_INITIAL_BUFFER': cfg['stream_initial_buffer'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_title(config):
|
||||
cfg = config['title']
|
||||
return {
|
||||
'STREAM_TITLE': cfg['file'],
|
||||
'STREAM_TITLE_CACHE_LIFETIME': cfg['file_cache_lifetime'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_names(config):
|
||||
cfg = config['names']
|
||||
return {
|
||||
'DEFAULT_HOST_NAME': cfg['broadcaster'],
|
||||
'DEFAULT_ANON_NAME': cfg['anonymous'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_memory(config):
|
||||
cfg = config['memory']
|
||||
assert cfg['states'] >= 0
|
||||
assert cfg['chat_scrollback'] >= 0
|
||||
assert cfg['chat_messages'] >= cfg['chat_scrollback']
|
||||
return {
|
||||
'MAX_STATES': cfg['states'],
|
||||
'MAX_CAPTCHAS': cfg['captchas'],
|
||||
'MAX_CHAT_MESSAGES': cfg['chat_messages'],
|
||||
'MAX_CHAT_SCROLLBACK': cfg['chat_scrollback'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_tasks(config):
|
||||
cfg = config['tasks']
|
||||
return {
|
||||
'TASK_ROTATE_USERS': cfg['rotate_users'],
|
||||
'TASK_ROTATE_CAPTCHAS': cfg['rotate_captchas'],
|
||||
'TASK_ROTATE_WEBSOCKETS': cfg['rotate_websockets'],
|
||||
'TASK_BROADCAST_PING': cfg['broadcast_ping'],
|
||||
'TASK_BROADCAST_USERS_UPDATE': cfg['broadcast_users_update'],
|
||||
'TASK_BROADCAST_STREAM_INFO_UPDATE': cfg['broadcast_stream_info_update'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_thresholds(config):
|
||||
cfg = config['thresholds']
|
||||
assert cfg['user_notwatching'] <= cfg['user_tentative'] <= cfg['user_absent']
|
||||
return {
|
||||
'THRESHOLD_USER_NOTWATCHING': cfg['user_notwatching'],
|
||||
'THRESHOLD_USER_TENTATIVE': cfg['user_tentative'],
|
||||
'THRESHOLD_USER_ABSENT': cfg['user_absent'],
|
||||
'THRESHOLD_NOJS_CHAT_TIMEOUT': cfg['nojs_chat_timeout'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_chat(config):
|
||||
cfg = config['chat']
|
||||
return {
|
||||
'CHAT_COMMENT_MAX_LENGTH': cfg['max_name_length'],
|
||||
'CHAT_NAME_MAX_LENGTH': cfg['max_name_length'],
|
||||
'CHAT_NAME_MIN_CONTRAST': cfg['min_name_contrast'],
|
||||
'CHAT_BACKGROUND_COLOUR': color_to_colour(cfg['background_color']),
|
||||
'CHAT_LEGACY_TRIPCODE_ALGORITHM': cfg['legacy_tripcode_algorithm'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_flood(config):
|
||||
cfg = config['flood']
|
||||
return {
|
||||
'FLOOD_MESSAGE_DURATION': cfg['messages']['duration'],
|
||||
'FLOOD_MESSAGE_THRESHOLD': cfg['messages']['threshold'],
|
||||
'FLOOD_LINE_DURATION': cfg['lines']['duration'],
|
||||
'FLOOD_LINE_THRESHOLD': cfg['lines']['threshold'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_captcha(config):
|
||||
cfg = config['captcha']
|
||||
return {
|
||||
'CAPTCHA_LIFETIME': cfg['lifetime'],
|
||||
'CAPTCHA_FONTS': cfg['fonts'],
|
||||
'CAPTCHA_ALPHABET': cfg['alphabet'],
|
||||
'CAPTCHA_LENGTH': cfg['length'],
|
||||
'CAPTCHA_BACKGROUND_COLOUR': color_to_colour(cfg['background_color']),
|
||||
'CAPTCHA_FOREGROUND_COLOUR': color_to_colour(cfg['foreground_color']),
|
||||
}
|
|
@ -43,7 +43,7 @@ def with_period(period):
|
|||
|
||||
return periodically
|
||||
|
||||
@with_period(CONFIG['TASK_PERIOD_ROTATE_USERS'])
|
||||
@with_period(CONFIG['TASK_ROTATE_USERS'])
|
||||
@with_timestamp
|
||||
async def t_sunset_users(timestamp, iteration):
|
||||
if iteration == 0:
|
||||
|
@ -69,7 +69,7 @@ async def t_sunset_users(timestamp, iteration):
|
|||
},
|
||||
)
|
||||
|
||||
@with_period(CONFIG['TASK_PERIOD_ROTATE_CAPTCHAS'])
|
||||
@with_period(CONFIG['TASK_ROTATE_CAPTCHAS'])
|
||||
async def t_expire_captchas(iteration):
|
||||
if iteration == 0:
|
||||
return
|
||||
|
@ -86,10 +86,10 @@ async def t_expire_captchas(iteration):
|
|||
for digest in to_delete:
|
||||
CAPTCHAS.pop(digest)
|
||||
|
||||
@with_period(CONFIG['TASK_PERIOD_ROTATE_WEBSOCKETS'])
|
||||
@with_period(CONFIG['TASK_ROTATE_WEBSOCKETS'])
|
||||
@with_timestamp
|
||||
async def t_close_websockets(timestamp, iteration):
|
||||
THRESHOLD = CONFIG['TASK_PERIOD_BROADCAST_PING'] * 1.5 + 4.0
|
||||
THRESHOLD = CONFIG['TASK_BROADCAST_PING'] * 1.5 + 4.0
|
||||
if iteration == 0:
|
||||
return
|
||||
else:
|
||||
|
@ -100,21 +100,21 @@ async def t_close_websockets(timestamp, iteration):
|
|||
if last_pong_ago > THRESHOLD:
|
||||
queue.put_nowait({'type': 'close'})
|
||||
|
||||
@with_period(CONFIG['TASK_PERIOD_BROADCAST_PING'])
|
||||
@with_period(CONFIG['TASK_BROADCAST_PING'])
|
||||
async def t_broadcast_ping(iteration):
|
||||
if iteration == 0:
|
||||
return
|
||||
else:
|
||||
broadcast(USERS, payload={'type': 'ping'})
|
||||
|
||||
@with_period(CONFIG['TASK_PERIOD_BROADCAST_USERS_UPDATE'])
|
||||
@with_period(CONFIG['TASK_BROADCAST_USERS_UPDATE'])
|
||||
async def t_broadcast_users_update(iteration):
|
||||
if iteration == 0:
|
||||
return
|
||||
else:
|
||||
broadcast_users_update()
|
||||
|
||||
@with_period(CONFIG['TASK_PERIOD_BROADCAST_STREAM_INFO_UPDATE'])
|
||||
@with_period(CONFIG['TASK_BROADCAST_STREAM_INFO_UPDATE'])
|
||||
async def t_broadcast_stream_info_update(iteration):
|
||||
if iteration == 0:
|
||||
title = await get_stream_title()
|
||||
|
@ -139,7 +139,7 @@ async def t_broadcast_stream_info_update(iteration):
|
|||
else:
|
||||
expected_uptime = (
|
||||
current_app.stream_uptime
|
||||
+ CONFIG['TASK_PERIOD_BROADCAST_STREAM_INFO_UPDATE']
|
||||
+ CONFIG['TASK_BROADCAST_STREAM_INFO_UPDATE']
|
||||
)
|
||||
current_app.stream_uptime = uptime
|
||||
if uptime is None and expected_uptime is None:
|
||||
|
|
|
@ -30,7 +30,7 @@ async def websocket_outbound(queue, user):
|
|||
},
|
||||
'scrollback': CONFIG['MAX_CHAT_SCROLLBACK'],
|
||||
'digest': get_random_captcha_digest_for(user),
|
||||
'pingpong': CONFIG['TASK_PERIOD_BROADCAST_PING'],
|
||||
'pingpong': CONFIG['TASK_BROADCAST_PING'],
|
||||
}
|
||||
await websocket.send_json(payload)
|
||||
await websocket.send_json({'type': 'ping'})
|
||||
|
|
読み込み中…
新しいイシューから参照