Eyes: delete old eyes
Also implements stack/queue behaviour where if the eyes limit would be exceeded, either the new eyes cause the oldest eyes to be deleted OR the new eyes aren't created at all. The default is the first option.
このコミットが含まれているのは:
コミット
51265fb277
|
@ -82,6 +82,7 @@ def toml_to_flask_section_memory(config):
|
|||
def toml_to_flask_section_tasks(config):
|
||||
cfg = config['tasks']
|
||||
return {
|
||||
'TASK_ROTATE_EYES': cfg['rotate_eyes'],
|
||||
'TASK_ROTATE_USERS': cfg['rotate_users'],
|
||||
'TASK_ROTATE_CAPTCHAS': cfg['rotate_captchas'],
|
||||
'TASK_ROTATE_WEBSOCKETS': cfg['rotate_websockets'],
|
||||
|
@ -112,11 +113,16 @@ def toml_to_flask_section_chat(config):
|
|||
|
||||
def toml_to_flask_section_flood(config):
|
||||
cfg = config['flood']
|
||||
assert cfg['video']['max_eyes'] >= 0
|
||||
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'],
|
||||
'FLOOD_VIDEO_MAX_EYES': cfg['video']['max_eyes'],
|
||||
#'FLOOD_VIDEO_COOLDOWN': cfg['video']['cooldown'],
|
||||
'FLOOD_VIDEO_EYES_EXPIRE_AFTER': cfg['video']['expire_after'],
|
||||
'FLOOD_VIDEO_OVERWRITE': cfg['video']['overwrite'],
|
||||
}
|
||||
|
||||
def toml_to_flask_section_captcha(config):
|
||||
|
|
|
@ -6,7 +6,7 @@ from quart.asgi import ASGIHTTPConnection as ASGIHTTPConnection_
|
|||
from quart.utils import encode_headers
|
||||
|
||||
|
||||
RESPONSE_ITERATOR_TIMEOUT = 10
|
||||
RESPONSE_ITERATOR_TIMEOUT = 10.0
|
||||
|
||||
|
||||
class ASGIHTTPConnection(ASGIHTTPConnection_):
|
||||
|
|
|
@ -6,7 +6,7 @@ from quart import current_app, request, render_template, abort, make_response, r
|
|||
from anonstream.captcha import get_captcha_image
|
||||
from anonstream.segments import segments, StopSendingSegments
|
||||
from anonstream.stream import is_online, get_stream_uptime
|
||||
from anonstream.user import watched, create_eyes, renew_eyes, ExpiredEyes
|
||||
from anonstream.user import watched, create_eyes, renew_eyes, EyesException
|
||||
from anonstream.routes.wrappers import with_user_from, auth_required
|
||||
from anonstream.utils.security import generate_csp
|
||||
|
||||
|
@ -25,12 +25,16 @@ async def stream(user):
|
|||
if not is_online():
|
||||
return abort(404)
|
||||
|
||||
eyes_id = create_eyes(user, dict(request.headers))
|
||||
try:
|
||||
eyes_id = create_eyes(user, dict(request.headers))
|
||||
except EyesException:
|
||||
return abort(429)
|
||||
|
||||
def segment_read_hook(uri):
|
||||
try:
|
||||
renew_eyes(user, eyes_id)
|
||||
except ExpiredEyes as e:
|
||||
raise StopSendingSegments(f'eyes {eyes_id} expired: {e}') from e
|
||||
renew_eyes(user, eyes_id, just_read_new_segment=True)
|
||||
except EyesException as e:
|
||||
raise StopSendingSegments(f'eyes {eyes_id} not allowed: {e!r}') from e
|
||||
print(f'{uri}: {eyes_id}~{user["token"]}')
|
||||
watched(user)
|
||||
|
||||
|
|
|
@ -43,6 +43,21 @@ def with_period(period):
|
|||
|
||||
return periodically
|
||||
|
||||
@with_period(CONFIG['TASK_ROTATE_EYES'])
|
||||
@with_timestamp
|
||||
async def t_delete_eyes(timestamp, iteration):
|
||||
if iteration == 0:
|
||||
return
|
||||
else:
|
||||
for user in USERS:
|
||||
to_delete = []
|
||||
for eyes_id, eyes in user['eyes']['current'].items():
|
||||
renewed_ago = timestamp - eyes['renewed']
|
||||
if renewed_ago >= CONFIG['FLOOD_VIDEO_EYES_EXPIRE_AFTER']:
|
||||
to_delete.append(eyes_id)
|
||||
for eyes_id in to_delete:
|
||||
user['eyes']['current'].pop(eyes_id)
|
||||
|
||||
@with_period(CONFIG['TASK_ROTATE_USERS'])
|
||||
@with_timestamp
|
||||
async def t_sunset_users(timestamp, iteration):
|
||||
|
@ -166,6 +181,7 @@ async def t_broadcast_stream_info_update(iteration):
|
|||
if payload:
|
||||
broadcast(USERS, payload={'type': 'info', **payload})
|
||||
|
||||
current_app.add_background_task(t_delete_eyes)
|
||||
current_app.add_background_task(t_sunset_users)
|
||||
current_app.add_background_task(t_expire_captchas)
|
||||
current_app.add_background_task(t_close_websockets)
|
||||
|
|
|
@ -25,7 +25,16 @@ class BadAppearance(ValueError):
|
|||
class BadCaptcha(ValueError):
|
||||
pass
|
||||
|
||||
class ExpiredEyes(Exception):
|
||||
class EyesException(Exception):
|
||||
pass
|
||||
|
||||
class TooManyEyes(EyesException):
|
||||
pass
|
||||
|
||||
class DeletedEyes(EyesException):
|
||||
pass
|
||||
|
||||
class ExpiredEyes(EyesException):
|
||||
pass
|
||||
|
||||
def add_state(user, **state):
|
||||
|
@ -225,6 +234,16 @@ def get_users_by_presence(timestamp):
|
|||
|
||||
@with_timestamp
|
||||
def create_eyes(timestamp, user, headers):
|
||||
if len(user['eyes']['current']) >= CONFIG['FLOOD_VIDEO_MAX_EYES']:
|
||||
# treat eyes as a stack, do not create new eyes if it would
|
||||
# cause the limit to be exceeded
|
||||
if not CONFIG['FLOOD_VIDEO_OVERWRITE']:
|
||||
raise TooManyEyes
|
||||
# treat eyes as a queue, expire old eyes upon creating new eyes
|
||||
# if the limit would have been exceeded
|
||||
elif user['eyes']['current']:
|
||||
oldest_eyes_id = min(user['eyes']['current'])
|
||||
user['eyes']['current'].pop(oldest_eyes_id)
|
||||
eyes_id = user['eyes']['total']
|
||||
user['eyes']['total'] += 1
|
||||
user['eyes']['current'][eyes_id] = {
|
||||
|
@ -238,12 +257,15 @@ def create_eyes(timestamp, user, headers):
|
|||
return eyes_id
|
||||
|
||||
@with_timestamp
|
||||
def renew_eyes(timestamp, user, eyes_id):
|
||||
def renew_eyes(timestamp, user, eyes_id, just_read_new_segment=False):
|
||||
try:
|
||||
eyes = user['eyes']['current'][eyes_id]
|
||||
except KeyError:
|
||||
raise ExpiredEyes(None)
|
||||
if timestamp - eyes['renewed'] >= 20.0: # TODO remove magic number
|
||||
raise DeletedEyes(eyes_id)
|
||||
renewed_ago = timestamp - eyes['renewed']
|
||||
if renewed_ago >= CONFIG['FLOOD_VIDEO_EYES_EXPIRE_AFTER']:
|
||||
user['eyes']['current'].pop(eyes_id)
|
||||
raise ExpiredEyes(eyes)
|
||||
eyes['renewed'] = timestamp
|
||||
if just_read_new_segment:
|
||||
eyes['n_segments'] += 1
|
||||
|
|
|
@ -34,6 +34,7 @@ chat_messages = 8192
|
|||
chat_scrollback = 256
|
||||
|
||||
[tasks]
|
||||
rotate_eyes = 3.0
|
||||
rotate_users = 60.0
|
||||
rotate_captchas = 60.0
|
||||
rotate_websockets = 2.0
|
||||
|
@ -60,6 +61,12 @@ threshold = 4
|
|||
duration = 20.0
|
||||
threshold = 20
|
||||
|
||||
[flood.video]
|
||||
max_eyes = 3
|
||||
#cooldown = 12.0
|
||||
expire_after = 5.0
|
||||
overwrite = true
|
||||
|
||||
[thresholds]
|
||||
user_notwatching = 8.0
|
||||
user_tentative = 20.0
|
||||
|
|
読み込み中…
新しいイシューから参照