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(" ")