anonstream/website/utils/stream.py

109 行
3.1 KiB
Python

import os
import re
import time
from flask import abort
from website.constants import CONFIG, SEGMENTS_DIR, SEGMENTS_M3U8, SEGMENT_INIT, STREAM_TITLE, STALE_PLAYLIST_THRESHOLD
RE_SEGMENT_OR_INIT = re.compile(r'\b(stream(?P<number>\d+)\.m4s|init\.mp4)\b')
RE_SEGMENT = re.compile(r'stream(?P<number>\d+)\.m4s')
def _segment_number(fn):
if fn == SEGMENT_INIT: return None
return int(RE_SEGMENT.fullmatch(fn).group('number'))
def _is_segment(fn):
return bool(RE_SEGMENT.fullmatch(fn))
def get_segments():
if playlist_is_stale():
return []
m3u8 = []
try:
with open(SEGMENTS_M3U8) as fp:
for line in fp.readlines():
line = line.rstrip()
if _is_segment(line):
m3u8.append(line)
# the stream has ended, return an empty list
elif line == '#EXT-X-ENDLIST':
m3u8.clear()
break
except FileNotFoundError:
m3u8.clear()
m3u8.sort(key=_segment_number)
return m3u8
def _is_available(fn, m3u8):
return fn in m3u8
def current_segment():
segments = get_segments()
if segments:
return _segment_number(segments[-1])
return None
def playlist_is_stale():
try:
return time.time() - os.path.getmtime(SEGMENTS_M3U8) >= STALE_PLAYLIST_THRESHOLD
except FileNotFoundError:
return True
def is_online():
return bool(get_segments())
def get_title():
try:
return open(STREAM_TITLE).read().strip()
except FileNotFoundError:
return ''
def get_start(absolute=True, relative=False):
start = None
# if segments exist
if is_online():
try:
# then the stream start at the mtime of init.mp4
start = os.path.getmtime(os.path.join(SEGMENTS_DIR, SEGMENT_INIT))
except FileNotFoundError:
pass
else:
# minus the length of 1 segment
# (because init.mp4 is written to when the first segment is created)
start = int(start) - CONFIG['stream']['hls_time']
diff = None if start == None else int(time.time()) - start
if absolute and relative:
return start, diff
elif absolute:
return start
elif relative:
return diff
def token_playlist(token):
'''
Append '?token={token}' to each segment in the playlist
'''
if playlist_is_stale():
return []
m3u8 = []
with open(SEGMENTS_M3U8) as fp:
for line in fp.readlines():
line = line.rstrip()
line = RE_SEGMENT_OR_INIT.sub(lambda match: f'{match.group()}?token={token}', line)
m3u8.append(line)
# the stream has ended, pretend this file doesn't exist
if line == '#EXT-X-ENDLIST':
raise FileNotFoundError
return '\n'.join(m3u8)
def readable_uptime():
uptime = get_start(absolute=False, relative=True)
if uptime == None:
return None
hours, uptime = divmod(uptime, 3600)
minutes, seconds = divmod(uptime, 60)
if hours:
return f'{hours}:{minutes:02}:{seconds:02}'
return f'{minutes:02}:{seconds:02}'