class MediaShow: # ******************* # External interface # ******************** def __init__(self, show, canvas, showlist, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon=Monitor() self.mon.on() #instantiate arguments self.show =show self.showlist=showlist self.canvas=canvas self.pp_home=pp_home self.pp_profile=pp_profile # open resources self.rr=ResourceReader() # Init variables self.player=None self.shower=None self._poll_for_interval_timer=None self._poll_for_continue_timer=None self._waiting_for_interval=False self._interval_timer=None self.error=False self._interval_timer_signal=False self._end_mediashow_signal=False self._next_track_signal=False self._previous_track_signal=False self._play_child_signal = False self._req_next='nil' self._state='closed' def play(self,end_callback,ready_callback=None, top=False,command='nil'): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate the arguments self._end_callback=end_callback self._ready_callback=ready_callback self.top=top self.command=command self.mon.log(self,"Starting show: " + self.show['show-ref']) # check data files are available. self.media_file = self.pp_profile + "/" + self.show['medialist'] if not os.path.exists(self.media_file): self.mon.err(self,"Medialist file not found: "+ self.media_file) self._end('error',"Medialist file not found") #create a medialist for the mediashow and read it. self.medialist=MediaList() if self.medialist.open_list(self.media_file,self.showlist.sissue())==False: self.mon.err(self,"Version of medialist different to Pi Presents") self._end('error',"Version of medialist different to Pi Presents") self._wait_for_trigger() # respond to key presses. def key_pressed(self,key_name): self.mon.log(self,"received key: " + key_name) if key_name=='': pass elif key_name=='escape': # if next lower show is running pass down to stop the show and lower level if self.shower<>None: self.shower.key_pressed(key_name) # if not at top stop the show else: if self.top == False: self._end_mediashow_signal=True # and if a track is running stop that first if self.player<>None: self.player.key_pressed(key_name) else: # at top level in a manual presentation stop the track if self.show['progress']=='manual': if self.player<>None: self.player.key_pressed(key_name) elif key_name in ('up','down'): # if child or sub-show is running and is a show pass to show, track does not use up/down # otherwise use keys for next or previous if self.shower<>None: self.shower.key_pressed(key_name) else: if key_name=='up': self._previous() else: self._next() elif key_name=='return': # if child show or sub-show is running and is show - pass down- player does not use return # ELSE use Return to start child or to start the show if waiting if self.shower<>None: self.shower.key_pressed(key_name) else: if self._state=='playing': if self.show['has-child']=='yes': self._play_child_signal=True # and stop the current track if its running if self.player<>None: self.player.key_pressed("escape") else: self._start_show() elif key_name=='pir': self._start_show() elif key_name in ('p',' '): # pass down if show or track running. if self.shower<>None: self.shower.key_pressed(key_name) elif self.player<>None: self.player.key_pressed(key_name) def button_pressed(self,button,edge): if button=='play': self.key_pressed("return") elif button =='up': self.key_pressed("up") elif button=='down': self.key_pressed("down") elif button=='stop': self.key_pressed("escape") elif button=='pause': self.key_pressed('p') elif button=='PIR': self.key_pressed('pir') # kill or error def terminate(self,reason): if self.shower<>None: self.mon.log(self,"sent terminate to shower") self.shower.terminate(reason) elif self.player<>None: self.mon.log(self,"sent terminate to player") self.player.terminate(reason) else: self._end(reason,'terminated without terminating shower or player') def _tidy_up(self): if self._poll_for_continue_timer<>None: self.canvas.after_cancel(self._poll_for_continue_timer) self._poll_for_continue_timer=None if self._poll_for_interval_timer<>None: self.canvas.after_cancel(self._poll_for_interval_timer) self._poll_for_interval_timer=None if self._interval_timer<>None: self.canvas.after_cancel(self._interval_timer) self._interval_timer=None def resource(self,section,item): value=self.rr.get(section,item) if value==False: self.mon.err(self, "resource: "+section +': '+ item + " not found" ) self.terminate("error") else: return value # *************************** # Respond to key/button presses # *************************** def _stop(self,message): self._end_mediashow_signal=True if self._interval_timer<>None: self.canvas.after_cancel(self._interval_timer) def _next(self): # stop track if running and set signal self._next_track_signal=True if self.shower<>None: self.shower.key_pressed("escape") else: if self.player<>None: self.player.key_pressed("escape") def _previous(self): self._previous_track_signal=True if self.shower<>None: self.shower.key_pressed("escape") else: if self.player<>None: self.player.key_pressed("escape") # *************************** # end of show functions # *************************** def _end(self,reason,message): self._end_mediashow_signal=False self.mon.log(self,"Ending Mediashow: "+ self.show['show-ref']) self._tidy_up() self._end_callback(reason,message) self=None return # *************************** # Show sequencer # *************************** def _wait_for_trigger(self): self._state='waiting' if self.ready_callback<>None: self.ready_callback() self.mon.log(self,"Waiting for trigger: "+ self.show['trigger']) if self.show['trigger']=="button": # blank screen waiting for trigger if auto, otherwise display something if self.show['progress']=="manual": text= self.resource('mediashow','m01') else: text="" self.display_message(self.canvas,'text',text,0,self._start_show) elif self.show['trigger']=="PIR": # blank screen waiting for trigger text = self.resource('mediashow','m02') self.display_message(self.canvas,'text',text,0,self._start_show) elif self.show['trigger']=="start": self._start_show() else: self.mon.err(self,"Unknown trigger: "+ self.show['trigger']) self._end('error',"Unknown trigger type") def _start_show(self): self._state='playing' self._direction='forward' # start interval timer if self.show['repeat']=="interval" and self.show['repeat-interval']<>0: self._interval_timer_signal=False self._interval_timer=self.canvas.after(int(self.show['repeat-interval'])*1000,self._end_interval_timer) # and play the first track unless commanded otherwise if self.command=='backward': self.medialist.finish() else: self.medialist.start() self._play_selected_track(self.medialist.selected_track()) def _what_next(self): self._direction='forward' # user wants to end, wait for any shows or tracks to have ended then end show if self._end_mediashow_signal==True: if self.player==None and self.shower==None: self._end_mediashow_signal=False self._end('normal',"show ended by user") else: pass #returning from a subshow needing to move onward elif self._req_next=='do-next': self._req_next='nil' self.medialist.next() self._play_selected_track(self.medialist.selected_track()) #returning from a subshow needing to move backward elif self._req_next=='do-previous': self._req_next='nil' self._direction='backward' self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) # user wants to play child elif self._play_child_signal == True: self._play_child_signal=False index = self.medialist.index_of_track('pp-child-show') if index >=0: #don't select the track as need to preserve mediashow sequence. child_track=self.medialist.track(index) self._display_eggtimer(self.resource('mediashow','m07')) self._play_selected_track(child_track) else: self.mon.err(self,"Child show not found in medialist: "+ self.show['pp-child-show']) self._end('error',"child show not found in medialist") # skip to next track on user input elif self._next_track_signal==True: self._next_track_signal=False if self.medialist.at_end()==True: if self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False: self._end('do-next',"Return from Sub Show") else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self._previous_track_signal==True: self._previous_track_signal=False self._direction='backward' if self.medialist.at_start()==True: if self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False: self._end('do-previous',"Return from Sub Show") else: self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) else: self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show['progress']=="auto": if self.medialist.at_end()==True: if self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==False: self._end('do-next',"Return from Sub Show") #### elif elif self.show['sequence']=="ordered" and self.show['repeat']=='oneshot' and self.top==True: self._wait_for_trigger() elif self._waiting_for_interval==True: if self._interval_timer_signal==True: self._interval_timer_signal=False self._waiting_for_interval=False self._start_show() else: self._poll_for_interval_timer=self.canvas.after(1000,self._what_next) elif self.show['sequence']=="ordered" and self.show['repeat']=='interval' and int(self.show['repeat-interval'])>0: self._waiting_for_interval=True self._poll_for_interval_timer=self.canvas.after(1000,self._what_next) elif self.show['sequence']=="ordered" and self.show['repeat']=='interval' and int(self.show['repeat-interval'])==0: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) else: self.mon.err(self,"Unhandled playing event: ") self._end('error',"Unhandled playing event") else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show['progress']=="manual": self._delete_eggtimer() self._display_eggtimer(self.resource('mediashow','m03')) self._poll_for_continue_timer=self.canvas.after(500,self._what_next) else: #unhandled state self.mon.err(self,"Unhandled playing event: ") self._end('error',"Unhandled playing event") def _end_interval_timer(self): self._interval_timer_signal=True # *************************** # Dispatching to Players/Shows # *************************** # used to display internal messages in situations where a medialist entry could be used. def display_message(self,canvas,source,content,duration,_display_message_callback): self._display_message_callback=_display_message_callback tp={'duration':duration,'message-colour':'white','message-font':'Helvetica 20 bold'} self.player=MessagePlayer(canvas,tp,tp) self.player.play(content,self._display_message_end,None) def _display_message_end(self,reason,message): self.player=None if reason in ('error','killed'): self._end(reason,message) else: self._display_message_callback() def complete_path(self,selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file[0]=="+": track_file=self.pp_home+track_file[1:] self.mon.log(self,"Track to play is: "+ track_file) return track_file def _play_selected_track(self,selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ self.canvas.delete(ALL) if self.show['progress']=="manual": self._display_eggtimer(self.resource('mediashow','m04')) # is menu required if self.show['has-child']=="yes": enable_child=True else: enable_child=False #dispatch track by type self.player=None self.shower=None track_type = selected_track['type'] self.mon.log(self,"Track type is: "+ track_type) if track_type=="video": # create a videoplayer track_file=self.complete_path(selected_track) self.player=VideoPlayer(self.canvas,self.show,selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type=="image": track_file=self.complete_path(selected_track) # images played from menus don't have children self.player=ImagePlayer(self.canvas,self.show,selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type=="message": # bit odd because MessagePlayer is used internally to display text. text=selected_track['text'] self.player=MessagePlayer(self.canvas,self.show,selected_track) self.player.play(text, self.end_player, self.ready_callback, enable_menu=enable_child ) elif track_type=="show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >=0: self.showlist.select(index) selected_show=self.showlist.selected_show() else: self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show']) self._end('error',"Unknown show") if selected_show['type']=="mediashow": self.shower= MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower,top=False,command=self._direction) elif selected_show['type']=="liveshow": self.shower= LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower,top=False,command='nil') elif selected_show['type']=="menu": self.shower= MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower,top=False,command='nil') else: self.mon.err(self,"Unknown Show Type: "+ selected_show['type']) self._end('error'"Unknown show type") else: self.mon.err(self,"Unknown Track Type: "+ track_type) self._end('error',"Unknown track type") def ready_callback(self): self._delete_eggtimer() def end_player(self,reason,message): self._req_next='nil' self.mon.log(self,"Returned from player with message: "+ message) self.player=None if reason in("killed","error"): self._end(reason,message) elif self.show['progress']=="manual": self._display_eggtimer(self.resource('mediashow','m05')) self._req_next=reason self._what_next() else: self._req_next=reason self._what_next() def end_shower(self,reason,message): self._req_next='nil' self.mon.log(self,"Returned from shower with message: "+ message) self.shower=None if reason in("killed","error"): self._end(reason,message) elif self.show['progress']=="manual": self._display_eggtimer(self.resource('mediashow','m06')) self._req_next=reason self._what_next() else: self._req_next=reason self._what_next() def _display_eggtimer(self,text): self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2, text= text, fill='white', font="Helvetica 20 bold") self.canvas.update_idletasks( ) def _delete_eggtimer(self): self.canvas.delete(ALL)
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon = Monitor() self.mon.on() #instantiate arguments self.show_params = show_params self.showlist = showlist self.root = root self.canvas = canvas self.pp_dir = pp_dir self.pp_home = pp_home self.pp_profile = pp_profile # open resources self.rr = ResourceReader() # Init variables self.player = None self.shower = None self.poll_for_interval_timer = None self.poll_for_continue_timer = None self.waiting_for_interval = False self.interval_timer = None self.duration_timer = None self.error = False self.interval_timer_signal = False self.end_trigger_signal = False self.end_mediashow_signal = False self.next_track_signal = False self.previous_track_signal = False self.play_child_signal = False self.req_next = 'nil' #create and instance of TimeOfDay scheduler so we can add events self.tod = TimeOfDay() self.state = 'closed' def play(self, show_id, end_callback, show_ready_callback, top=False, command='nil'): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate the arguments self.show_id = show_id self.end_callback = end_callback self.show_ready_callback = show_ready_callback self.top = top self.command = command self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Starting show") # check data files are available. self.media_file = self.pp_profile + "/" + self.show_params['medialist'] if not os.path.exists(self.media_file): self.mon.err(self, "Medialist file not found: " + self.media_file) self.end('error', "Medialist file not found") #create a medialist for the mediashow and read it. self.medialist = MediaList() if self.medialist.open_list(self.media_file, self.showlist.sissue()) == False: self.mon.err(self, "Version of medialist different to Pi Presents") self.end('error', "Version of medialist different to Pi Presents") #get controls for this show if top level controlsmanager = ControlsManager() if self.top == True: self.controls_list = controlsmanager.default_controls() # and merge in controls from profile self.controls_list = controlsmanager.merge_show_controls( self.controls_list, self.show_params['controls']) #set up the time of day triggers for the show if self.show_params['trigger'] in ('time', 'time-quiet'): error_text = self.tod.add_times(self.show_params['trigger-input'], id(self), self.tod_start_callback, self.show_params['trigger']) if error_text <> '': self.mon.err(self, error_text) self.end('error', error_text) if self.show_params['trigger-end'] == 'time': # print self.show_params['trigger-end-time'] error_text = self.tod.add_times( self.show_params['trigger-end-time'], id(self), self.tod_end_callback, 'n/a') if error_text <> '': self.mon.err(self, error_text) self.end('error', error_text) if self.show_params['trigger-end'] == 'duration': error_text = self.calculate_duration( self.show_params['trigger-end-time']) if error_text <> '': self.mon.err(self, error_text) self.end('error', error_text) self.state = 'closed' self.egg_timer = None self.wait_for_trigger() # ******************************** # Respond to external events # ******************************** #stop received from another concurrent show def managed_stop(self): # if next lower show is running pass down to stop the show and lower level if self.shower <> None: self.shower.managed_stop() else: #stop the show if not at top self.end_mediashow_signal = True # and if track is runing stop that first if self.player <> None: self.player.input_pressed('stop') # kill or error def terminate(self, reason): if self.shower <> None: self.shower.terminate(reason) elif self.player <> None: self.player.terminate(reason) else: self.end(reason, ' terminated with no shower or player to terminate') # respond to input events def input_pressed(self, symbol, edge, source): self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": received input: " + symbol) # check symbol against mediashow triggers, triggers can be used at top or lower level # and not affected by disable-controls if self.state == 'waiting' and self.show_params['trigger'] in ( 'input', 'input-quiet') and symbol == self.show_params['trigger-input']: self.start_show() elif self.state == 'playing' and self.show_params[ 'trigger-next'] == 'input' and symbol == self.show_params[ 'next-input']: self.next() # internal functions are triggered only when disable-controls is 'no' if self.show_params['disable-controls'] == 'yes': return # if at top convert symbolic name to operation otherwise lower down we have received an operation # look through list of standard symbols to find match (symbolic-name, function name) operation =lookup (symbol if self.top == True: operation = self.lookup_control(symbol, self.controls_list) else: operation = symbol # print 'operation',operation self.do_operation(operation, edge, source) #service the standard inputs for this show def do_operation(self, operation, edge, source): if self.shower <> None: # if next lower show is running pass down to stop the show and lower level self.shower.input_pressed(operation, edge, source) else: # control this show and its tracks # print 'operation',operation if operation == 'stop': if self.top == False: # not at top so stop the current show self.end_mediashow_signal = True # and if a track is running stop that first if self.player <> None: self.player.input_pressed('stop') else: # top = True, just stop track if running if self.player <> None: self.player.input_pressed('stop') elif operation in ('up', 'down'): #if playing rather than waiting use keys for next or previous if operation == 'up' and self.state == 'playing': self.previous() else: self.next() elif operation == 'play': # use 'play' to start child if state=playing or to trigger the show if waiting for trigger if self.state == 'playing': if self.show_params['has-child'] == 'yes': self.play_child_signal = True self.child_track_ref = 'pp-child-show' # and stop the current track if its running if self.player <> None: self.player.input_pressed('stop') else: if self.state == 'waiting': self.start_show() elif operation == 'pause': if self.player <> None: self.player.input_pressed(operation) #if the operation is omxplayer or mplayer runtime control then pass it to player if running elif operation[0:4] == 'omx-' or operation[ 0:6] == 'mplay-' or operation[0:5] == 'uzbl-': if self.player <> None: self.player.input_pressed(operation) def lookup_control(self, symbol, controls_list): for control in controls_list: if symbol == control[0]: return control[1] return '' # *************************** # Show sequencer # *************************** def end_interval_timer(self): self.interval_timer_signal = True # callback from time of day scheduler def tod_start_callback(self): if self.state == 'waiting' and self.show_params['trigger'] in ( 'time', 'time-quiet'): self.start_show() def tod_end_callback(self): if self.state == 'playing' and self.show_params['trigger-end'] in ( 'time', 'duration'): self.end_trigger_signal = True if self.shower <> None: self.shower.input_pressed('stop') elif self.player <> None: self.player.input_pressed('stop') def stop(self, message): self.end_mediashow_signal = True if self.interval_timer <> None: self.canvas.after_cancel(self.interval_timer) def next(self): # stop track if running and set signal self.next_track_signal = True if self.shower <> None: self.shower.input_pressed("stop") else: if self.player <> None: self.player.input_pressed("stop") def previous(self): self.previous_track_signal = True if self.shower <> None: self.shower.input_pressed("stop") else: if self.player <> None: self.player.input_pressed("stop") # wait for trigger sets the state to waiting so that events can do a start show. def wait_for_trigger(self): self.state = 'waiting' if self.show_ready_callback <> None: self.show_ready_callback() self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Waiting for trigger: " + self.show_params['trigger']) if self.show_params['trigger'] == "input": # blank screen waiting for trigger if auto, otherwise display something if self.show_params['progress'] == "manual": text = self.resource('mediashow', 'm01') else: text = self.resource('mediashow', 'm02') self.display_message(self.canvas, 'text', text, 0, self.start_show) elif self.show_params['trigger'] == "input-quiet": # blank screen waiting for trigger text = self.resource('mediashow', 'm10') self.display_message(self.canvas, 'text', text, 0, self.start_show) pass elif self.show_params['trigger'] in ('time', 'time-quiet'): # show next show notice quiet = 3 # if next show is this one display text next_show = self.tod.next_event_time() if next_show[quiet] == False: if next_show[1] == 'tomorrow': text = self.resource('mediashow', 'm09') else: text = self.resource('mediashow', 'm08') text = text.replace('%tt', next_show[0]) self.display_message(self.canvas, 'text', text, 0, self.start_show) elif self.show_params['trigger'] == "start": self.start_show() else: self.mon.err(self, "Unknown trigger: " + self.show_params['trigger']) self.end('error', "Unknown trigger type") def start_show(self): self.state = 'playing' self.direction = 'forward' # self.canvas.delete(ALL) # start interval timer if self.show_params['repeat'] == "interval" and self.show_params[ 'repeat-interval'] <> 0: self.interval_timer_signal = False self.interval_timer = self.canvas.after( int(self.show_params['repeat-interval']) * 1000, self.end_interval_timer) # start duration timer if self.show_params['trigger-end'] == 'duration': # print 'set alarm ', self.duration self.duration_timer = self.canvas.after(self.duration * 1000, self.tod_end_callback) # and play the first track unless commanded otherwise if self.command == 'backward': self.medialist.finish() else: self.medialist.start() self.play_selected_track(self.medialist.selected_track()) def what_next(self): self.direction = 'forward' # end of show trigger caused by tod if self.end_trigger_signal == True: self.end_trigger_signal = False if self.top == True: self.state = 'waiting' self.wait_for_trigger() else: # not at top so stop the show self.end('normal', 'sub-show end time trigger') # user wants to end, wait for any shows or tracks to have ended then end show # probalby will get here with end_m set when player and shower has finished elif self.end_mediashow_signal == True: if self.player == None and self.shower == None: self.end_mediashow_signal = False self.end('normal', "show ended by user") #returning from a subshow needing to move onward elif self.req_next == 'do-next': self.req_next = 'nil' self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) #returning from a subshow needing to move backward elif self.req_next == 'do-previous': self.req_next = 'nil' self.direction = 'backward' self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # user wants to play child elif self.play_child_signal == True: self.play_child_signal = False index = self.medialist.index_of_track(self.child_track_ref) if index >= 0: #don't use select the track as need to preserve mediashow sequence. child_track = self.medialist.track(index) self.display_eggtimer(self.resource('mediashow', 'm07')) self.play_selected_track(child_track) else: self.mon.err( self, "Child show not found in medialist: " + self.show_params['pp-child-show']) self.end('error', "child show not found in medialist") # skip to next track on user input elif self.next_track_signal == True: self.next_track_signal = False if self.medialist.at_end() == True: if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'oneshot' and self.top == False: self.end('do-next', "Return from Sub Show") elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run' and self.top == False: self.end('do-next', "Return from Sub Show") else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self.previous_track_signal == True: self.previous_track_signal = False self.direction = 'backward' if self.medialist.at_start() == True: if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'oneshot' and self.top == False: self.end('do-previous', "Return from Sub Show") elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run' and self.top == False: self.end('do-previous', "Return from Sub Show") else: self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show_params['progress'] == "auto": if self.medialist.at_end() == True: # oneshot if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'oneshot' and self.top == False: self.end('normal', "End of Oneshot in subshow") elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'oneshot' and self.top == True: self.wait_for_trigger() # single run elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run' and self.top == True: self.end('normal', "End of Single Run") elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run' and self.top == False: self.end('do-next', "End of single run - Return from Sub Show") # repeating and waiting to restart elif self.waiting_for_interval == True: if self.interval_timer_signal == True: self.interval_timer_signal = False self.waiting_for_interval = False self.start_show() else: self.poll_for_interval_timer = self.canvas.after( 1000, self.what_next) elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'interval' and int( self.show_params['repeat-interval']) > 0: self.waiting_for_interval = True self.poll_for_interval_timer = self.canvas.after( 1000, self.what_next) #elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0: elif self.show_params['repeat'] == 'interval' and int( self.show_params['repeat-interval']) == 0: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # shuffling so there is no end condition elif self.show_params['sequence'] == "shuffle": self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.mon.err( self, "Unhandled playing event: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['repeat-interval']) self.end('error', "Unhandled playing event") else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show_params['progress'] == "manual": self.delete_eggtimer() self.canvas.delete('pp-content') if self.show_params['trigger-next'] == 'input': self.display_eggtimer(self.resource('mediashow', 'm03')) self.poll_for_continue_timer = self.canvas.after( 2000, self.what_next) else: #unhandled state self.mon.err(self, "Unhandled playing event: ") self.end('error', "Unhandled playing event") # *************************** # Dispatching to Players/Shows # *************************** def ready_callback(self): self.delete_eggtimer() def play_selected_track(self, selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ self.delete_eggtimer() if self.show_params['progress'] == "manual": self.display_eggtimer(self.resource('mediashow', 'm04')) # is menu required if self.show_params['has-child'] == "yes": self.enable_child = True else: self.enable_child = False #dispatch track by type self.player = None self.shower = None track_type = selected_track['type'] self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Track type is: " + track_type) if track_type == "video": # create a videoplayer track_file = self.complete_path(selected_track) self.player = VideoPlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "audio": # create a audioplayer track_file = self.complete_path(selected_track) self.player = AudioPlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "web": # create a browser track_file = self.complete_path(selected_track) self.player = BrowserPlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "image": track_file = self.complete_path(selected_track) # images played from menus don't have children self.player = ImagePlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "message": # bit odd because MessagePlayer is used internally to display text. text = selected_track['text'] self.player = MessagePlayer(self.show_id, self.root, self.canvas, self.show_params, selected_track, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(text, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type == "show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >= 0: self.showlist.select(index) selected_show = self.showlist.selected_show() else: self.mon.err( self, "Show not found in showlist: " + selected_track['sub-show']) self.end('error', "Unknown show") if selected_show['type'] == "mediashow": self.shower = MediaShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command=self.direction) elif selected_show['type'] == "liveshow": self.shower = LiveShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command='nil') elif selected_show['type'] == "radiobuttonshow": self.shower = RadioButtonShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command='nil') elif selected_show['type'] == "hyperlinkshow": self.shower = HyperlinkShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command='nil') elif selected_show['type'] == "menu": self.shower = MenuShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, self.ready_callback, top=False, command='nil') else: self.mon.err(self, "Unknown Show Type: " + selected_show['type']) self.end('error' "Unknown show type") else: self.mon.err(self, "Unknown Track Type: " + track_type) self.end('error', "Unknown track type") def end_player(self, reason, message): self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Returned from player with message: " + message) self.player = None self.req_next = 'nil' if reason in ("killed", "error"): self.end(reason, message) else: # elif>else move to what-next? if self.show_params['progress'] == "manual": self.display_eggtimer(self.resource('mediashow', 'm05')) self.req_next = reason self.what_next() else: self.req_next = reason self.what_next() def end_shower(self, show_id, reason, message): self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Returned from shower with message: " + message) self.shower = None self.req_next = 'nil' if reason in ("killed", "error"): self.end(reason, message) else: if self.show_params['progress'] == "manual": self.display_eggtimer(self.resource('mediashow', 'm06')) self.req_next = reason self.what_next() else: self.req_next = reason self.what_next() # *************************** # end of show # *************************** def end(self, reason, message): self.end_mediashow_signal = False self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Ending Mediashow") self.tidy_up() self.end_callback(self.show_id, reason, message) self = None return def tidy_up(self): #clear outstanding time of day events for this show # self.tod.clear_times_list(id(self)) if self.poll_for_continue_timer <> None: self.canvas.after_cancel(self.poll_for_continue_timer) self.poll_for_continue_timer = None if self.poll_for_interval_timer <> None: self.canvas.after_cancel(self.poll_for_interval_timer) self.poll_for_interval_timer = None if self.interval_timer <> None: self.canvas.after_cancel(self.interval_timer) self.interval_timer = None if self.duration_timer <> None: self.canvas.after_cancel(self.duration_timer) self.duration_timer = None # *************************** # displaying things # *************************** def display_eggtimer(self, text): self.canvas.create_text(int(self.canvas['width']) / 2, int(self.canvas['height']) / 2, text=text, fill='white', font="Helvetica 20 bold", tag='pp-eggtimer') self.canvas.update_idletasks() def delete_eggtimer(self): self.canvas.delete('pp-eggtimer') self.canvas.update_idletasks() # used to display internal messages in situations where a medialist entry could not be used. def display_message(self, canvas, source, content, duration, _display_message_callback): self.display_message_callback = _display_message_callback tp = { 'duration': duration, 'message-colour': 'white', 'message-font': 'Helvetica 20 bold', 'background-colour': '', 'message-justify': 'left', 'background-image': '', 'show-control-begin': '', 'show-control-end': '', 'animate-begin': '', 'animate-clear': '', 'animate-end': '', 'message-x': '', 'message-y': '', 'display-show-background': 'no', 'display-show-text': 'no', 'show-text': '', 'track-text': '', 'plugin': '' } self.player = MessagePlayer(self.show_id, self.root, canvas, tp, tp, self.pp_dir, self.pp_home, self.pp_profile) self.player.play(content, self.showlist, self.display_message_end, None, False) def display_message_end(self, reason, message): self.player = None if reason in ('error', 'killed'): self.end(reason, message) else: self.display_message_callback() # *************************** # utilities # *************************** def calculate_duration(self, line): fields = line.split(':') if len(fields) == 1: secs = fields[0] minutes = '0' hours = '0' if len(fields) == 2: secs = fields[1] minutes = fields[0] hours = '0' if len(fields) == 3: secs = fields[2] minutes = fields[1] hours = fields[0] self.duration = 3600 * long(hours) + 60 * long(minutes) + long(secs) return '' def resource(self, section, item): value = self.rr.get(section, item) if value == False: self.mon.err(self, "resource: " + section + ': ' + item + " not found") self.terminate("error") else: return value def complete_path(self, selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file <> '' and track_file[0] == "+": track_file = self.pp_home + track_file[1:] self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Track to play is: " + track_file) return track_file
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show, canvas, showlist, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon = Monitor() self.mon.on() #instantiate arguments self.show = show self.showlist = showlist self.canvas = canvas self.pp_home = pp_home self.pp_profile = pp_profile # open resources self.rr = ResourceReader() # Init variables self.player = None self.shower = None self._poll_for_interval_timer = None self._poll_for_continue_timer = None self._waiting_for_interval = False self._interval_timer = None self.error = False self._interval_timer_signal = False self._end_mediashow_signal = False self._next_track_signal = False self._previous_track_signal = False self._play_child_signal = False self._req_next = 'nil' self._state = 'closed' def play(self, end_callback, ready_callback=None, top=False, command='nil'): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate the arguments self._end_callback = end_callback self._ready_callback = ready_callback self.top = top self.command = command self.mon.log(self, "Starting show: " + self.show['show-ref']) # check data files are available. self.media_file = self.pp_profile + "/" + self.show['medialist'] if not os.path.exists(self.media_file): self.mon.err(self, "Medialist file not found: " + self.media_file) self._end('error', "Medialist file not found") #create a medialist for the mediashow and read it. self.medialist = MediaList() if self.medialist.open_list(self.media_file, self.showlist.sissue()) == False: self.mon.err(self, "Version of medialist different to Pi Presents") self._end('error', "Version of medialist different to Pi Presents") self._wait_for_trigger() # respond to key presses. def key_pressed(self, key_name): self.mon.log(self, "received key: " + key_name) if key_name == '': pass elif key_name == 'escape': # if next lower show is running pass down to stop the show and lower level if self.shower <> None: self.shower.key_pressed(key_name) # if not at top stop the show else: if self.top == False: self._end_mediashow_signal = True # and if a track is running stop that first if self.player <> None: self.player.key_pressed(key_name) else: # at top level in a manual presentation stop the track if self.show['progress'] == 'manual': if self.player <> None: self.player.key_pressed(key_name) elif key_name in ('up', 'down'): # if child or sub-show is running and is a show pass to show, track does not use up/down # otherwise use keys for next or previous if self.shower <> None: self.shower.key_pressed(key_name) else: if key_name == 'up': self._previous() else: self._next() elif key_name == 'return': # if child show or sub-show is running and is show - pass down- player does not use return # ELSE use Return to start child or to start the show if waiting if self.shower <> None: self.shower.key_pressed(key_name) else: if self._state == 'playing': if self.show['has-child'] == 'yes': self._play_child_signal = True # and stop the current track if its running if self.player <> None: self.player.key_pressed("escape") else: self._start_show() elif key_name == 'pir': self._start_show() elif key_name in ('p', ' '): # pass down if show or track running. if self.shower <> None: self.shower.key_pressed(key_name) elif self.player <> None: self.player.key_pressed(key_name) def button_pressed(self, button, edge): if button == 'play': self.key_pressed("return") elif button == 'up': self.key_pressed("up") elif button == 'down': self.key_pressed("down") elif button == 'stop': self.key_pressed("escape") elif button == 'pause': self.key_pressed('p') elif button == 'PIR': self.key_pressed('pir') # kill or error def terminate(self, reason): if self.shower <> None: self.mon.log(self, "sent terminate to shower") self.shower.terminate(reason) elif self.player <> None: self.mon.log(self, "sent terminate to player") self.player.terminate(reason) else: self._end(reason, 'terminated without terminating shower or player') def _tidy_up(self): if self._poll_for_continue_timer <> None: self.canvas.after_cancel(self._poll_for_continue_timer) self._poll_for_continue_timer = None if self._poll_for_interval_timer <> None: self.canvas.after_cancel(self._poll_for_interval_timer) self._poll_for_interval_timer = None if self._interval_timer <> None: self.canvas.after_cancel(self._interval_timer) self._interval_timer = None def resource(self, section, item): value = self.rr.get(section, item) if value == False: self.mon.err(self, "resource: " + section + ': ' + item + " not found") self.terminate("error") else: return value # *************************** # Respond to key/button presses # *************************** def _stop(self, message): self._end_mediashow_signal = True if self._interval_timer <> None: self.canvas.after_cancel(self._interval_timer) def _next(self): # stop track if running and set signal self._next_track_signal = True if self.shower <> None: self.shower.key_pressed("escape") else: if self.player <> None: self.player.key_pressed("escape") def _previous(self): self._previous_track_signal = True if self.shower <> None: self.shower.key_pressed("escape") else: if self.player <> None: self.player.key_pressed("escape") # *************************** # end of show functions # *************************** def _end(self, reason, message): self._end_mediashow_signal = False self.mon.log(self, "Ending Mediashow: " + self.show['show-ref']) self._tidy_up() self._end_callback(reason, message) self = None return # *************************** # Show sequencer # *************************** def _wait_for_trigger(self): self._state = 'waiting' if self.ready_callback <> None: self.ready_callback() self.mon.log(self, "Waiting for trigger: " + self.show['trigger']) if self.show['trigger'] == "button": # blank screen waiting for trigger if auto, otherwise display something if self.show['progress'] == "manual": text = self.resource('mediashow', 'm01') else: text = "" self.display_message(self.canvas, 'text', text, 0, self._start_show) elif self.show['trigger'] == "PIR": # blank screen waiting for trigger text = self.resource('mediashow', 'm02') self.display_message(self.canvas, 'text', text, 0, self._start_show) elif self.show['trigger'] == "start": self._start_show() else: self.mon.err(self, "Unknown trigger: " + self.show['trigger']) self._end('error', "Unknown trigger type") def _start_show(self): self._state = 'playing' self._direction = 'forward' # start interval timer if self.show[ 'repeat'] == "interval" and self.show['repeat-interval'] <> 0: self._interval_timer_signal = False self._interval_timer = self.canvas.after( int(self.show['repeat-interval']) * 1000, self._end_interval_timer) # and play the first track unless commanded otherwise if self.command == 'backward': self.medialist.finish() else: self.medialist.start() self._play_selected_track(self.medialist.selected_track()) def _what_next(self): self._direction = 'forward' # user wants to end, wait for any shows or tracks to have ended then end show if self._end_mediashow_signal == True: if self.player == None and self.shower == None: self._end_mediashow_signal = False self._end('normal', "show ended by user") else: pass #returning from a subshow needing to move onward elif self._req_next == 'do-next': self._req_next = 'nil' self.medialist.next() self._play_selected_track(self.medialist.selected_track()) #returning from a subshow needing to move backward elif self._req_next == 'do-previous': self._req_next = 'nil' self._direction = 'backward' self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) # user wants to play child elif self._play_child_signal == True: self._play_child_signal = False index = self.medialist.index_of_track('pp-child-show') if index >= 0: #don't select the track as need to preserve mediashow sequence. child_track = self.medialist.track(index) self._display_eggtimer(self.resource('mediashow', 'm07')) self._play_selected_track(child_track) else: self.mon.err( self, "Child show not found in medialist: " + self.show['pp-child-show']) self._end('error', "child show not found in medialist") # skip to next track on user input elif self._next_track_signal == True: self._next_track_signal = False if self.medialist.at_end() == True: if self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'oneshot' and self.top == False: self._end('do-next', "Return from Sub Show") else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self._previous_track_signal == True: self._previous_track_signal = False self._direction = 'backward' if self.medialist.at_start() == True: if self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'oneshot' and self.top == False: self._end('do-previous', "Return from Sub Show") else: self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) else: self.medialist.previous() self._play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show['progress'] == "auto": if self.medialist.at_end() == True: if self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'oneshot' and self.top == False: self._end('do-next', "Return from Sub Show") #### elif elif self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'oneshot' and self.top == True: self._wait_for_trigger() elif self._waiting_for_interval == True: if self._interval_timer_signal == True: self._interval_timer_signal = False self._waiting_for_interval = False self._start_show() else: self._poll_for_interval_timer = self.canvas.after( 1000, self._what_next) elif self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'interval' and int( self.show['repeat-interval']) > 0: self._waiting_for_interval = True self._poll_for_interval_timer = self.canvas.after( 1000, self._what_next) elif self.show['sequence'] == "ordered" and self.show[ 'repeat'] == 'interval' and int( self.show['repeat-interval']) == 0: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) else: self.mon.err(self, "Unhandled playing event: ") self._end('error', "Unhandled playing event") else: self.medialist.next() self._play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show['progress'] == "manual": self._delete_eggtimer() self._display_eggtimer(self.resource('mediashow', 'm03')) self._poll_for_continue_timer = self.canvas.after( 500, self._what_next) else: #unhandled state self.mon.err(self, "Unhandled playing event: ") self._end('error', "Unhandled playing event") def _end_interval_timer(self): self._interval_timer_signal = True # *************************** # Dispatching to Players/Shows # *************************** # used to display internal messages in situations where a medialist entry could be used. def display_message(self, canvas, source, content, duration, _display_message_callback): self._display_message_callback = _display_message_callback tp = { 'duration': duration, 'message-colour': 'white', 'message-font': 'Helvetica 20 bold' } self.player = MessagePlayer(canvas, tp, tp) self.player.play(content, self._display_message_end, None) def _display_message_end(self, reason, message): self.player = None if reason in ('error', 'killed'): self._end(reason, message) else: self._display_message_callback() def complete_path(self, selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file[0] == "+": track_file = self.pp_home + track_file[1:] self.mon.log(self, "Track to play is: " + track_file) return track_file def _play_selected_track(self, selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ self.canvas.delete(ALL) if self.show['progress'] == "manual": self._display_eggtimer(self.resource('mediashow', 'm04')) # is menu required if self.show['has-child'] == "yes": enable_child = True else: enable_child = False #dispatch track by type self.player = None self.shower = None track_type = selected_track['type'] self.mon.log(self, "Track type is: " + track_type) if track_type == "video": # create a videoplayer track_file = self.complete_path(selected_track) self.player = VideoPlayer(self.canvas, self.show, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "image": track_file = self.complete_path(selected_track) # images played from menus don't have children self.player = ImagePlayer(self.canvas, self.show, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "message": # bit odd because MessagePlayer is used internally to display text. text = selected_track['text'] self.player = MessagePlayer(self.canvas, self.show, selected_track) self.player.play(text, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >= 0: self.showlist.select(index) selected_show = self.showlist.selected_show() else: self.mon.err( self, "Show not found in showlist: " + selected_track['sub-show']) self._end('error', "Unknown show") if selected_show['type'] == "mediashow": self.shower = MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower, top=False, command=self._direction) elif selected_show['type'] == "liveshow": self.shower = LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower, top=False, command='nil') elif selected_show['type'] == "menu": self.shower = MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.end_shower, top=False, command='nil') else: self.mon.err(self, "Unknown Show Type: " + selected_show['type']) self._end('error' "Unknown show type") else: self.mon.err(self, "Unknown Track Type: " + track_type) self._end('error', "Unknown track type") def ready_callback(self): self._delete_eggtimer() def end_player(self, reason, message): self._req_next = 'nil' self.mon.log(self, "Returned from player with message: " + message) self.player = None if reason in ("killed", "error"): self._end(reason, message) elif self.show['progress'] == "manual": self._display_eggtimer(self.resource('mediashow', 'm05')) self._req_next = reason self._what_next() else: self._req_next = reason self._what_next() def end_shower(self, reason, message): self._req_next = 'nil' self.mon.log(self, "Returned from shower with message: " + message) self.shower = None if reason in ("killed", "error"): self._end(reason, message) elif self.show['progress'] == "manual": self._display_eggtimer(self.resource('mediashow', 'm06')) self._req_next = reason self._what_next() else: self._req_next = reason self._what_next() def _display_eggtimer(self, text): self.canvas.create_text(int(self.canvas['width']) / 2, int(self.canvas['height']) / 2, text=text, fill='white', font="Helvetica 20 bold") self.canvas.update_idletasks() def _delete_eggtimer(self): self.canvas.delete(ALL)
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon=Monitor() self.mon.on() #instantiate arguments self.show_params =show_params self.showlist=showlist self.root=root self.canvas=canvas self.pp_dir=pp_dir self.pp_home=pp_home self.pp_profile=pp_profile # open resources self.rr=ResourceReader() # Init variables self.player=None self.shower=None self.poll_for_interval_timer=None self.poll_for_continue_timer=None self.waiting_for_interval=False self.interval_timer=None self.duration_timer=None self.error=False self.interval_timer_signal=False self.end_trigger_signal=False self.end_mediashow_signal=False self.next_track_signal=False self.previous_track_signal=False self.play_child_signal = False self.req_next='nil' #create and instance of TimeOfDay scheduler so we can add events self.tod=TimeOfDay() self.state='closed' def play(self,show_id,end_callback,show_ready_callback, top=False,command='nil'): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ #instantiate the arguments self.show_id=show_id self.end_callback=end_callback self.show_ready_callback=show_ready_callback self.top=top self.command=command self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Starting show") # check data files are available. self.media_file = self.pp_profile + "/" + self.show_params['medialist'] if not os.path.exists(self.media_file): self.mon.err(self,"Medialist file not found: "+ self.media_file) self.end('error',"Medialist file not found") #create a medialist for the mediashow and read it. self.medialist=MediaList(self.show_params['sequence']) if self.medialist.open_list(self.media_file,self.showlist.sissue())==False: self.mon.err(self,"Version of medialist different to Pi Presents") self.end('error',"Version of medialist different to Pi Presents") #get controls for this show if top level controlsmanager=ControlsManager() if self.top==True: self.controls_list=controlsmanager.default_controls() # and merge in controls from profile self.controls_list=controlsmanager.merge_show_controls(self.controls_list,self.show_params['controls']) #set up the time of day triggers for the show if self.show_params['trigger']in('time','time-quiet'): error_text=self.tod.add_times(self.show_params['trigger-input'],id(self),self.tod_start_callback,self.show_params['trigger']) if error_text<>'': self.mon.err(self,error_text) self.end('error',error_text) if self.show_params['trigger-end']=='time': # print self.show_params['trigger-end-time'] error_text=self.tod.add_times(self.show_params['trigger-end-time'],id(self),self.tod_end_callback,'n/a') if error_text<>'': self.mon.err(self,error_text) self.end('error',error_text) if self.show_params['trigger-end']=='duration': error_text=self.calculate_duration(self.show_params['trigger-end-time']) if error_text<>'': self.mon.err(self,error_text) self.end('error',error_text) self.state='closed' self.egg_timer=None self.wait_for_trigger() # ******************************** # Respond to external events # ******************************** #stop received from another concurrent show def managed_stop(self): # if next lower show is running pass down to stop the show and lower level if self.shower<>None: self.shower.managed_stop() else: #stop the show if not at top self.end_mediashow_signal=True # and if track is runing stop that first if self.player<>None: self.player.input_pressed('stop') # kill or error def terminate(self,reason): if self.shower<>None: self.shower.terminate(reason) elif self.player<>None: self.player.terminate(reason) else: self.end(reason,' terminated with no shower or player to terminate') # respond to input events def input_pressed(self,symbol,edge,source): self.mon.log(self, self.show_params['show-ref']+ ' '+ str(self.show_id)+": received input: " + symbol) # check symbol against mediashow triggers, triggers can be used at top or lower level # and not affected by disable-controls if self.state=='waiting' and self.show_params['trigger'] in ('input','input-quiet')and symbol == self.show_params['trigger-input']: self.start_show() elif self.state=='playing' and self.show_params['trigger-next']=='input' and symbol == self.show_params['next-input']: self.next() # internal functions are triggered only when disable-controls is 'no' if self.show_params['disable-controls']=='yes': return # if at top convert symbolic name to operation otherwise lower down we have received an operation # look through list of standard symbols to find match (symbolic-name, function name) operation =lookup (symbol if self.top==True: operation=self.lookup_control(symbol,self.controls_list) else: operation=symbol # print 'operation',operation self.do_operation(operation,edge,source) #service the standard inputs for this show def do_operation(self,operation,edge,source): if self.shower<>None: # if next lower show is running pass down to stop the show and lower level self.shower.input_pressed(operation,edge,source) else: # control this show and its tracks # print 'operation',operation if operation=='stop': if self.top == False: # not at top so stop the current show self.end_mediashow_signal=True # and if a track is running stop that first if self.player<>None: self.player.input_pressed('stop') else: # top = True, just stop track if running if self.player<>None: self.player.input_pressed('stop') elif operation in ('up','down'): #if playing rather than waiting use keys for next or previous if operation=='up' and self.state=='playing': self.previous() else: self.next() elif operation=='play': # use 'play' to start child if state=playing or to trigger the show if waiting for trigger if self.state=='playing': if self.show_params['has-child']=='yes': self.play_child_signal=True self.child_track_ref='pp-child-show' # and stop the current track if its running if self.player<>None: self.player.input_pressed('stop') else: if self.state=='waiting': self.start_show() elif operation == 'pause': if self.player<>None: self.player.input_pressed(operation) #if the operation is omxplayer or mplayer runtime control then pass it to player if running elif operation[0:4]=='omx-' or operation[0:6]=='mplay-'or operation[0:5]=='uzbl-': if self.player<>None: self.player.input_pressed(operation) def lookup_control(self,symbol,controls_list): for control in controls_list: if symbol == control[0]: return control[1] return '' # *************************** # Show sequencer # *************************** def end_interval_timer(self): self.interval_timer_signal=True # callback from time of day scheduler def tod_start_callback(self): if self.state=='waiting' and self.show_params['trigger']in('time','time-quiet'): self.start_show() def tod_end_callback(self): if self.state=='playing' and self.show_params['trigger-end'] in ('time','duration'): self.end_trigger_signal=True if self.shower<>None: self.shower.input_pressed('stop') elif self.player<>None: self.player.input_pressed('stop') def stop(self,message): self.end_mediashow_signal=True if self.interval_timer<>None: self.canvas.after_cancel(self.interval_timer) def next(self): # stop track if running and set signal self.next_track_signal=True if self.shower<>None: self.shower.input_pressed("stop") else: if self.player<>None: self.player.input_pressed("stop") def previous(self): self.previous_track_signal=True if self.shower<>None: self.shower.input_pressed("stop") else: if self.player<>None: self.player.input_pressed("stop") # wait for trigger sets the state to waiting so that events can do a start show. def wait_for_trigger(self): self.state='waiting' if self.show_ready_callback<>None: self.show_ready_callback() self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Waiting for trigger: "+ self.show_params['trigger']) if self.show_params['trigger']=="input": # blank screen waiting for trigger if auto, otherwise display something if self.show_params['progress']=="manual": text= self.resource('mediashow','m01') else: text= self.resource('mediashow','m02') self.display_message(self.canvas,'text',text,0,self.start_show) elif self.show_params['trigger']=="input-quiet": # blank screen waiting for trigger text = self.resource('mediashow','m10') self.display_message(self.canvas,'text',text,0,self.start_show) pass elif self.show_params['trigger'] in ('time','time-quiet'): # show next show notice quiet=3 # if next show is this one display text next_show=self.tod.next_event_time() if next_show[quiet]==False: if next_show[1]=='tomorrow': text = self.resource('mediashow','m09') else: text = self.resource('mediashow','m08') text=text.replace('%tt',next_show[0]) self.display_message(self.canvas,'text',text,0,self.start_show) elif self.show_params['trigger']=="start": self.start_show() else: self.mon.err(self,"Unknown trigger: "+ self.show_params['trigger']) self.end('error',"Unknown trigger type") def start_show(self): self.state='playing' self.direction='forward' # self.canvas.delete(ALL) # start interval timer if self.show_params['repeat']=="interval" and self.show_params['repeat-interval']<>0: self.interval_timer_signal=False self.interval_timer=self.canvas.after(int(self.show_params['repeat-interval'])*1000,self.end_interval_timer) # start duration timer if self.show_params['trigger-end']=='duration': # print 'set alarm ', self.duration self.duration_timer = self.canvas.after(self.duration*1000,self.tod_end_callback) # and play the first track unless commanded otherwise if self.command=='backward': self.medialist.finish() else: self.medialist.start() self.play_selected_track(self.medialist.selected_track()) def what_next(self): self.direction='forward' # end of show trigger caused by tod if self.end_trigger_signal==True: self.end_trigger_signal=False if self.top==True: self.state='waiting' self.wait_for_trigger() else: # not at top so stop the show self.end('normal','sub-show end time trigger') # user wants to end, wait for any shows or tracks to have ended then end show # probalby will get here with end_m set when player and shower has finished elif self.end_mediashow_signal==True: if self.player==None and self.shower==None: self.end_mediashow_signal=False self.end('normal',"show ended by user") #returning from a subshow needing to move onward elif self.req_next=='do-next': self.req_next='nil' self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) #returning from a subshow needing to move backward elif self.req_next=='do-previous': self.req_next='nil' self.direction='backward' self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # user wants to play child elif self.play_child_signal == True: self.play_child_signal=False index = self.medialist.index_of_track(self.child_track_ref) if index >=0: #don't use select the track as need to preserve mediashow sequence. child_track=self.medialist.track(index) self.display_eggtimer(self.resource('mediashow','m07')) self.play_selected_track(child_track) else: self.mon.err(self,"Child show not found in medialist: "+ self.show_params['pp-child-show']) self.end('error',"child show not found in medialist") # skip to next track on user input elif self.next_track_signal==True: self.next_track_signal=False if self.medialist.at_end()==True: if self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==False: self.end('do-next',"Return from Sub Show") elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='single-run' and self.top==False: self.end('do-next',"Return from Sub Show") else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self.previous_track_signal==True: self.previous_track_signal=False self.direction='backward' if self.medialist.at_start()==True: if self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==False: self.end('do-previous',"Return from Sub Show") elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='single-run' and self.top==False: self.end('do-previous',"Return from Sub Show") else: self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.medialist.previous(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show_params['progress']=="auto": if self.medialist.at_end()==True: # oneshot if self.show_params['repeat']=='oneshot' and self.top==False: self.end('normal',"End of Oneshot in subshow") elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='oneshot' and self.top==True: self.wait_for_trigger() # single run elif self.show_params['repeat']=='single-run' and self.top==True: self.end('normal',"End of Single Run") elif self.show_params['repeat']=='single-run' and self.top==False: self.end('do-next',"End of single run - Return from Sub Show") # repeating and waiting to restart elif self.waiting_for_interval==True: if self.interval_timer_signal==True: self.interval_timer_signal=False self.waiting_for_interval=False self.start_show() else: self.poll_for_interval_timer=self.canvas.after(1000,self.what_next) elif self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])>0: self.waiting_for_interval=True self.poll_for_interval_timer=self.canvas.after(1000,self.what_next) #elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0: elif self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # shuffling so there is no end condition elif self.show_params['sequence']=="shuffle": self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) else: self.mon.err(self,"Unhandled playing event: "+self.show_params['sequence'] +' with ' + self.show_params['repeat']+" of "+ self.show_params['repeat-interval']) self.end('error',"Unhandled playing event") else: self.medialist.next(self.show_params['sequence']) self.play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show_params['progress']=="manual": self.delete_eggtimer() self.canvas.delete('pp-content') if self.show_params['trigger-next']=='input': self.display_eggtimer(self.resource('mediashow','m03')) self.poll_for_continue_timer=self.canvas.after(2000,self.what_next) else: #unhandled state self.mon.err(self,"Unhandled playing event: ") self.end('error',"Unhandled playing event") # *************************** # Dispatching to Players/Shows # *************************** def ready_callback(self): self.delete_eggtimer() def play_selected_track(self,selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ self.delete_eggtimer() if self.show_params['progress']=="manual": self.display_eggtimer(self.resource('mediashow','m04')) # is menu required if self.show_params['has-child']=="yes": self.enable_child=True else: self.enable_child=False #dispatch track by type self.player=None self.shower=None track_type = selected_track['type'] self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Track type is: "+ track_type) if track_type=="video": # create a videoplayer track_file=self.complete_path(selected_track) self.player=VideoPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type=="audio": # create a audioplayer track_file=self.complete_path(selected_track) self.player=AudioPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type=="web": # create a browser track_file=self.complete_path(selected_track) self.player=BrowserPlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type=="image": track_file=self.complete_path(selected_track) # images played from menus don't have children self.player=ImagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(track_file, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child) elif track_type=="message": # bit odd because MessagePlayer is used internally to display text. text=selected_track['text'] self.player=MessagePlayer(self.show_id,self.root,self.canvas,self.show_params,selected_track,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(text, self.showlist, self.end_player, self.ready_callback, enable_menu=self.enable_child ) elif track_type=="show": # get the show from the showlist index = self.showlist.index_of_show(selected_track['sub-show']) if index >=0: self.showlist.select(index) selected_show=self.showlist.selected_show() else: self.mon.err(self,"Show not found in showlist: "+ selected_track['sub-show']) self.end('error',"Unknown show") if selected_show['type']=="mediashow": self.shower= MediaShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) print "Starting MediaShow: {0}".format(selected_track['sub-show']) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command=self.direction) elif selected_show['type']=="liveshow": self.shower= LiveShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') elif selected_show['type']=="radiobuttonshow": self.shower= RadioButtonShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') elif selected_show['type']=="hyperlinkshow": self.shower= HyperlinkShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') elif selected_show['type']=="menu": self.shower= MenuShow(selected_show, self.root, self.canvas, self.showlist, self.pp_dir, self.pp_home, self.pp_profile) self.shower.play(self.show_id,self.end_shower,self.ready_callback,top=False,command='nil') else: self.mon.err(self,"Unknown Show Type: "+ selected_show['type']) self.end('error'"Unknown show type") else: self.mon.err(self,"Unknown Track Type: "+ track_type) self.end('error',"Unknown track type") def end_player(self,reason,message): self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Returned from player with message: "+ message) self.player=None self.req_next='nil' if reason in("killed","error"): self.end(reason,message) else: # elif>else move to what-next? if self.show_params['progress']=="manual": self.display_eggtimer(self.resource('mediashow','m05')) self.req_next=reason self.what_next() else: self.req_next=reason self.what_next() def end_shower(self,show_id,reason,message): self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Returned from shower with message: "+ message) self.shower=None self.req_next='nil' if reason in("killed","error"): self.end(reason,message) else: if self.show_params['progress']=="manual": self.display_eggtimer(self.resource('mediashow','m06')) self.req_next=reason self.what_next() else: self.req_next=reason self.what_next() # *************************** # end of show # *************************** def end(self,reason,message): self.end_mediashow_signal=False self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Ending Mediashow") self.tidy_up() self.end_callback(self.show_id,reason,message) self=None return def tidy_up(self): #clear outstanding time of day events for this show # self.tod.clear_times_list(id(self)) if self.poll_for_continue_timer<>None: self.canvas.after_cancel(self.poll_for_continue_timer) self.poll_for_continue_timer=None if self.poll_for_interval_timer<>None: self.canvas.after_cancel(self.poll_for_interval_timer) self.poll_for_interval_timer=None if self.interval_timer<>None: self.canvas.after_cancel(self.interval_timer) self.interval_timer=None if self.duration_timer<>None: self.canvas.after_cancel(self.duration_timer) self.duration_timer=None # *************************** # displaying things # *************************** def display_eggtimer(self,text): self.canvas.create_text(int(self.canvas['width'])/2, int(self.canvas['height'])/2, text= text, fill='white', font="Helvetica 20 bold", tag='pp-eggtimer') self.canvas.update_idletasks( ) def delete_eggtimer(self): self.canvas.delete('pp-eggtimer') self.canvas.update_idletasks( ) # used to display internal messages in situations where a medialist entry could not be used. def display_message(self,canvas,source,content,duration,_display_message_callback): self.display_message_callback=_display_message_callback tp={'duration':duration,'message-colour':'white','message-font':'Helvetica 20 bold','background-colour':'', 'message-justify':'left','background-image':'','show-control-begin':'','show-control-end':'', 'animate-begin':'','animate-clear':'','animate-end':'','message-x':'','message-y':'', 'display-show-background':'no','display-show-text':'no','show-text':'','track-text':'', 'plugin':''} self.player=MessagePlayer(self.show_id,self.root,canvas,tp,tp,self.pp_dir,self.pp_home,self.pp_profile) self.player.play(content,self.showlist,self.display_message_end,None,False) def display_message_end(self,reason,message): self.player=None if reason in ('error','killed'): self.end(reason,message) else: self.display_message_callback() # *************************** # utilities # *************************** def calculate_duration(self,line): fields=line.split(':') if len(fields)==1: secs=fields[0] minutes='0' hours='0' if len(fields)==2: secs=fields[1] minutes=fields[0] hours='0' if len(fields)==3: secs=fields[2] minutes=fields[1] hours=fields[0] self.duration=3600*long(hours)+60*long(minutes)+long(secs) return '' def resource(self,section,item): value=self.rr.get(section,item) if value==False: self.mon.err(self, "resource: "+section +': '+ item + " not found" ) self.terminate("error") else: return value def complete_path(self,selected_track): # complete path of the filename of the selected entry track_file = selected_track['location'] if track_file<>'' and track_file[0]=="+": track_file=self.pp_home+track_file[1:] self.mon.log(self,self.show_params['show-ref']+ ' '+ str(self.show_id)+ ": Track to play is: "+ track_file) return track_file
class RadioMediaShow(Show): def __init__(self, show_id, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile, command_callback): # init the common bits Show.base__init__(self, show_id, show_params, root, canvas, showlist, pp_dir, pp_home, pp_profile, command_callback) # instatiatate the screen driver - used only to access enable and hide click areas self.sr = ScreenDriver() # create an instance of PathManager - only used to parse the links. self.path = PathManager() self.allowed_links = ('play', 'pause', 'exit', 'return', 'null', 'no-command', 'stop') # init variables self.links = [] self.track_timeout_timer = None self.show_timeout_timer = None self.next_track_signal = False self.current_track_ref = '' self.req_next = '' self.controlsmanager = ControlsManager() # Init variables special to this show self.poll_for_interval_timer = None self.interval_timer_signal = False self.waiting_for_interval = False self.interval_timer = None self.duration_timer = None self.end_trigger_signal = False self.next_track_signal = False self.previous_track_signal = False self.play_child_signal = False self.error_signal = False self.show_timeout_signal = False self.req_next = 'nil' self.state = 'closed' self.count = 0 self.interval = 0 self.duration = 0 self.controls_list = [] self.enable_hint = True # def play(self,end_callback,show_ready_callback, direction_command,level,controls_list): # # # use the appropriate medialist # self.medialist=MediaList(self.show_params['sequence']) # # GapShow.play(self,end_callback,show_ready_callback, direction_command,level,controls_list) def play(self, end_callback, show_ready_callback, parent_kickback_signal, level, controls_list): # use the appropriate medialist self.medialist = MediaList(self.show_params['sequence']) self.mon.newline(3) self.mon.trace(self, self.show_params['show-ref']) Show.base_play(self, end_callback, show_ready_callback, parent_kickback_signal, level, controls_list) # unpack show parameters reason, message, self.show_timeout = Show.calculate_duration( self, self.show_params['show-timeout']) if reason == 'error': self.mon.err( self, 'ShowTimeout has bad time: ' + self.show_params['show-timeout']) self.end( 'error', 'ShowTimeout has bad time: ' + self.show_params['show-timeout']) self.track_count_limit = int(self.show_params['track-count-limit']) reason, message, self.interval = Show.calculate_duration( self, self.show_params['interval']) if reason == 'error': self.mon.err( self, 'Interval has bad time: ' + self.show_params['interval']) self.end('error', 'Interval has bad time: ' + self.show_params['interval']) # delete eggtimer started by the parent if self.previous_shower is not None: self.previous_shower.delete_eggtimer() self.start_show() # respond to inputs def handle_input_event(self, symbol): Show.base_handle_input_event(self, symbol) #self.handle_input_event_this_show(symbol) def handle_input_event_this_show(self, symbol): # for radiobuttonshow the symbolic names are links to play tracks, also a limited number of in-track operations # find the first entry in links that matches the symbol and execute its operation #print 'radiomediashow ', symbol found, link_op, link_arg = self.path.find_link(symbol, self.links) #print 'input event', symbol, link_op if found is True: if link_op == 'play': #print 'playing ' + link_arg self.do_play(link_arg) elif link_op == 'exit': #exit the show self.exit() elif link_op == 'stop': self.stop_timers() if self.current_player is not None: if self.current_track_ref == self.first_track_ref and self.level != 0: # if quiescent then set signal to stop the show when track has stopped self.user_stop_signal = True self.current_player.input_pressed('stop') elif link_op == 'return': # return to the first track if self.current_track_ref != self.first_track_ref: self.do_play(self.first_track_ref) # in-track operations elif link_op == 'pause': if self.current_player is not None: self.current_player.input_pressed(link_op) elif link_op in ('no-command', 'null'): return elif link_op[0:4] == 'omx-' or link_op[0:6] == 'mplay-' or link_op[ 0:5] == 'uzbl-': if self.current_player is not None: self.current_player.input_pressed(link_op) else: self.mon.err(self, "unknown link command: " + link_op) self.end('error', "unknown link command: " + link_op) def do_play(self, track_ref): #print 'do_play ' + track_ref # if track_ref != self.current_track_ref: # cancel the show timeout when playing another track if self.show_timeout_timer is not None: self.canvas.after_cancel(self.show_timeout_timer) self.show_timeout_timer = None # print '\n NEED NEXT TRACK' #self.next_track_signal=True self.next_track_op = 'play' self.next_track_arg = track_ref self.play_child_signal = True if self.shower is not None: #print 'current_shower not none so stopping',self.mon.id(self.current_shower) self.shower.do_operation('stop') elif self.current_player is not None: #print 'current_player not none so stopping',self.mon.id(self.current_player), ' for' ,track_ref self.current_player.input_pressed('stop') else: return # *************************** # Show sequencing # *************************** def start_show(self): # initial direction from parent show self.kickback_for_next_track = self.parent_kickback_signal # print '\n\ninital KICKBACK from parent', self.kickback_for_next_track # start duration timer if self.show_timeout != 0: # print 'set alarm ', self.show_timeout self.duration_timer = self.canvas.after(self.show_timeout * 1000, self.show_timeout_stop) self.first_list = True # and start the first list of the show self.wait_for_trigger() def wait_for_trigger(self): # wait for trigger sets the state to waiting so that trigger events can do a start_list. self.state = 'waiting' self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ": Waiting for trigger: " + self.show_params['trigger-start-type']) if self.show_params['trigger-start-type'] == "input": #close the previous track to display admin message Show.base_shuffle(self) Show.base_track_ready_callback(self, False) Show.display_admin_message(self, self.show_params['trigger-wait-text']) elif self.show_params['trigger-start-type'] == "input-persist": if self.first_list == True: #first time through track list so play the track without waiting to get to end. self.first_list = False self.start_list() else: #wait for trigger while displaying previous track pass elif self.show_params['trigger-start-type'] == "start": # don't close the previous track to give seamless repeat of the show self.start_list() else: self.mon.err( self, "Unknown trigger: " + self.show_params['trigger-start-type']) self.end( 'error', "Unknown trigger: " + self.show_params['trigger-start-type']) def start_list(self): # starts the list or any repeat having waited for trigger first. self.state = 'playing' # initialise track counter for the list self.track_count = 0 # start interval timer self.interval_timer_signal = False if self.interval != 0: self.interval_timer = self.canvas.after(self.interval * 1000, self.end_interval_timer) #get rid of previous track in order to display the empty message if self.medialist.display_length() == 0: Show.base_shuffle(self) Show.base_track_ready_callback(self, False) Show.display_admin_message(self, self.show_params['empty-text']) self.wait_for_not_empty() else: self.not_empty() def wait_for_not_empty(self): if self.medialist.display_length() == 0: # list is empty retry after 5 secs self.canvas.after(5000, self.wait_for_not_empty) else: Show.delete_admin_message(self) self.not_empty() def not_empty(self): #get first or last track depending on direction # print 'use direction for start or end of list', self.kickback_for_next_track if self.kickback_for_next_track is True: self.medialist.finish() else: self.medialist.start() self.start_load_show_loop(self.medialist.selected_track()) # *************************** # Track load/show loop # *************************** # track playing loop starts here def start_load_show_loop(self, selected_track): # uncomment the next line to write stats for every track # Show.write_stats(self,'play a track',self.show_params,selected_track) # shuffle players Show.base_shuffle(self) self.delete_eggtimer() # is child track required if self.show_params['child-track-ref'] != '': self.enable_child = True else: self.enable_child = False # read the show links. Track links will be added by ready_callback # needs to be done in show loop as each track adds different links to the show links if self.show_params['disable-controls'] == 'yes': self.links = [] else: reason, message, self.links = self.path.parse_links( self.show_params['links'], self.allowed_links) if reason == 'error': self.mon.err(self, message + " in show") self.end('error', message + " in show") # load the track or show # params - track,enable_menu enable = self.enable_child & self.enable_hint Show.base_load_track_or_show(self, selected_track, self.what_next_after_load, self.end_shower, enable) # track has loaded so show it. def what_next_after_load(self, status, message): self.mon.log( self, 'Show Id ' + str(self.show_id) + ' load complete with status: ' + status + ' message: ' + message) if self.current_player.play_state == 'load-failed': self.error_signal = True self.what_next_after_showing() else: if self.terminate_signal is True or self.exit_signal is True or self.user_stop_signal is True: self.what_next_after_showing() else: self.mon.trace(self, ' - showing track') self.current_player.show(self.track_ready_callback, self.finished_showing, self.closed_after_showing) def finished_showing(self, reason, message): self.sr.hide_click_areas(self.controls_list) if self.current_player.play_state == 'show-failed': self.error_signal = True else: self.req_next = 'finished-player' # showing has finished with 'pause at end', showing the next track will close it after next has started showing self.mon.trace(self, ' - pause at end ') self.mon.log( self, "pause at end of showing track with reason: " + reason + ' and message: ' + message) self.what_next_after_showing() def closed_after_showing(self, reason, message): self.sr.hide_click_areas(self.controls_list) if self.current_player.play_state == 'show-failed': self.error_signal = True else: self.req_next = 'closed-player' # showing has finished with closing of player but track instance is alive for hiding the x_content self.mon.trace(self, ' - closed') self.mon.log( self, "Closed after showing track with reason: " + reason + ' and message: ' + message) self.what_next_after_showing() # subshow or child show has ended def end_shower(self, show_id, reason, message): self.mon.log( self, self.show_params['show-ref'] + ' ' + str(self.show_id) + ': Returned from shower with ' + reason + ' ' + message) self.sr.hide_click_areas(self.controls_list) self.req_next = reason Show.base_end_shower(self) self.what_next_after_showing() def pretty_what_next_after_showing_state(self): state = '\n* terminate signal ' + str(self.terminate_signal) state += '\n* error signal ' + str(self.error_signal) state += '\n* req_next used to indicate subshow reports an error ' + self.req_next state += '\n* exit signal ' + str(self.exit_signal) state += '\n* show timeout signal ' + str(self.show_timeout_signal) state += '\n* user stop signal ' + str(self.user_stop_signal) state += '\n* previous track signal ' + str( self.previous_track_signal) state += '\n* next track signal ' + str(self.next_track_signal) state += '\n* kickback from subshow ' + str( self.subshow_kickback_signal) return state + '\n' def what_next_after_showing(self): self.mon.trace(self, self.pretty_what_next_after_showing_state()) self.track_count += 1 # set false when child rack is to be played self.enable_hint = True # first of all deal with conditions that do not require the next track to be shown # some of the conditions can happen at any time, others only when a track is closed or at pause_at_end # need to terminate if self.terminate_signal is True: self.terminate_signal = False self.stop_timers() # set what to do after closed or unloaded self.ending_reason = 'killed' Show.base_close_or_unload(self) elif self.error_signal == True or self.req_next == 'error': self.error_signal = False self.req_next = '' self.stop_timers() # set what to do after closed or unloaded self.ending_reason = 'error' Show.base_close_or_unload(self) # used for exiting show from other shows, time of day, external etc. elif self.exit_signal is True: self.exit_signal = False self.stop_timers() self.ending_reason = 'exit' Show.base_close_or_unload(self) # show timeout elif self.show_timeout_signal is True: self.show_timeout_signal = False self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) # user wants to stop the show elif self.user_stop_signal is True: self.user_stop_signal = False self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) # interval > 0. If last track has finished we are waiting for interval timer before ending the list # note: if medialist finishes after interval is up then this route is used to start trigger. elif self.waiting_for_interval is True: # interval_timer_signal set by alarm clock started in start_list if self.interval_timer_signal is True: self.interval_timer_signal = False self.waiting_for_interval = False # print 'RECEIVED INTERVAL TIMER SIGNAL' if self.show_params['repeat'] == 'repeat': self.wait_for_trigger() else: self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) else: self.poll_for_interval_timer = self.canvas.after( 1000, self.what_next_after_showing) # has content of list been changed (replaced if it has, used for content of livelist) # causes it to go to wait_for_trigger and start_list #not sure why need clos_and_unload here ??????? elif self.medialist.replace_if_changed() is True: self.ending_reason = 'change-medialist' # print 'CHANGE REQ' Show.base_close_or_unload(self) # otherwise consider operation that might show the next track else: # setup default direction for next track as normally goes forward unless kicked back self.kickback_for_next_track = False # print 'init direction to False at begin of what_next_after showing', self.kickback_for_next_track # end trigger from input, or track count if self.end_trigger_signal is True or (self.track_count_limit > 0 and self.track_count == self.track_count_limit): self.end_trigger_signal = False # repeat so test start trigger if self.show_params['repeat'] == 'repeat': self.stop_timers() # print 'END TRIGGER restart' self.wait_for_trigger() else: # single run so exit show if self.level != 0: self.kickback_for_next_track = False self.subshow_kickback_signal = False self.end('normal', "End of single run - Return from Sub Show") else: # end of single run and at top - exit the show self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) # user wants to play child elif self.play_child_signal is True: self.play_child_signal = False # index = self.medialist.index_of_track(self.child_track_ref) # if index >=0: # # don't use select the track as need to preserve mediashow sequence for returning from child # child_track=self.medialist.track(index) # Show.write_stats(self,'play child',self.show_params,child_track) # self.display_eggtimer() # self.enable_hint=False # self.start_load_show_loop(child_track) # else: # self.mon.err(self,"Child not found in medialist: "+ self.child_track_ref) # self.ending_reason='error' # Show.base_close_or_unload(self) # play user selected track # HOOK ! self.current_track_ref = self.next_track_arg #print 'what next - next track signal is True so load ', self.current_track_ref index = self.medialist.index_of_track(self.current_track_ref) if index >= 0: # don't use select the track as not using selected_track in radiobuttonshow # and load it Show.write_stats(self, 'play', self.show_params, self.medialist.track(index)) self.display_eggtimer() self.enable_hint = False self.start_load_show_loop(self.medialist.track(index)) else: self.mon.err( self, "next track not found in medialist: " + self.current_track_ref) self.end( 'error', "next track not found in medialist: " + self.current_track_ref) Show.base_close_or_unload(self) # skip to next track on user input or after subshow elif self.next_track_signal is True: #print 'skip forward test' ,self.subshow_kickback_signal if self.next_track_signal is True or self.subshow_kickback_signal is False: self.next_track_signal = False self.kickback_for_next_track = False if self.medialist.at_end() is True: # medialist_at_end can give false positive for shuffle if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'repeat': self.wait_for_trigger() elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run': if self.level != 0: self.kickback_for_next_track = False self.subshow_kickback_signal = False # print 'end subshow skip forward test, self. direction is ' ,self.kickback_for_next_track self.end('normal', "Return from Sub Show") else: # end of single run and at top - exit the show self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) else: # shuffling - just do next track self.kickback_for_next_track = False self.medialist.next(self.show_params['sequence']) self.start_load_show_loop( self.medialist.selected_track()) else: # not at end just do next track self.medialist.next(self.show_params['sequence']) self.start_load_show_loop( self.medialist.selected_track()) # skip to previous track on user input or after subshow elif self.previous_track_signal is True or self.subshow_kickback_signal is True: #print 'skip backward test, subshow kickback is' ,self.subshow_kickback_signal self.subshow_kickback_signal = False self.previous_track_signal = False self.kickback_for_next_track = True # medialist_at_start can give false positive for shuffle if self.medialist.at_start() is True: # print 'AT START' if self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'repeat': self.kickback_for_next_track = True self.wait_for_trigger() elif self.show_params[ 'sequence'] == "ordered" and self.show_params[ 'repeat'] == 'single-run': if self.level != 0: self.kickback_for_next_track = True self.subshow_kickback_signal = True # print 'end subshow skip forward test, self. direction is ' ,self.kickback_for_next_track self.end('normal', "Return from Sub Show") else: # end of single run and at top - exit the show self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) else: # shuffling - just do previous track self.kickback_for_next_track = True self.medialist.previous(self.show_params['sequence']) self.start_load_show_loop( self.medialist.selected_track()) else: # not at end just do next track self.medialist.previous(self.show_params['sequence']) self.start_load_show_loop(self.medialist.selected_track()) # AT END OF MEDIALIST elif self.medialist.at_end() is True: #print 'MEDIALIST AT END' # interval>0 and list finished so wait for the interval timer if self.show_params[ 'sequence'] == "ordered" and self.interval > 0 and self.interval_timer_signal == False: self.waiting_for_interval = True # print 'WAITING FOR INTERVAL' Show.base_shuffle(self) Show.base_track_ready_callback(self, False) self.poll_for_interval_timer = self.canvas.after( 200, self.what_next_after_showing) # interval=0 #elif self.show_params['sequence'] == "ordered" and self.show_params['repeat'] == 'repeat' and self.show_params['trigger-end-type']== 'interval' and int(self.show_params['trigger-end-param']) == 0: #self.medialist.next(self.show_params['sequence']) # self.start_load_show_loop(self.medialist.selected_track()) # shuffling so there is no end condition, get out of end test elif self.show_params['sequence'] == "shuffle": self.medialist.next(self.show_params['sequence']) self.start_load_show_loop(self.medialist.selected_track()) # nothing special to do at end of list, just repeat or exit elif self.show_params['sequence'] == "ordered": if self.show_params['repeat'] == 'repeat': self.wait_for_trigger() else: # single run # if not at top return to parent if self.level != 0: self.end('normal', "End of Single Run") else: # at top so close the show self.stop_timers() self.ending_reason = 'user-stop' Show.base_close_or_unload(self) else: self.mon.err( self, "Unhandled playing event at end of list: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['trigger-end-param']) self.end( 'error', "Unhandled playing event at end of list: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['trigger-end-param']) elif self.medialist.at_end() is False: # nothing special just do the next track self.medialist.next(self.show_params['sequence']) self.start_load_show_loop(self.medialist.selected_track()) else: # unhandled state self.mon.err( self, "Unhandled playing event: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['trigger-end-param']) self.end( 'error', "Unhandled playing event: " + self.show_params['sequence'] + ' with ' + self.show_params['repeat'] + " of " + self.show_params['trigger-end-param']) # ********************* # Interface with other shows/players to reduce black gaps # ********************* # # called just before a track is shown to remove the previous track from the screen # # and if necessary close it # def track_ready_callback(self,enable_show_background): # self.delete_eggtimer() # # # get control bindings for this show # # needs to be done for each track as track can override the show controls # if self.show_params['disable-controls'] == 'yes': # self.controls_list=[] # else: # reason,message,self.controls_list= self.controlsmanager.get_controls(self.show_params['controls']) # if reason=='error': # self.mon.err(self,message) # self.end('error',"error in controls: " + message) # return # # # print 'controls',reason,self.show_params['controls'],self.controls_list # #merge controls from the track # controls_text=self.current_player.get_links() # reason,message,track_controls=self.controlsmanager.parse_controls(controls_text) # if reason == 'error': # self.mon.err(self,message + " in track: "+ self.current_player.track_params['track-ref']) # self.error_signal=True # self.what_next_after_showing() # self.controlsmanager.merge_controls(self.controls_list,track_controls) # # # enable the click-area that are in the list of controls # self.sr.enable_click_areas(self.controls_list) # Show.base_track_ready_callback(self,enable_show_background) # called just before a track is shown to remove the previous track from the screen # and if necessary close it def track_ready_callback(self, enable_show_background): self.delete_eggtimer() # print 'TRACK READY CALLBACK' # print 'previous is',self.mon.id(self.previous_player), self.next_track_signal #merge links from the track if self.show_params['disable-controls'] == 'yes': track_links = [] else: reason, message, track_links = self.path.parse_links( self.current_player.get_links(), self.allowed_links) if reason == 'error': self.mon.err( self, message + " in track: " + self.current_player.track_params['track-ref']) self.req_next = 'error' self.what_next_after_showing() self.path.merge_links(self.links, track_links) # enable the click-area that are in the list of links self.sr.enable_click_areas(self.links) Show.base_track_ready_callback(self, enable_show_background) # callback from begining of a subshow, provide previous player to called show def subshow_ready_callback(self): return Show.base_subshow_ready_callback(self) # ********************* # End the show # ********************* def end(self, reason, message): Show.base_end(self, reason, message) def stop_timers(self): # clear outstanding time of day events for this show # self.tod.clear_times_list(id(self)) if self.poll_for_interval_timer is not None: self.canvas.after_cancel(self.poll_for_interval_timer) self.poll_for_interval_timer = None if self.interval_timer is not None: self.canvas.after_cancel(self.interval_timer) self.interval_timer = None if self.duration_timer is not None: self.canvas.after_cancel(self.duration_timer) self.duration_timer = None
class MediaShow: # ******************* # External interface # ******************** def __init__(self, show_params, canvas, showlist, pp_home, pp_profile): """ canvas - the canvas that the menu is to be written on show - the dictionary fo the show to be played pp_home - Pi presents data_home directory pp_profile - Pi presents profile directory """ self.mon = Monitor() self.mon.on() # instantiate arguments self.show_params = show_params self.showlist = showlist self.canvas = canvas self.pp_home = pp_home self.pp_profile = pp_profile # open resources self.rr = ResourceReader() # Init variables self.player = None self.shower = None self._poll_for_interval_timer = None self._poll_for_continue_timer = None self._waiting_for_interval = False self._interval_timer = None self.duration_timer = None self.error = False self._interval_timer_signal = False self._end_trigger_signal = False self._end_mediashow_signal = False self._next_track_signal = False self._previous_track_signal = False self._play_child_signal = False self._req_next = "nil" # create and instance of TimeOfDay scheduler so we can add events self.tod = TimeOfDay() self._state = "closed" def play(self, show_id, end_callback, ready_callback=None, top=False, command="nil"): """ displays the mediashow end_callback - function to be called when the menu exits ready_callback - callback when menu is ready to display (not used) top is True when the show is top level (run from [start]) """ # instantiate the arguments self.show_id = show_id self._end_callback = end_callback self._ready_callback = ready_callback self.top = top self.command = command self.mon.log(self, "Starting show: Id= " + str(self.show_id) + " " + self.show_params["show-ref"]) # check data files are available. self.media_file = self.pp_profile + "/" + self.show_params["medialist"] if not os.path.exists(self.media_file): self.mon.err(self, "Medialist file not found: " + self.media_file) self._end("error", "Medialist file not found") # create a medialist for the mediashow and read it. self.medialist = MediaList() if self.medialist.open_list(self.media_file, self.showlist.sissue()) == False: self.mon.err(self, "Version of medialist different to Pi Presents") self._end("error", "Version of medialist different to Pi Presents") # set up the time of day triggers for the show if self.show_params["trigger"] in ("time", "time-quiet"): error_text = self.tod.add_times( self.show_params["trigger-input"], id(self), self.tod_start_callback, self.show_params["trigger"] ) if error_text <> "": self.mon.err(self, error_text) self._end("error", error_text) if self.show_params["trigger-end"] == "time": # print self.show_params['trigger-end-time'] error_text = self.tod.add_times( self.show_params["trigger-end-time"], id(self), self.tod_end_callback, "n/a" ) if error_text <> "": self.mon.err(self, error_text) self._end("error", error_text) if self.show_params["trigger-end"] == "duration": error_text = self.calculate_duration(self.show_params["trigger-end-time"]) if error_text <> "": self.mon.err(self, error_text) self._end("error", error_text) self._state = "closed" self.egg_timer = None self._wait_for_trigger() def calculate_duration(self, line): fields = line.split(":") if len(fields) == 1: secs = fields[0] minutes = "0" hours = "0" if len(fields) == 2: secs = fields[1] minutes = fields[0] hours = "0" if len(fields) == 3: secs = fields[2] minutes = fields[1] hours = fields[0] self.duration = 3600 * long(hours) + 60 * long(minutes) + long(secs) return "" # ******************************** # Respond to external events # ******************************** # respond to key presses def key_pressed(self, key_name): self.mon.log(self, "received key: " + key_name) if self.show_params["disable-controls"] == "yes": return if key_name == "": pass elif key_name == "escape": # if next lower show is running pass down to stop the show and lower level if self.shower <> None: self.shower.key_pressed(key_name) # if not at top stop the show else: if self.top == False: self._end_mediashow_signal = True # and if a track is running stop that first if self.player <> None: self.player.key_pressed(key_name) else: # at top level in a manual presentation stop the track if self.show_params["progress"] == "manual": if self.player <> None: self.player.key_pressed(key_name) elif key_name in ("up", "down"): # if child or sub-show is running and is a show pass to show, track does not use up/down # otherwise use keys for next or previous if self.shower <> None: self.shower.key_pressed(key_name) else: if key_name == "up" and self._state == "playing": self._previous() else: self._next() elif key_name == "return": # if child show or sub-show is running and is show - pass down- player does not use return # ELSE use Return to start child or to start the show if waiting if self.shower <> None: self.shower.key_pressed(key_name) else: if self._state == "playing": if self.show_params["has-child"] == "yes": self._play_child_signal = True # and stop the current track if its running if self.player <> None: self.player.key_pressed("escape") else: if self._state == "waiting": self._start_show() elif key_name in ("p", " "): # pass down if show or track running. if self.shower <> None: self.shower.key_pressed(key_name) elif self.player <> None: self.player.key_pressed(key_name) def button_pressed(self, button, edge): # print 'mediashow button pressed', button if button == "play": self.key_pressed("return") elif button == "up": self.key_pressed("up") elif button == "down": self.key_pressed("down") elif button == "stop": self.key_pressed("escape") elif button == "pause": self.key_pressed("p") else: self.input_pressed(button) def input_pressed(self, xinput): # print self._state, self.show_params['trigger-next'], self.show_params['next-input'] if ( self._state == "waiting" and self.show_params["trigger"] == "GPIO" and xinput == self.show_params["trigger-input"] ): self.key_pressed("return") elif ( self._state == "playing" and self.show_params["trigger-next"] == "GPIO" and xinput == self.show_params["next-input"] ): self.key_pressed("down") # callback from time of day scheduler def tod_start_callback(self): if self._state == "waiting" and self.show_params["trigger"] in ("time", "time-quiet"): self._start_show() def tod_end_callback(self): if self._state == "playing" and self.show_params["trigger-end"] in ("time", "duration"): self._end_trigger_signal = True if self.shower <> None: self.shower.key_pressed("escape") elif self.player <> None: self.player.key_pressed("escape") # kill or error def terminate(self, reason): if self.shower <> None: self.mon.log(self, "sent terminate to shower") self.shower.terminate(reason) elif self.player <> None: self.mon.log(self, "sent terminate to player") self.player.terminate(reason) else: self._end(reason, "terminated without terminating shower or player") def _tidy_up(self): # clear outstanding time of day events for this show self.tod.clear_times_list(id(self)) if self._poll_for_continue_timer <> None: self.canvas.after_cancel(self._poll_for_continue_timer) self._poll_for_continue_timer = None if self._poll_for_interval_timer <> None: self.canvas.after_cancel(self._poll_for_interval_timer) self._poll_for_interval_timer = None if self._interval_timer <> None: self.canvas.after_cancel(self._interval_timer) self._interval_timer = None if self.duration_timer <> None: self.canvas.after_cancel(self.duration_timer) self.duration_timer = None def resource(self, section, item): value = self.rr.get(section, item) if value == False: self.mon.err(self, "resource: " + section + ": " + item + " not found") self.terminate("error") else: return value # *************************** # Do actions as a result of events # *************************** def _stop(self, message): self._end_mediashow_signal = True if self._interval_timer <> None: self.canvas.after_cancel(self._interval_timer) def _next(self): # stop track if running and set signal self._next_track_signal = True if self.shower <> None: self.shower.key_pressed("escape") else: if self.player <> None: self.player.key_pressed("escape") def _previous(self): self._previous_track_signal = True if self.shower <> None: self.shower.key_pressed("escape") else: if self.player <> None: self.player.key_pressed("escape") # *************************** # end of show functions # *************************** def _end(self, reason, message): self._end_mediashow_signal = False self.mon.log(self, "Ending Mediashow: " + self.show_params["show-ref"]) self._tidy_up() self._end_callback(self.show_id, reason, message) self = None return # *************************** # Show sequencer # *************************** # wait for trigger sets the state to waiting so that key/button presses can do a start show. def _wait_for_trigger(self): self._state = "waiting" if self.ready_callback <> None: self.ready_callback() self.mon.log(self, "Waiting for trigger: " + self.show_params["trigger"]) if self.show_params["trigger"] == "button": # blank screen waiting for trigger if auto, otherwise display something if self.show_params["progress"] == "manual": text = self.resource("mediashow", "m01") else: text = "" self.display_message(self.canvas, "text", text, 0, self._start_show) elif self.show_params["trigger"] == "GPIO": # blank screen waiting for trigger # text = self.resource('mediashow','m02') # self.display_message(self.canvas,'text',text,0,self._start_show) pass elif self.show_params["trigger"] in ("time", "time-quiet"): # show next show notice quiet = 3 # if next show is this one display text next_show = self.tod.next_event_time() if next_show[quiet] == False: if next_show[1] == "tomorrow": text = self.resource("mediashow", "m09") else: text = self.resource("mediashow", "m08") text = text.replace("%tt", next_show[0]) self.display_message(self.canvas, "text", text, 0, self._start_show) elif self.show_params["trigger"] == "start": self._start_show() else: self.mon.err(self, "Unknown trigger: " + self.show_params["trigger"]) self._end("error", "Unknown trigger type") def _start_show(self): self._state = "playing" self._direction = "forward" # self.canvas.delete(ALL) # start interval timer if self.show_params["repeat"] == "interval" and self.show_params["repeat-interval"] <> 0: self._interval_timer_signal = False self._interval_timer = self.canvas.after( int(self.show_params["repeat-interval"]) * 1000, self._end_interval_timer ) # start duration timer if self.show_params["trigger-end"] == "duration": # print 'set alarm ', self.duration self.duration_timer = self.canvas.after(self.duration * 1000, self.tod_end_callback) # and play the first track unless commanded otherwise if self.command == "backward": self.medialist.finish() else: self.medialist.start() self._play_selected_track(self.medialist.selected_track()) def _what_next(self): self._direction = "forward" # end of show trigger if self._end_trigger_signal == True: self._end_trigger_signal = False if self.top == True: self._state = "waiting" self._wait_for_trigger() else: # not at top so stop the show self._end("normal", "sub-show end time trigger") # user wants to end, wait for any shows or tracks to have ended then end show elif self._end_mediashow_signal == True: if self.player == None and self.shower == None: self._end_mediashow_signal = False self._end("normal", "show ended by user") else: pass # returning from a subshow needing to move onward elif self._req_next == "do-next": self._req_next = "nil" self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # returning from a subshow needing to move backward elif self._req_next == "do-previous": self._req_next = "nil" self._direction = "backward" self.medialist.previous(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # user wants to play child elif self._play_child_signal == True: self._play_child_signal = False index = self.medialist.index_of_track("pp-child-show") if index >= 0: # don't select the track as need to preserve mediashow sequence. child_track = self.medialist.track(index) self._display_eggtimer(self.resource("mediashow", "m07")) self._play_selected_track(child_track) else: self.mon.err(self, "Child show not found in medialist: " + self.show_params["pp-child-show"]) self._end("error", "child show not found in medialist") # skip to next track on user input elif self._next_track_signal == True: self._next_track_signal = False if self.medialist.at_end() == True: if ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "oneshot" and self.top == False ): self._end("do-next", "Return from Sub Show") else: self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) else: self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # skip to previous track on user input elif self._previous_track_signal == True: self._previous_track_signal = False self._direction = "backward" if self.medialist.at_start() == True: if ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "oneshot" and self.top == False ): self._end("do-previous", "Return from Sub Show") else: self.medialist.previous(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) else: self.medialist.previous(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # track is finished and we are on auto elif self.show_params["progress"] == "auto": if self.medialist.at_end() == True: if ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "oneshot" and self.top == False ): self._end("do-next", "Return from Sub Show") elif ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "oneshot" and self.top == True ): self._wait_for_trigger() elif self._waiting_for_interval == True: if self._interval_timer_signal == True: self._interval_timer_signal = False self._waiting_for_interval = False self._start_show() else: self._poll_for_interval_timer = self.canvas.after(1000, self._what_next) elif ( self.show_params["sequence"] == "ordered" and self.show_params["repeat"] == "interval" and int(self.show_params["repeat-interval"]) > 0 ): self._waiting_for_interval = True self._poll_for_interval_timer = self.canvas.after(1000, self._what_next) # elif self.show_params['sequence']=="ordered" and self.show_params['repeat']=='interval' and int(self.show_params['repeat-interval'])==0: elif self.show_params["repeat"] == "interval" and int(self.show_params["repeat-interval"]) == 0: self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # shffling so there is no end condition elif self.show_params["sequence"] == "shuffle": self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) else: self.mon.err( self, "Unhandled playing event: " + self.show_params["sequence"] + " with " + self.show_params["repeat"] + " of " + self.show_params["repeat-interval"], ) self._end("error", "Unhandled playing event") else: self.medialist.next(self.show_params["sequence"]) self._play_selected_track(self.medialist.selected_track()) # track has finished and we are on manual progress elif self.show_params["progress"] == "manual": self._delete_eggtimer() self.canvas.delete(ALL) if self.show_params["trigger-next"] == "button": self._display_eggtimer(self.resource("mediashow", "m03")) self._poll_for_continue_timer = self.canvas.after(2000, self._what_next) else: # unhandled state self.mon.err(self, "Unhandled playing event: ") self._end("error", "Unhandled playing event") def _end_interval_timer(self): self._interval_timer_signal = True # *************************** # Dispatching to Players/Shows # *************************** # used to display internal messages in situations where a medialist entry could be used. def display_message(self, canvas, source, content, duration, _display_message_callback): self._display_message_callback = _display_message_callback tp = { "duration": duration, "message-colour": "white", "message-font": "Helvetica 20 bold", "background-colour": "", "background-image": "", } self.player = MessagePlayer(self.show_id, canvas, self.pp_home, tp, tp) self.player.play(content, self._display_message_end, None) def _display_message_end(self, reason, message): self.player = None if reason in ("error", "killed"): self._end(reason, message) else: self._display_message_callback() def complete_path(self, selected_track): # complete path of the filename of the selected entry track_file = selected_track["location"] if track_file <> "" and track_file[0] == "+": track_file = self.pp_home + track_file[1:] self.mon.log(self, "Track to play is: " + track_file) return track_file def _play_selected_track(self, selected_track): """ selects the appropriate player from type field of the medialist and computes the parameters for that type selected track is a dictionary for the track/show """ # self.canvas.delete(ALL) if self.show_params["progress"] == "manual": self._display_eggtimer(self.resource("mediashow", "m04")) # is menu required if self.show_params["has-child"] == "yes": enable_child = True else: enable_child = False # dispatch track by type self.player = None self.shower = None track_type = selected_track["type"] self.mon.log(self, "Track type is: " + track_type) if track_type == "video": # create a videoplayer track_file = self.complete_path(selected_track) self.player = VideoPlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "audio": # create a audioplayer track_file = self.complete_path(selected_track) self.player = AudioPlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "image": track_file = self.complete_path(selected_track) # images played from menus don't have children self.player = ImagePlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track) self.player.play(track_file, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "message": # bit odd because MessagePlayer is used internally to display text. text = selected_track["text"] self.player = MessagePlayer(self.show_id, self.canvas, self.pp_home, self.show_params, selected_track) self.player.play(text, self.end_player, self.ready_callback, enable_menu=enable_child) elif track_type == "show": # get the show from the showlist index = self.showlist.index_of_show(selected_track["sub-show"]) if index >= 0: self.showlist.select(index) selected_show = self.showlist.selected_show() else: self.mon.err(self, "Show not found in showlist: " + selected_track["sub-show"]) self._end("error", "Unknown show") if selected_show["type"] == "mediashow": self.shower = MediaShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, top=False, command=self._direction) elif selected_show["type"] == "liveshow": self.shower = LiveShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, top=False, command="nil") elif selected_show["type"] == "menu": self.shower = MenuShow(selected_show, self.canvas, self.showlist, self.pp_home, self.pp_profile) self.shower.play(self.show_id, self.end_shower, top=False, command="nil") else: self.mon.err(self, "Unknown Show Type: " + selected_show["type"]) self._end("error" "Unknown show type") else: self.mon.err(self, "Unknown Track Type: " + track_type) self._end("error", "Unknown track type") def ready_callback(self): self._delete_eggtimer() def end_player(self, reason, message): self._req_next = "nil" self.mon.log(self, " Show Id: " + str(self.show_id) + " Returned from player with message: " + message) self.player = None if reason in ("killed", "error"): self._end(reason, message) elif self.show_params["progress"] == "manual": self._display_eggtimer(self.resource("mediashow", "m05")) self._req_next = reason self._what_next() else: self._req_next = reason self._what_next() def end_shower(self, show_id, reason, message): self._req_next = "nil" self.mon.log(self, "Returned from shower with message: " + message) self.shower = None if reason in ("killed", "error"): self._end(reason, message) elif self.show_params["progress"] == "manual": self._display_eggtimer(self.resource("mediashow", "m06")) self._req_next = reason self._what_next() else: self._req_next = reason self._what_next() def _display_eggtimer(self, text): self.egg_timer = self.canvas.create_text( int(self.canvas["width"]) / 2, int(self.canvas["height"]) / 2, text=text, fill="white", font="Helvetica 20 bold", ) self.canvas.update_idletasks() def _delete_eggtimer(self): if self.egg_timer != None: self.canvas.delete(self.egg_timer) self.canvas.update_idletasks()