Source code for easyplayer.core

from time import time
from signal import SIGINT, SIGHUP, SIGTERM
from logging import getLogger
from circuits import handler, Component, Timer
from circuits.net.sockets import UDPClient
from circuits.net.events import write
from path import Path
from .workshop import Workshop, Command
from .download import DownloadManager
from .events import (confirm_cmd,
                     download_message,
                     get_program,
                     handle,
                     check_download, check_allowed,
                     content_update, refresh_files, application_restart,
                     reload_programs,
                     set_dwld_status, start_download,
                     enable_player, disable_player,
                     set_status,
                     terminate,
                     watchdog,
                     )
from .webclient import WebClient
from .audio import AudioPlayer
from .video import VideoPlayer
from .settings import (DOWNLOAD_CHECK_INITIAL, ALLOWED_INTERVAL,
                       CHECK_NO_SONG_INTERVAL,
                       WATCHDOG_INTERVAL, WATCHDOG_HOST, WATCHDOG_PORT)
from .storage import ProgramData, NewProgramData
from .utils import force_bytes, seconds_until, time_format
from .utils.display import DisplayConfig
from .utils.info import PlayerInfo
from .utils.stream import MediaStream
from .utils.wifi import WifiConfig


[docs]class App(Component): """ Main app component Child components: * WebClient * AudioPlayer * VideoPlayer * Workshop * DownloadManager """ #_STATES = Enum('State', ['INITIAL', 'RUNNING', 'NEWPRG', 'DWLD']) LOCAL_COMMANDS = { 'cn' : content_update, 'cr' : refresh_files, 'ar' : application_restart }
[docs] def init(self, config): """ Component initializiation, launches other child components and timers""" self.logger = getLogger(__name__) self.config = config self.data_dir = Path(config['data_dir']).expanduser() #self.status = self._STATES.INITIAL self.download_disabled = None self.webclient = WebClient(config).register(self) self.audioplayer = AudioPlayer(config) #.register(self) self.videoplayer = VideoPlayer(config) #.register(self) self.workshop = Workshop(config).register(self) self.dwldman = DownloadManager(config).register(self) self.wdclient = UDPClient(0, channel='watchdogclient').register(self) # start timer to notify watchdog self.wdtimer = Timer(WATCHDOG_INTERVAL, watchdog(), persist=True).register(self) self.player_info = PlayerInfo(self.data_dir) # timer to schedule check if player is allowed to play self.allowed_timer = Timer(ALLOWED_INTERVAL, check_allowed(), persist=True).register(self)
def _check_remote_config(self): self.logger.debug("Checking wifi and display rotation settings") config = self.player_info a = WifiConfig(config).check() b = DisplayConfig(config).check() if a or b: self.logger.info("Changed wifi: %s, Display rotation: %s", a, b) # reboot msgs = ['wifi', 'display rotation'] msg = ' and '.join([x for i, x in enumerate(msgs) if [a,b][i]]) self.logger.warning('Rebooting due to changed %s settings', msg) self.fire(handle('rb')) return True else: self.logger.debug("Display rotation and wifi not changed.")
[docs] def started(self, component, *args): """ Handler for application started event. Starts timer for check_allowed event. """ if component.name == 'App': self.logger.info('started: %s', self) changed = self._check_remote_config() if not changed: # check if all files downloaded after a while Timer(DOWNLOAD_CHECK_INITIAL, check_download()).register(self) # initialize players self.allowed_timer.expiry = time() + 0.1
[docs] def command(self, cmd, *args, **kwargs): """ handler for command event. Some commands are handled by workshop module and some locally in this class. """ self.logger.debug('command forward: %s', cmd) if cmd in Command.COMMAND_HANDLERS or cmd in self.LOCAL_COMMANDS: self.fire(confirm_cmd(cmd)) if cmd in self.LOCAL_COMMANDS: ev = self.LOCAL_COMMANDS[cmd] self.fire(ev()) else: # allow command confirmation to get to server Timer(5, handle(cmd), self.workshop.channel).register(self)
[docs] def content_update(self): """ local command: start download unconditionally """ self.fire(start_download(force=True))
[docs] def refresh_files(self): """ local command: download and refresh existing files """ self.fire(start_download(force=True, refresh=True))
[docs] def update_finished(self, exit_code): """ handle actions after update """ delay = 5 if exit_code == 0: self.logger.warning('Restarting application in %ss after update', delay) Timer(delay, application_restart()).register(self) elif exit_code == 99: self.logger.warning('Rebooting in %ss after update', delay) cmd = 'rb' Timer(delay, handle(cmd), self.workshop.channel).register(self)
[docs] def application_restart(self, **kw): """ local command: terminate application """ self.logger.info("exiting for restart") self.fire(terminate())
[docs] def watchdog(self): """ send message to circus watchdog """ bind = (WATCHDOG_HOST, WATCHDOG_PORT) message = "{pid};{time}".format(pid=self.pid, time=time()) message = force_bytes(message) self.logger.debug('watchdog message: %s', message) self.fire(write(bind, message), 'watchdogclient')
[docs] def check_allowed(self): """ check if allowed to play """ self.logger.info('Checking if allowed to play') pi = self.player_info player = None if pi.play_audio(): player = self.audioplayer other_player = self.videoplayer if pi.play_video(): player = self.videoplayer other_player = self.audioplayer if other_player and other_player in self.components: self.fire(disable_player, other_player.channel) if player: if not pi.can_play(): self.logger.warning('Not allowed to play %s', player.channel) start_delta, end_delta = pi.opening_time_deltas() self.logger.debug('opening times deltas: %s - %s', start_delta, end_delta) # status = 'enabled' if player.enabled else 'disabled' # msg = ' '.join([player.channel, status]) if player.enabled: if end_delta is not None and end_delta <= ALLOWED_INTERVAL: end_delta = max(end_delta, 0.1) Timer(end_delta, disable_player(), player.channel).register(self) self.logger.info('disabling %s in %s', player.channel, end_delta) else: # if not playing find next start if start_delta is not None and start_delta <= ALLOWED_INTERVAL: if player not in self.components: player.register(self) start_delta = max(start_delta, 0.1) Timer(start_delta, enable_player(), player.channel).register(self) self.logger.info('enabling %s in %s', player.channel, start_delta) else: self.fire(set_status('not_allowed'))
def download_disabled(self, flag): if self.download_disabled and not flag: # if download gets enabled continue downlaoding self.fire(download_message('download_enabled')) self.fire(check_download()) self.download_disabled = flag
[docs] def finish_download(self): """ reset download status """ #self.status = self._STATES.RUNNING self.fire(set_dwld_status('finished'))
[docs] def check_download(self): """ check download status """ self.logger.info('Checking download status') newstorage = NewProgramData(self.data_dir) storage = ProgramData(self.data_dir) if newstorage.exists() or not storage.all_files_exist(): info = self.player_info if not info.can_download(): self.logger.warning('Download not allowed.') dly = seconds_until(info.dwld_start) + 3 Timer(dly, check_download()).register(self) self.fire(set_dwld_status('waiting')) kw = {'until': time_format(info.dwld_start)} self.fire(download_message('download_not_allowed', **kw)) else: if self.download_disabled: self.logger.warning('Download disabled.') self.fire(download_message('download_disabled')) else: self.fire(start_download(), self.dwldman.channel)
[docs] def new_program(self): """ handle new program event """ self.logger.debug('new program') self.check_download()
[docs] def new_info(self): """ handle new info event """ self.logger.info('new info') self.player_info = PlayerInfo(self.data_dir) # shorten allowed check self.allowed_timer.expiry = time() + 0.1 changed = self._check_remote_config() if not changed: self.fire(check_download())
[docs] def no_song_to_play(self): """ check the MediaStream or get the program from server """ self.logger.info('no song to play.') try: stream = MediaStream(ProgramData(self.data_dir)) except ProgramData.DataNotPresent: self.logger.warning('Missing program data.') self.fire(get_program()) if stream.total_files > 0: self.logger.info('total files: %s, existing: %s, sum play counts: %s', stream.total_files, stream.n_existing_files, stream.n_play_counts) if stream.total_files > stream.n_existing_files: # start download self.fire(check_download()) else: # reload programs after a while Timer(CHECK_NO_SONG_INTERVAL, reload_programs()).register(self) else: # get new program from server self.fire(get_program())
[docs] @handler("signal", channel="*") def signal(self, signo, stack): """ catch interrupt """ if signo == SIGHUP: self.config.reload_config() elif signo in (SIGINT, SIGTERM): self.fire(terminate()) return True
def terminate(self): self.stop()