class MenuScreen(Screen): """ Site Menu Screen. Base class for all book menu screens """ def __init__(self, util, listeners, rows, columns, voice_assistant, d=None, turn_page=None, page_in_title=True, show_loading=False): """ Initializer :param util: utility object :param listeners: file browser listeners :param rows: menu rows :param d: dictionary with menu button flags :param turn_page: turn page callback :param util: utility object """ self.util = util self.config = util.config self.factory = Factory(util) self.bounding_box = util.screen_rect self.player = None self.turn_page = turn_page self.page_in_title = page_in_title self.show_loading = show_loading self.cache = Cache(self.util) self.layout = BorderLayout(self.bounding_box) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) Screen.__init__(self, util, "", PERCENT_TOP_HEIGHT, voice_assistant, "menu_screen_screen_title", True, self.layout.TOP) self.menu_layout = self.layout.CENTER if d: self.menu_button_layout = self.get_menu_button_layout(d) self.img_rect = self.menu_button_layout.image_rectangle listeners[GO_LEFT_PAGE] = self.previous_page listeners[GO_RIGHT_PAGE] = self.next_page self.total_pages = 0 self.current_page = 1 self.menu = None def get_menu_button_layout(self, d): """ Return menu button layout :param d: dictionary with menu button flags :return: menu button layout """ s = State() s.show_img = True s.show_label = True button_w = int(self.menu_layout.w / d[1]) button_h = int(self.menu_layout.h / d[0]) label_padding = 2 image_padding = 4 try: self.show_author = d[2] except: self.show_author = False try: self.show_genre = d[3] except: self.show_genre = False s.bounding_box = Rect(0, 0, button_w, button_h) return MultiLineButtonLayout(s, label_padding, image_padding) def previous_page(self, state): """ Handle click on left button :param state: button state object """ if self.current_page == 1: return self.current_page -= 1 if getattr(state, "select_last", False): self.components[1].selected_index = 0 self.menu.current_page = self.current_page self.menu.selected_index = 0 if self.show_loading: self.set_loading(self.screen_title.text) self.turn_page() if self.show_loading: self.reset_loading() def next_page(self, state): """ Handle click on right button :param state: button state object """ if self.total_pages <= 1 or self.total_pages == self.current_page: return self.current_page += 1 if self.current_page > self.total_pages: self.current_page = self.total_pages self.menu.current_page = self.current_page self.menu.selected_index = 0 if self.show_loading: self.set_loading(self.screen_title.text) self.turn_page() if self.show_loading: self.reset_loading() def set_menu(self, menu): """ Set menu :param menu: menu object """ self.menu = menu self.add_component(self.menu) def set_title(self, page_num): """ Set screen title :param page_num: menu page number """ if self.total_pages == 1 or not self.page_in_title: self.screen_title.set_text(self.title) return if len(str(page_num)) <= len(str(self.total_pages)): self.screen_title.set_text(self.title + " (" + str(page_num) + ")") def reset_title(self): """ Reset screen title """ self.screen_title.set_text(self.title + " (" + str(self.current_page) + ")") def go_to_page(self, page_num): """ Handle go to page event :param page_num: menu page number """ n = int(page_num) if n > self.total_pages: n = self.total_pages if n == 0: n = 1 self.current_page = n self.menu.current_page = self.current_page self.turn_page() def get_image_from_cache(self, url): """ Return image from cache :param url: image url :return: image """ return self.cache.get_image(url) def put_image_to_cache(self, url, img): """ Put image into cache :param url: image url :param img: image """ self.cache.cache_image(img, url) def set_button_image(self, b, icon, img_y=None): """ Set button image :param b: button :param icon: image :param img_y: image Y coordinate """ bb = b.bounding_box comps = b.components im = comps[1] im.content = icon w = im.content.get_size()[0] h = im.content.get_size()[1] im.content_x = bb.x + (bb.width - w) / 2 if img_y == None: img_area_height = bb.height - ((bb.height / LINES) * 2) img_y = bb.y + (img_area_height - h) / 2 im.content_y = img_y self.components[1].clean_draw_update() def set_loading(self, name, text=None): """ Show Loading... sign :param name: screen title :param text: screen text """ Screen.set_loading(self, name, self.menu_layout, text)
class BookPlayer(FilePlayerScreen): """ Book Player Screen """ def __init__(self, listeners, util, site_parser, voice_assistant): """ Initializer :param listeners: screen listeners :param util: utility object :param site_parser: site parser """ FilePlayerScreen.__init__(self, listeners, util, self.get_playlist, voice_assistant) self.config = util.config self.image_util = util.image_util self.current_playlist = None self.parser = site_parser self.cache = Cache(self.util) self.current_book_state = None self.current_track_index = 0 self.playlist = self.get_playlist() self.audio_files = self.get_audio_files_from_playlist() self.loading_listeners = [] self.reset_loading_listeners = [] self.LOADING = util.config[LABELS][KEY_LOADING] def get_playlist(self): """ Return current playlist :return: playlist """ return self.current_playlist def set_book(self, new_track, state=None): """ Set book :param new_track: flag defining if track is new :param state: button state object """ self.set_loading() self.current_book_state = state img_url = None if hasattr(state, "event_origin"): img_url = state.event_origin.state.icon_base[0] elif hasattr(state, "img_url"): img_url = state.img_url self.current_playlist = self.parser.get_book_audio_files_by_url( state.book_url, img_url) state.url = self.parser.book_parser.img_url img = self.cache.get_image(state.url) if img == None: i = self.image_util.load_image_from_url(state.url) if i: self.cache.cache_image(i[1], state.url) img = i[1] bb = self.file_button.bounding_box w = bb.w h = bb.h self.cover_image = self.image_util.scale_image_with_padding(w, h, img, padding=1) self.screen_title.set_text(state.name) if self.current_playlist == None: return self.playlist = self.current_playlist if new_track and self.playlist: self.config[AUDIOBOOKS][BROWSER_BOOK_TITLE] = state.name self.config[AUDIOBOOKS][BROWSER_BOOK_URL] = state.book_url self.config[AUDIOBOOKS][BROWSER_IMAGE_URL] = img_url self.config[AUDIOBOOKS][BROWSER_TRACK_FILENAME] = self.playlist[0][ "title"] state.track_time = self.config[AUDIOBOOKS][BROWSER_BOOK_TIME] = "0" elif not new_track: state.track_time = self.config[AUDIOBOOKS][BROWSER_BOOK_TIME] self.audio_files = self.playlist def set_current(self, new_track=False, state=None): """ Set current file :param new_track: True - new audio file :param state: button state object """ if state.source == BOOK_MENU or state.source == INIT: if self.current_book_state and self.current_book_state.book_url == state.book_url and self.time_control.timer_started: return self.current_track_index = 0 if state.source == INIT: new_track = False else: new_track = True self.set_book(new_track, state) elif state.source == TRACK_MENU: if getattr(state, "index", None) and state.index == self.current_track_index: return new_track = True st = {"file_name": state.file_name} self.set_current_track_index(st) elif state.source == ARROW_BUTTON: new_track = True st = {"file_name": state.file_name} self.set_current_track_index(st) elif state.source == RESUME: new_track = False st = {"file_name": state.file_name} self.set_current_track_index(st) elif state.source == HOME_NAVIGATOR or state.source == BOOK_NAVIGATOR_BACK or state.source == BOOK_NAVIGATOR or state.source == GO_PLAYER: return if getattr(state, "url", None): self.file_button.components[ 1].image_filename = self.img_filename = state.url else: if hasattr(self, "img_filename"): self.file_button.components[ 1].image_filename = self.img_filename else: self.file_button.components[ 1].image_filename = self.img_filename = None img = self.cover_image if img: self.file_button.components[1].content = img self.file_button.state.icon_base = img self.file_button.state.show_bgr = True self.file_button.state.bgr = self.config[COLORS][COLOR_MEDIUM] self.file_button.add_background(self.file_button.state) self.file_button.components[ 1].content_x = self.layout.CENTER.x + int( (self.layout.CENTER.w - img.get_size()[0]) / 2) if self.layout.CENTER.h > img.get_size()[1]: self.file_button.components[ 1].content_y = self.layout.CENTER.y + int( (self.layout.CENTER.h - img.get_size()[1]) / 2) else: self.file_button.components[1].content_y = self.layout.CENTER.y if self.current_playlist == None: return self.set_audio_file(new_track, state) def set_audio_file(self, new_track, s=None): """ Set new audio file :param new_track: True - new audio file :param s: button state object """ state = State() state.playback_mode = FILE_AUDIO state.playlist_track_number = 0 name = url = None if s == None: if self.config[AUDIOBOOKS][BROWSER_TRACK_FILENAME]: name = self.config[AUDIOBOOKS][BROWSER_TRACK_FILENAME] for t in self.playlist: if t["title"].endswith(name): url = t["mp3"] else: i = 0 if s != None: i = s.index t = self.playlist[i] url = t["mp3"] name = t["title"] else: if getattr(s, "track_filename", None): i = self.get_current_track_index( {"file_name": s.track_filename}) elif getattr(s, "playlist_track_number", None): i = s.playlist_track_number else: i = self.current_track_index t = self.playlist[i] url = t["mp3"] name = t["title"] self.config[PLAYER_SETTINGS][PAUSE] = False state.file_name = name self.config[AUDIOBOOKS][BROWSER_TRACK_FILENAME] = t["file_name"] state.mute = self.config[PLAYER_SETTINGS][MUTE] state.pause = self.config[PLAYER_SETTINGS][PAUSE] self.play_button.draw_default_state(None) if self.config[VOLUME_CONTROL][ VOLUME_CONTROL_TYPE] == VOLUME_CONTROL_TYPE_PLAYER: state.volume = self.config[PLAYER_SETTINGS][VOLUME] else: state.volume = None state.file_type = FILE_AUDIO state.dont_notify = True state.source = FILE_AUDIO state.url = url state.mode = AUDIOBOOKS state.playback_mode = FILE_AUDIO state.music_folder = self.config[AUDIO][MUSIC_FOLDER] self.audio_files = self.get_audio_files_from_playlist() if self.config[AUDIOBOOKS][BROWSER_BOOK_TIME]: if new_track: state.track_time = "0" else: state.track_time = self.config[AUDIOBOOKS][BROWSER_BOOK_TIME] self.reset_loading() logging.debug(state.url) self.notify_play_listeners(state) def get_audio_files(self): """ Return audio files from playlist :return: files """ return self.get_audio_files_from_playlist() def get_audio_files_from_playlist(self): """ Call player for files in the playlist :return: list of files from playlist """ files = [] if getattr(self, "playlist", None): for n in range(len(self.playlist)): st = State() st.index = st.comparator_item = n t = self.playlist[n] st.file_type = FILE_AUDIO st.file_name = t["file_name"] files.append(st) return files def set_loading(self): """ Show Loading... sign """ b = self.config[COLORS][COLOR_DARK] f = self.config[COLORS][COLOR_BRIGHT] fs = 20 bx = self.file_button.bounding_box x = bx.x - 1 y = bx.y w = bx.w + 2 h = bx.h bb = pygame.Rect(x, y, w, h) t = self.factory.create_output_text(self.LOADING, bb, b, f, fs) t.set_text(self.LOADING) t.layer_name = self.LOADING self.left_button.change_label("") self.right_button.change_label("") self.add_component(t) self.clean_draw_update() self.notify_loading_listeners() def reset_loading(self): """ Remove Loading... sign """ n = getattr(self.components[-1], "layer_name", None) if n and n == self.LOADING: del self.components[-1] pygame.event.clear() self.notify_reset_loading_listeners() def add_loading_listener(self, listener): """ Add loading listener :param listener: event listener """ if listener not in self.loading_listeners: self.loading_listeners.append(listener) def notify_loading_listeners(self): """ Notify all loading listeners """ for listener in self.loading_listeners: listener(None) def add_reset_loading_listener(self, listener): """ Add reset loading listener :param listener: event listener """ if listener not in self.reset_loading_listeners: self.reset_loading_listeners.append(listener) def notify_reset_loading_listeners(self): """ Notify all reset loading listeners """ for listener in self.reset_loading_listeners: listener(None) def add_screen_observers(self, update_observer, redraw_observer, start_time_control, stop_time_control, title_to_json): """ Add screen observers :param update_observer: observer for updating the screen :param redraw_observer: observer to redraw the whole screen :param start_time_control: :param stop_time_control: :param title_to_json: """ FilePlayerScreen.add_screen_observers(self, update_observer, redraw_observer, start_time_control, stop_time_control, title_to_json) self.add_loading_listener(redraw_observer) self.add_reset_loading_listener(redraw_observer)
class MenuScreen(Screen): """ Site Menu Screen. Base class for all book menu screens """ def __init__(self, util, listeners, rows, columns, voice_assistant, d=None, turn_page=None, page_in_title=True): """ Initializer :param util: utility object :param listeners: file browser listeners :param rows: menu rows :param d: dictionary with menu button flags :param turn_page: turn page callback :param util: utility object """ self.util = util self.config = util.config self.factory = Factory(util) self.bounding_box = self.config[SCREEN_RECT] self.player = None self.turn_page = turn_page self.page_in_title = page_in_title self.cache = Cache(self.util) self.layout = BorderLayout(self.bounding_box) self.layout.set_percent_constraints(PERCENT_TOP_HEIGHT, PERCENT_BOTTOM_HEIGHT, 0, 0) Screen.__init__(self, util, "", PERCENT_TOP_HEIGHT, voice_assistant, "menu_screen_screen_title", True, self.layout.TOP) color_dark_light = self.config[COLORS][COLOR_DARK_LIGHT] self.menu_layout = self.layout.CENTER self.menu_button_layout = self.get_menu_button_layout(d) self.img_rect = self.menu_button_layout.image_rectangle listeners[GO_LEFT_PAGE] = self.previous_page listeners[GO_RIGHT_PAGE] = self.next_page try: self.navigator = BookNavigator(util, self.layout.BOTTOM, listeners, color_dark_light, d[4]) Container.add_component(self, None) Container.add_component(self, self.navigator) except: Container.add_component(self, None) self.total_pages = 0 self.current_page = 1 self.menu = None self.loading_listeners = [] self.LOADING = util.config[LABELS][KEY_LOADING] def get_menu_button_layout(self, d): """ Return menu button layout :param d: dictionary with menu button flags :return: menu button layout """ s = State() s.show_img = True s.show_label = True button_w = int(self.menu_layout.w / d[1]) button_h = int(self.menu_layout.h / d[0]) label_padding = 2 image_padding = 4 try: self.show_author = d[2] except: self.show_author = False try: self.show_genre = d[3] except: self.show_genre = False s.bounding_box = Rect(0, 0, button_w, button_h) return MultiLineButtonLayout(s, label_padding, image_padding) def previous_page(self, state): """ Handle click on left button :param state: button state object """ if self.current_page == 1: return self.current_page -= 1 if getattr(state, "select_last", False): self.components[1].selected_index = 0 self.menu.current_page = self.current_page self.menu.selected_index = 0 self.turn_page() def next_page(self, state): """ Handle click on right button :param state: button state object """ if self.total_pages <= 1 or self.total_pages == self.current_page: return self.current_page += 1 if self.current_page > self.total_pages: self.current_page = self.total_pages self.menu.current_page = self.current_page self.menu.selected_index = 0 self.turn_page() def set_menu(self, menu): """ Set menu :param menu: menu object """ self.menu = self.components[1] = menu def set_title(self, page_num): """ Set screen title :param page_num: menu page number """ if self.total_pages == 1 or not self.page_in_title: self.screen_title.set_text(self.title) return if len(str(page_num)) <= len(str(self.total_pages)): self.screen_title.set_text(self.title + " (" + str(page_num) + ")") def reset_title(self): """ Reset screen title """ self.screen_title.set_text(self.title + " (" + str(self.current_page) + ")") def go_to_page(self, page_num): """ Handle go to page event :param page_num: menu page number """ n = int(page_num) if n > self.total_pages: n = self.total_pages if n == 0: n = 1 self.current_page = n self.menu.current_page = self.current_page self.turn_page() def get_image_from_cache(self, url): """ Return image from cache :param url: image url :return: image """ return self.cache.get_image(url) def put_image_to_cache(self, url, img): """ Put image into cache :param url: image url :param img: image """ self.cache.cache_image(img, url) def set_button_image(self, b, icon, img_y=None): """ Set button image :param b: button :param icon: image :param img_y: image Y coordinate """ bb = b.bounding_box comps = b.components im = comps[1] im.content = icon w = im.content.get_size()[0] h = im.content.get_size()[1] im.content_x = bb.x + (bb.width - w) / 2 if img_y == None: img_area_height = bb.height - ((bb.height / LINES) * 2) img_y = bb.y + (img_area_height - h) / 2 im.content_y = img_y self.components[1].clean_draw_update() def set_loading(self, name): """ Show Loading... sign :name: screen title """ b = self.config[COLORS][COLOR_DARK] f = self.config[COLORS][COLOR_BRIGHT] fs = int(self.bounding_box.h * 0.07) bb = self.menu_layout t = self.factory.create_output_text(self.LOADING, bb, b, f, fs) t.set_text(self.LOADING) self.screen_title.set_text(name) self.set_visible(True) self.add_component(t) self.clean_draw_update() self.notify_loading_listeners() def reset_loading(self): """ Remove Loading... sign """ del self.components[-1] self.notify_loading_listeners() def add_loading_listener(self, listener): """ Add loading listener :param listener: event listener """ if listener not in self.loading_listeners: self.loading_listeners.append(listener) def notify_loading_listeners(self): """ Notify all loading listeners """ for listener in self.loading_listeners: listener(None)