aircox/aircox_streamer/controllers/sources.py
2024-04-28 18:59:33 +02:00

144 lines
4.0 KiB
Python
Executable File

import os
import tzlocal
from aircox.utils import to_seconds
from ..conf import settings
from .metadata import Metadata, Request
__all__ = (
"Source",
"PlaylistSource",
"QueueSource",
)
local_tz = tzlocal.get_localzone()
class Source(Metadata):
controller = None
"""Parent controller."""
id = None
"""Source id."""
remaining = 0.0
"""Remaining time."""
status = "stopped"
@property
def station(self):
return self.controller.station
def __init__(self, controller=None, id=None, *args, **kwargs):
super().__init__(controller, *args, **kwargs)
self.id = id
def sync(self):
"""Synchronize what should be synchronized."""
def fetch(self):
try:
data = self.controller.send(self.id, ".remaining")
if data:
self.remaining = float(data)
except ValueError:
self.remaining = None
data = self.controller.send(f"var.get {self.id}_meta", parse_json=True)
if data:
self.validate(data if data and isinstance(data, (dict, list)) else {}, as_dict=True)
def skip(self):
"""Skip the current source sound."""
self.controller.send(self.id, ".skip")
def restart(self):
"""Restart current sound."""
# seek 10 hours back since there is not possibility to get current pos
self.seek(-216000 * 10)
def seek(self, n):
"""Seeks into the sound."""
self.controller.send(self.id, ".seek ", str(n))
class PlaylistSource(Source):
"""Source handling playlists (program streams)"""
path = None
"""Path to playlist."""
program = None
"""Related program."""
playlist = None
"""The playlist."""
def __init__(self, controller, id=None, program=None, **kwargs):
id = program.slug.replace("-", "_") if id is None else id
self.program = program
super().__init__(controller, id=id, **kwargs)
self.path = settings.get_dir(self.station, f"{self.id}.m3u")
def get_sound_queryset(self):
"""Get playlist's sounds queryset."""
return self.program.sound_set.broadcast()
def get_playlist(self):
"""Get playlist from db."""
return self.get_sound_queryset().playlist()
def write_playlist(self, playlist=[]):
"""Write playlist to file."""
playlist = playlist or self.get_playlist()
os.makedirs(os.path.dirname(self.path), exist_ok=True)
with open(self.path, "w") as file:
file.write("\n".join(playlist or []))
def stream(self):
"""Return program's stream info if any (or None) as dict."""
# used in templates
# TODO: multiple streams
stream = self.program.stream_set.all().first()
if not stream or (not stream.begin and not stream.delay):
return
return {
"begin": stream.begin.strftime("%Hh%M") if stream.begin else None,
"end": stream.end.strftime("%Hh%M") if stream.end else None,
"delay": to_seconds(stream.delay) if stream.delay else 0,
}
def sync(self):
playlist = self.get_playlist()
self.write_playlist(playlist)
class QueueSource(Source):
queue = None
"""Source's queue (excluded on_air request)"""
@property
def requests(self):
"""Queue as requests metadata."""
requests = [Request(self.controller, rid) for rid in self.queue]
for request in requests:
request.fetch()
return requests
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def push(self, *paths):
"""Add the provided paths to source's play queue."""
for path in paths:
print(self.controller.send(f"{self.id}_queue.push {path}"))
def fetch(self):
super().fetch()
queue = self.controller.send(f"{self.id}_queue.queue").strip()
if not queue:
self.queue = []
return
self.queue = queue.split(" ")