def restore_state(self): """ Restore player state """ try: if App().settings.get_value("save-state"): current_track_id = load(open(LOLLYPOP_DATA_PATH + "/track_id.bin", "rb")) self.set_queue(load(open(LOLLYPOP_DATA_PATH + "/queue.bin", "rb"))) albums = load(open(LOLLYPOP_DATA_PATH + "/Albums.bin", "rb")) playlist_ids = load(open(LOLLYPOP_DATA_PATH + "/playlist_ids.bin", "rb")) (is_playing, was_party) = load(open(LOLLYPOP_DATA_PATH + "/player.bin", "rb")) if playlist_ids and playlist_ids[0] == Type.RADIOS: radios = Radios() track = Track() name = radios.get_name(current_track_id) url = radios.get_url(name) track.set_radio(name, url) self.load(track, is_playing) elif App().tracks.get_uri(current_track_id) != "": if albums: if was_party: App().lookup_action("party").change_state( GLib.Variant("b", True)) else: self._albums = load(open( LOLLYPOP_DATA_PATH + "/Albums.bin", "rb")) # Load track from player albums current_track = Track(current_track_id) index = self.album_ids.index(current_track.album.id) for track in self._albums[index].tracks: if track.id == current_track_id: self._load_track(track) break else: for playlist_id in playlist_ids: tracks = App().playlists.get_tracks(playlist_id) App().player.populate_playlist_by_tracks( tracks, playlist_ids) for track in tracks: if track.id == current_track_id: self._load_track(track) break if is_playing: self.play() else: self.pause() position = load(open(LOLLYPOP_DATA_PATH + "/position.bin", "rb")) self.seek(position / Gst.SECOND) else: Logger.info("Player::restore_state(): track missing") self.emit("playlist-changed") except Exception as e: Logger.error("Player::restore_state(): %s" % e)
def prepare_to_exit(self, action=None, param=None): """ Save window position and view """ if self._is_fs: return if self.settings.get_value('save-state'): self.window.save_view_state() # Save current track if self.player.current_track.id is None: track_id = -1 elif self.player.current_track.id == Type.RADIOS: radios = Radios() track_id = radios.get_id( self.player.current_track.album_artist) else: track_id = self.player.current_track.id self.settings.set_value('track-id', GLib.Variant('i', track_id)) # Save current playlist if self.player.current_track.id == Type.RADIOS: playlist_ids = [Type.RADIOS] elif not self.player.get_user_playlist_ids(): playlist_ids = [] else: playlist_ids = self.player.get_user_playlist_ids() self.settings.set_value('playlist-ids', GLib.Variant('ai', playlist_ids)) self.player.stop_all() if self.window: self.window.stop_all() self.quit()
def prev(self): """ Return prev radio name, uri @return Track """ track = Track() if self.current_track.id != Type.RADIOS: return track radios_manager = Radios() radios = radios_manager.get() i = len(radios) - 1 for (name, url) in reversed(radios): i -= 1 if self.current_track.album_artist == name: break # Get prev radio if i < 0: i = len(radios) - 1 name = radios[i][0] url = radios[i][1] if url: track.set_radio(name, url) return track
def next(self): """ Return next radio name, uri @return Track """ track = Track() if self.current_track.id != Type.RADIOS: return track radios_manager = Radios() radios = radios_manager.get() i = 0 for (name, url) in radios: i += 1 if self.current_track.album_artist == name: break # Get next radio if i >= len(radios): i = 0 name = radios[i][0] url = radios[i][1] if url: track.set_radio(name, url) return track
def set_popularity(self, new_rate): """ Set popularity @param new_rate as int between 0 and 5 """ if self.id is None: return try: if self.id >= 0: avg_popularity = self.db.get_avg_popularity() popularity = int((new_rate * avg_popularity / 5) + 0.5) best_popularity = self.db.get_higher_popularity() if new_rate == 5: popularity = (popularity + best_popularity) / 2 self.db.set_popularity(self.id, popularity) elif self.id == Type.RADIOS: radios = Radios() avg_popularity = radios.get_avg_popularity() popularity = int((new_rate * avg_popularity / 5) + 0.5) best_popularity = self.db.get_higher_popularity() if new_rate == 5: popularity = (popularity + best_popularity) / 2 radios.set_popularity(self._radio_id, popularity) except Exception as e: Logger.error("Base::set_popularity(): %s" % e)
def restore_state(self): """ Restore player state """ try: if Lp().settings.get_value('save-state'): track_id = load(open(DataPath + "/track_id.bin", "rb")) playlist_ids = load(open(DataPath + "/playlist_ids.bin", "rb")) if playlist_ids and playlist_ids[0] == Type.RADIOS: radios = Radios() track = Track() name = radios.get_name(track_id) url = radios.get_url(name) track.set_radio(name, url) self.load(track) elif Lp().tracks.get_uri(track_id) != "": track = Track(track_id) if Lp().notify is not None: Lp().notify.inhibit() self._load_track(track) # We set this initial state # because seek while failed otherwise self.pause() if playlist_ids: pids = [] for playlist_id in playlist_ids: pids.append(int(playlist_id)) track_ids = [] for playlist_id in playlist_ids: if playlist_id == Type.POPULARS: tracks = Lp().tracks.get_populars() elif playlist_id == Type.RECENTS: tracks = Lp().tracks.get_recently_listened_to() elif playlist_id == Type.NEVER: tracks = Lp().tracks.get_never_listened_to() elif playlist_id == Type.RANDOMS: tracks = Lp().tracks.get_randoms() else: tracks = Lp().playlists.get_track_ids( playlist_id) for track_id in tracks: if track_id not in track_ids: track_ids.append(track_id) self.populate_user_playlist_by_tracks( track_ids, pids) else: self._albums = load( open(DataPath + "/albums.bin", "rb")) self.shuffle_albums(True) self._context.genre_ids = load( open(DataPath + "/genre_ids.bin", "rb")) self._context.artist_ids = load( open(DataPath + "/artist_ids.bin", "rb")) self.set_next() self.set_prev() else: print("Player::restore_state(): track missing") except Exception as e: print("Player::restore_state()", e)
def restore_state(self): """ Restore player state """ if Lp().settings.get_value('save-state'): track_id = Lp().settings.get_value('track-id').get_int32() playlist_ids = Lp().settings.get_value('playlist-ids') if playlist_ids and playlist_ids[0] == Type.RADIOS: radios = Radios() track = Track() name = radios.get_name(track_id) url = radios.get_url(name) track.set_radio(name, url) self.load(track) elif Lp().tracks.get_path(track_id) != "": track = Track(track_id) self._load_track(track) if playlist_ids: try: pids = [] for playlist_id in playlist_ids: pids.append(int(playlist_id)) track_ids = [] for playlist_id in playlist_ids: if playlist_id == Type.POPULARS: tracks = Lp().tracks.get_populars() elif playlist_id == Type.RECENTS: tracks = Lp().tracks.get_recently_listened_to() elif playlist_id == Type.NEVER: tracks = Lp().tracks.get_never_listened_to() elif playlist_id == Type.RANDOMS: tracks = Lp().tracks.get_randoms() else: tracks = Lp().playlists.get_tracks_ids( playlist_id) for track_id in tracks: if track_id not in track_ids: track_ids.append(track_id) self.populate_user_playlist_by_tracks(track_ids, pids) except: pass # User set non int in gsettings else: self._albums = Lp().artists.get_albums( track.album_artist_ids) for album_id in self._albums: self.context.genre_ids[album_id] = [] self.context.artist_ids[album_id] = [] self.set_next() self.set_prev() if Lp().settings.get_value('repeat'): self.context.next = NextContext.NONE else: self.context.next = NextContext.STOP_ALL self.emit('current-changed') else: print("Player::restore_state(): track missing")
def set_rate(self, rate): """ Set rate @param rate as int between -1 and 5 """ if self.id == Type.RADIOS: radios = Radios() radios.set_rate(self._album_artists[0], rate) else: self.db.set_rate(self.id, rate)
def set_radio_id(self, radio_id): """ Set radio id @param radio_id as int """ from lollypop.radios import Radios radios = Radios() name = radios.get_name(radio_id) uri = radios.get_uri(radio_id) self.set_radio(name, uri)
def set_rate(self, rate): """ Set rate @param rate as int between -1 and 5 """ if self.id == Type.RADIOS: radios = Radios() radios.set_rate(self._radio_id, rate) else: self.db.set_rate(self.id, rate) App().player.emit("rate-changed", (self.id, rate))
def set_radio_id(self, radio_id): """ Set radio id @param radio_id as int """ from lollypop.radios import Radios radios = Radios() self.id = Type.RADIOS self._radio_id = radio_id self._radio_name = radios.get_name(radio_id) self._uri = radios.get_uri(radio_id)
def prepare_to_exit(self, action=None, param=None, exit=True): """ Save window position and view """ if self.__is_fs: return if self.settings.get_value('save-state'): self.window.save_view_state() # Save current track if self.player.current_track.id is None: track_id = -1 elif self.player.current_track.id == Type.RADIOS: from lollypop.radios import Radios radios = Radios() track_id = radios.get_id( self.player.current_track.album_artists[0]) else: track_id = self.player.current_track.id # Save albums context try: dump(self.player.context.genre_ids, open(DataPath + "/genre_ids.bin", "wb")) dump(self.player.context.artist_ids, open(DataPath + "/artist_ids.bin", "wb")) self.player.shuffle_albums(False) dump(self.player.get_albums(), open(DataPath + "/albums.bin", "wb")) except Exception as e: print("Application::prepare_to_exit()", e) dump(track_id, open(DataPath + "/track_id.bin", "wb")) dump([self.player.is_playing, self.player.is_party], open(DataPath + "/player.bin", "wb")) # Save current playlist if self.player.current_track.id == Type.RADIOS: playlist_ids = [Type.RADIOS] elif not self.player.get_user_playlist_ids(): playlist_ids = [] else: playlist_ids = self.player.get_user_playlist_ids() dump(playlist_ids, open(DataPath + "/playlist_ids.bin", "wb")) if self.player.current_track.id is not None: position = self.player.position else: position = 0 dump(position, open(DataPath + "/position.bin", "wb")) self.player.stop_all() self.window.stop_all() if self.charts is not None: self.charts.stop() if exit: self.quit()
def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._previous_urls = [] self._covers_to_download = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 4, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() self._spinner = builder.get_object('spinner') builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._stack.add_named(self._spinner, 'spinner') self._stack.add_named(builder.get_object('notfound'), 'notfound') self._stack.add_named(self._scrolled, 'scrolled') self.add(widget) size_setting = Lp().settings.get_value('window-size') if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1] * 0.7) else: self.set_size_request(700, 400)
def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self.__tunein = TuneIn() if radios_manager is not None: self.__radios_manager = radios_manager else: self.__radios_manager = Radios() self.__current_url = None self.__timeout_id = None self.__previous_urls = [] self.__covers_to_download = [] self.__stack = Gtk.Stack() self.__stack.set_property("expand", True) self.__stack.show() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/TuneinPopover.ui") builder.connect_signals(self) widget = builder.get_object("widget") widget.attach(self.__stack, 0, 2, 5, 1) self.__back_btn = builder.get_object("back_btn") self.__home_btn = builder.get_object("home_btn") self.__label = builder.get_object("label") self.__view = Gtk.FlowBox() self.__view.set_selection_mode(Gtk.SelectionMode.NONE) self.__view.set_max_children_per_line(100) self.__view.set_property("row-spacing", 10) self.__view.set_property("expand", True) self.__view.show() self.__spinner = builder.get_object("spinner") builder.get_object("viewport").add(self.__view) builder.get_object("viewport").set_property("margin", 10) self.__scrolled = builder.get_object("scrolled") self.__stack.add_named(self.__spinner, "spinner") self.__stack.add_named(builder.get_object("notfound"), "notfound") self.__stack.add_named(self.__scrolled, "scrolled") self.add(widget) self.connect("map", self.__on_map) self.connect("unmap", self.__on_unmap)
def __init__(self): """ Init view """ FlowBoxView.__init__(self) ViewController.__init__(self, ViewControllerType.RADIO) self._widget_class = RadioWidget self.__radios = Radios() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/RadiosView.ui") builder.connect_signals(self) self.insert_row(0) self.attach(builder.get_object("widget"), 0, 0, 1, 1) self.__pop_tunein = TuneinPopover(self.__radios) self.__pop_tunein.set_relative_to(builder.get_object("search_btn"))
def get_rate(self): """ Get rate @return int """ if self.id is None or self.id == Type.EXTERNALS: return 0 rate = 0 if self.id >= 0: rate = self.db.get_rate(self.id) elif self.id == Type.RADIOS: radios = Radios() rate = radios.get_rate(self._album_artists[0]) return rate
def get_rate(self): """ Get rate @return int """ if self.id is None: return 0 rate = 0 if self.id >= 0: rate = self.db.get_rate(self.id) elif self.id == Type.RADIOS: radios = Radios() rate = radios.get_rate(self._radio_id) return rate
def __start_playback(self, track): """ Start playing track @param track as Track: """ self._plugins.volume.props.volume = 1.0 self._playbin.set_state(Gst.State.NULL) self._playbin.set_property('uri', track.uri) Radios().set_more_popular(track.album_artists[0]) self._current_track = track self.__current = None self._playbin.set_state(Gst.State.PLAYING) if not self.__radios: self.__radios = Radios().get() self.emit('status-changed')
def __save_state(self): """ Save window position and view """ if self.is_fullscreen(): return if self.settings.get_value("save-state"): self.window.save_view_state() # Save current track if self.player.current_track.id is None: track_id = -1 elif self.player.current_track.id == Type.RADIOS: from lollypop.radios import Radios radios = Radios() track_id = radios.get_id( self.player.current_track.album_artists[0]) else: track_id = self.player.current_track.id # Save albums context try: dump(self.player.context.genre_ids, open(LOLLYPOP_DATA_PATH + "/genre_ids.bin", "wb")) dump(self.player.context.artist_ids, open(LOLLYPOP_DATA_PATH + "/artist_ids.bin", "wb")) self.player.shuffle_albums(False) dump(self.player.get_albums(), open(LOLLYPOP_DATA_PATH + "/albums.bin", "wb")) except Exception as e: print("Application::__save_state()", e) dump(track_id, open(LOLLYPOP_DATA_PATH + "/track_id.bin", "wb")) dump([self.player.is_playing, self.player.is_party], open(LOLLYPOP_DATA_PATH + "/player.bin", "wb")) # Save current playlist if self.player.current_track.id == Type.RADIOS: playlist_ids = [Type.RADIOS] elif not self.player.get_user_playlist_ids(): playlist_ids = [] else: playlist_ids = self.player.get_user_playlist_ids() dump(playlist_ids, open(LOLLYPOP_DATA_PATH + "/playlist_ids.bin", "wb")) if self.player.current_track.id is not None: position = self.player.position else: position = 0 dump(position, open(LOLLYPOP_DATA_PATH + "/position.bin", "wb")) self.player.stop_all() self.window.stop_all()
def quit(self): """ Quit lollypop """ if self.scanner.is_locked(): self.scanner.stop() GLib.idle_add(self.quit) return self.db.del_tracks(self.tracks.get_non_persistent()) try: from lollypop.radios import Radios with SqlCursor(self.db) as sql: sql.isolation_level = None sql.execute('VACUUM') sql.isolation_level = '' with SqlCursor(self.playlists) as sql: sql.isolation_level = None sql.execute('VACUUM') sql.isolation_level = '' with SqlCursor(Radios()) as sql: sql.isolation_level = None sql.execute('VACUUM') sql.isolation_level = '' except Exception as e: print("Application::quit(): ", e) self.window.destroy()
def __upgrade_21(self, db): """ Add rate to radios """ with SqlCursor(Radios()) as sql: sql.execute("ALTER TABLE radios ADD rate\ INT NOT NULL DEFAULT -1")
def __vacuum(self): """ VACUUM DB """ try: if self.scanner.is_locked(): self.scanner.stop() GLib.idle_add(self.__vacuum) return self.tracks.del_non_persistent() self.tracks.clean() self.albums.clean() self.artists.clean() self.genres.clean() from lollypop.radios import Radios with SqlCursor(self.db) as sql: sql.isolation_level = None sql.execute("VACUUM") sql.isolation_level = "" with SqlCursor(self.playlists) as sql: sql.isolation_level = None sql.execute("VACUUM") sql.isolation_level = "" with SqlCursor(Radios()) as sql: sql.isolation_level = None sql.execute("VACUUM") sql.isolation_level = "" except Exception as e: Logger.error("Application::__vacuum(): %s" % e)
def __vacuum(self): """ VACUUM DB """ if self.scanner.is_locked(): self.scanner.stop() GLib.idle_add(self.__vacuum) return self.db.del_tracks(self.tracks.get_non_persistent()) try: from lollypop.radios import Radios with SqlCursor(self.db) as sql: sql.isolation_level = None sql.execute("VACUUM") sql.isolation_level = "" with SqlCursor(self.playlists) as sql: sql.isolation_level = None sql.execute("VACUUM") sql.isolation_level = "" with SqlCursor(Radios()) as sql: sql.isolation_level = None sql.execute("VACUUM") sql.isolation_level = "" except Exception as e: print("Application::__vacuum(): ", e)
def set_radio(self, name, uri): """ Set radio for non DB radios (Tunein) @param name as string @param uri as string """ from lollypop.radios import Radios radios = Radios() self.id = Type.RADIOS self._radio_id = radios.get_id(name) self._radio_name = name self._uri = uri # Generate a tmp album id, needed by InfoController album_id = 0 for i in list(map(ord, name)): album_id += i self.album.id = album_id
def __init__(self): """ Init view """ View.__init__(self) self._signal = Lp().art.connect('radio-artwork-changed', self._on_logo_changed) self._radios_manager = Radios() self._radios_manager.connect('radios-changed', self._on_radios_changed) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/RadiosView.ui') builder.connect_signals(self) widget = builder.get_object('widget') self._empty = builder.get_object('empty') self._pop_tunein = TuneinPopover(self._radios_manager) self._pop_tunein.set_relative_to(builder.get_object('search_btn')) self._sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.BOTH) self._radiobox = Gtk.FlowBox() self._radiobox.set_selection_mode(Gtk.SelectionMode.NONE) self._radiobox.connect("child-activated", self._on_album_activated) self._radiobox.set_property('column-spacing', 5) self._radiobox.set_property('row-spacing', 5) self._radiobox.set_homogeneous(True) self._radiobox.set_max_children_per_line(1000) self._radiobox.show() self._stack = Gtk.Stack() self._stack.set_transition_duration(500) self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self._stack.add(self._scrolled) self._stack.add(self._empty) self._stack.show() self._viewport.set_property("valign", Gtk.Align.START) self._viewport.set_property('margin', 5) self._viewport.add(self._radiobox) self._scrolled.set_property('expand', True) self.add(widget) self.add(self._stack)
def __init__(self, view_type=ViewType.SCROLLED): """ Init view @param view_type as ViewType """ FlowBoxView.__init__(self, view_type) ViewController.__init__(self, ViewControllerType.RADIO) self._widget_class = RadioWidget self._empty_icon_name = get_icon_name(Type.RADIOS) self.__radios = Radios() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/RadiosView.ui") builder.connect_signals(self) self.insert_row(0) self.attach(builder.get_object("widget"), 0, 0, 1, 1) self.__pop_tunein = None if not get_network_available("TUNEIN"): builder.get_object("search_btn").hide()
def set_popularity(self, popularity): """ Set popularity @param popularity as int between 0 and 5 """ if self.id is None or self.id == Type.EXTERNALS: return try: if self.id >= 0: avg_popularity = self.db.get_avg_popularity() popularity = int((popularity * avg_popularity / 5) + 0.5) self.db.set_popularity(self.id, popularity, True) elif self.id == Type.RADIOS: radios = Radios() avg_popularity = radios.get_avg_popularity() popularity = int((popularity * avg_popularity / 5) + 0.5) radios.set_popularity(self._album_artists[0], popularity) except Exception as e: print("Base::set_popularity(): %s" % e)
def get_popularity(self): """ Get popularity @return int between 0 and 5 """ if self.id is None or self.id == Type.EXTERNALS: return 0 popularity = 0 if self.id >= 0: avg_popularity = self.db.get_avg_popularity() if avg_popularity > 0: popularity = self.db.get_popularity(self.id) elif self.id == Type.RADIOS: radios = Radios() avg_popularity = radios.get_avg_popularity() if avg_popularity > 0: popularity = radios.get_popularity(self._album_artists[0]) return popularity * 5 / avg_popularity + 0.5
def get_popularity(self): """ Get popularity @return int between 0 and 5 """ if self.id is None: return 0 popularity = 0 if self.id >= 0: avg_popularity = self.db.get_avg_popularity() if avg_popularity > 0: popularity = self.db.get_popularity(self.id) elif self.id == Type.RADIOS: radios = Radios() avg_popularity = radios.get_avg_popularity() if avg_popularity > 0: popularity = radios.get_popularity(self._radio_id) return popularity * 5 / avg_popularity + 0.5
def __save_state(self): """ Save window position and view """ if not self.settings.get_value("save-state"): return if self.player.current_track.id is None or\ self.player.current_track.mtime == 0: track_id = None elif self.player.current_track.id == Type.RADIOS: from lollypop.radios import Radios radios = Radios() track_id = radios.get_id(self.player.current_track.radio_name) else: track_id = self.player.current_track.id # Save albums context try: with open(LOLLYPOP_DATA_PATH + "/Albums.bin", "wb") as f: dump(list(self.player.albums), f) except Exception as e: Logger.error("Application::__save_state(): %s" % e) dump(track_id, open(LOLLYPOP_DATA_PATH + "/track_id.bin", "wb")) dump([self.player.is_playing, self.player.is_party], open(LOLLYPOP_DATA_PATH + "/player.bin", "wb")) dump(self.player.queue, open(LOLLYPOP_DATA_PATH + "/queue.bin", "wb")) # Save current playlist if self.player.current_track.id == Type.RADIOS: playlist_ids = [Type.RADIOS] elif not self.player.playlist_ids: playlist_ids = [] else: playlist_ids = self.player.playlist_ids dump(playlist_ids, open(LOLLYPOP_DATA_PATH + "/playlist_ids.bin", "wb")) if self.player.current_track.id is not None: position = self.player.position else: position = 0 dump(position, open(LOLLYPOP_DATA_PATH + "/position.bin", "wb")) self.player.stop_all() self.__window.container.stop_all()
def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._timeout_id = None self._previous_urls = [] self._covers_to_download = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 5, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() self._spinner = builder.get_object('spinner') builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._stack.add_named(self._spinner, 'spinner') self._stack.add_named(builder.get_object('notfound'), 'notfound') self._stack.add_named(self._scrolled, 'scrolled') self.add(widget) size_setting = Lp().settings.get_value('window-size') if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1]*0.7) else: self.set_size_request(700, 400)
def quit(self): """ Quit lollypop """ if Lp.scanner.is_locked(): Lp.scanner.stop() GLib.idle_add(self.quit) return try: Lp.sql.execute('VACUUM') sql_p = Lp.playlists.get_cursor() sql_p.execute('VACUUM') sql_p.close() radios = Radios() sql_r = radios.get_cursor() sql_r.execute('VACUUM') sql_r.close() except Exception as e: print("Application::quit(): ", e) Lp.window.destroy() Lp.sql.close() Gst.deinit()
def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._previous_urls = [] self._stack = Gtk.Stack() self._stack.set_property("expand", True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Lollypop/TuneinPopover.ui") builder.connect_signals(self) widget = builder.get_object("widget") widget.attach(self._stack, 0, 2, 4, 1) self._back_btn = builder.get_object("back_btn") self._home_btn = builder.get_object("home_btn") self._label = builder.get_object("label") self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property("row-spacing", 10) self._view.set_property("expand", True) self._view.show() builder.get_object("viewport").add(self._view) builder.get_object("viewport").set_property("margin", 10) self._scrolled = builder.get_object("scrolled") self._stack.add_named(builder.get_object("spinner"), "spinner") self._stack.add_named(builder.get_object("notfound"), "notfound") self._stack.add_named(self._scrolled, "scrolled") self._stack.set_visible_child_name("spinner") self.add(widget) size_setting = Lp().settings.get_value("window-size") if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1] * 0.7) else: self.set_size_request(700, 400)
def __init__(self): """ Init view """ LazyLoadingView.__init__(self, True) self.__signal = Lp().art.connect('radio-artwork-changed', self.__on_logo_changed) self.__radios_manager = Radios() self.__radios_manager.connect('radios-changed', self.__on_radios_changed) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/RadiosView.ui') builder.connect_signals(self) widget = builder.get_object('widget') self.__empty = builder.get_object('empty') self.__pop_tunein = TuneinPopover(self.__radios_manager) self.__pop_tunein.set_relative_to(builder.get_object('search_btn')) self._box = Gtk.FlowBox() self._box.set_selection_mode(Gtk.SelectionMode.NONE) # Allow lazy loading to not jump up and down self._box.set_homogeneous(True) self._box.set_max_children_per_line(1000) self._box.set_filter_func(self._filter_func) self._box.show() self.__stack = Gtk.Stack() self.__stack.set_transition_duration(500) self.__stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self.__stack.add(self._scrolled) self.__stack.add(self.__empty) self.__stack.show() self._viewport.set_property("valign", Gtk.Align.START) self._viewport.set_property('margin', 5) self._scrolled.set_property('expand', True) self.add(widget) self.add(self.__stack)
def restore_state(self): """ Restore player state """ try: if Lp().settings.get_value('save-state'): track_id = load(open(DataPath + "/track_id.bin", "rb")) playlist_ids = load(open(DataPath + "/playlist_ids.bin", "rb")) if playlist_ids and playlist_ids[0] == Type.RADIOS: radios = Radios() track = Track() name = radios.get_name(track_id) url = radios.get_url(name) track.set_radio(name, url) self.load(track) elif Lp().tracks.get_path(track_id) != "": track = Track(track_id) if Lp().notify is not None: Lp().notify.inhibit() self._load_track(track) # We set this initial state # because seek while failed otherwise self.pause() if playlist_ids: pids = [] for playlist_id in playlist_ids: pids.append(int(playlist_id)) track_ids = [] for playlist_id in playlist_ids: if playlist_id == Type.POPULARS: tracks = Lp().tracks.get_populars() elif playlist_id == Type.RECENTS: tracks = Lp().tracks.get_recently_listened_to() elif playlist_id == Type.NEVER: tracks = Lp().tracks.get_never_listened_to() elif playlist_id == Type.RANDOMS: tracks = Lp().tracks.get_randoms() else: tracks = Lp().playlists.get_track_ids( playlist_id) for track_id in tracks: if track_id not in track_ids: track_ids.append(track_id) self.populate_user_playlist_by_tracks(track_ids, pids) else: self._albums = load(open( DataPath + "/albums.bin", "rb")) self.shuffle_albums(True) self._context.genre_ids = load(open( DataPath + "/genre_ids.bin", "rb")) self._context.artist_ids = load(open( DataPath + "/artist_ids.bin", "rb")) self.set_next() self.set_prev() if Lp().settings.get_value('repeat'): self._context.next = NextContext.NONE else: self._context.next = NextContext.STOP_ALL else: print("Player::restore_state(): track missing") except Exception as e: print("Player::restore_state()", e)
class TuneinPopover(Gtk.Popover): """ Popover showing tunin radios """ def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._previous_urls = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 4, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._stack.add_named(builder.get_object('spinner'), 'spinner') self._stack.add_named(builder.get_object('notfound'), 'notfound') self._stack.add_named(self._scrolled, 'scrolled') self._stack.set_visible_child_name('spinner') self.add(widget) size_setting = Lp().settings.get_value('window-size') if isinstance(size_setting[1], int): self.set_size_request(700, size_setting[1]*0.7) else: self.set_size_request(700, 400) def populate(self, url=None): """ Populate views @param url as string """ if not self._view.get_children(): self._current_url = url self._clear() self._back_btn.set_sensitive(False) self._home_btn.set_sensitive(False) self._label.set_text(_("Please wait...")) t = Thread(target=self._populate, args=(url,)) t.daemon = True t.start() ####################### # PRIVATE # ####################### def _show_not_found(self): """ Show not found message """ self._label.set_text(_("Can't connect to TuneIn...")) self._stack.set_visible_child_name('notfound') self._home_btn.set_sensitive(True) def _populate(self, url): """ Same as populate() @param url as string @thread safe """ if url is None: items = self._tunein.get_items() else: items = self._tunein.get_items(url) if items: self._add_items(items) else: GLib.idle_add(self._show_not_found) def _add_items(self, items): """ Add current items @param items as [TuneItem] @thread safe """ for item in items: GLib.idle_add(self._add_item, item) def _add_item(self, item): """ Add item @param item as TuneItem """ child = Gtk.Grid() child.set_property('halign', Gtk.Align.START) child.show() link = Gtk.LinkButton.new_with_label(item.URL, item.TEXT) link.connect('activate-link', self._on_activate_link, item) link.show() if item.TYPE == "audio": link.set_tooltip_text(_("Play")) button = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.MENU) button.connect('clicked', self._on_button_clicked, item) button.set_relief(Gtk.ReliefStyle.NONE) button.set_tooltip_text(_("Add")) button.show() child.add(button) else: link.set_tooltip_text('') child.add(link) self._view.add(child) # Remove spinner if exist if self._stack.get_visible_child_name() == 'spinner': self._stack.set_visible_child_name('scrolled') self._label.set_text(_("Browse themes and add a new radio")) if self._current_url is not None: self._back_btn.set_sensitive(True) self._home_btn.set_sensitive(True) def _clear(self): """ Clear view """ for child in self._view.get_children(): self._view.remove(child) child.destroy() def _add_radio(self, item): """ Add selected radio @param item as TuneIn Item """ # Get cover art try: cache = Art._RADIOS_PATH s = Gio.File.new_for_uri(item.LOGO) d = Gio.File.new_for_path(cache+"/%s.png" % item.TEXT.replace('/', '-')) s.copy(d, Gio.FileCopyFlags.OVERWRITE, None, None) except Exception as e: print("TuneinPopover::_add_radio: %s" % e) url = item.URL # Tune in embbed uri in ashx files, so get content if possible try: f = Gio.File.new_for_uri(url) (status, data, tag) = f.load_contents() if status: url = data.decode('utf-8').split('\n')[0] except Exception as e: print("TuneinPopover::_add_radio: %s" % e) self._radios_manager.add(item.TEXT.replace('/', '-'), url) def _on_back_btn_clicked(self, btn): """ Go to previous URL @param btn as Gtk.Button """ url = None self._current_url = None if self._previous_urls: url = self._previous_urls.pop() self._stack.set_visible_child_name('spinner') self._clear() self.populate(url) def _on_home_btn_clicked(self, btn): """ Go to root URL @param btn as Gtk.Button """ self._current_url = None self._previous_urls = [] self._stack.set_visible_child_name('spinner') self._clear() self.populate() def _on_activate_link(self, link, item): """ Update header with new link @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self._stack.set_visible_child_name('spinner') self._clear() self._scrolled.get_vadjustment().set_value(0.0) if self._current_url is not None: self._previous_urls.append(self._current_url) self.populate(item.URL) elif item.TYPE == "audio": # Only toolbar will get this one, so only create small in cache if Gio.NetworkMonitor.get_default().get_network_available(): t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, ArtSize.SMALL)) t.daemon = True t.start() Lp().player.load_external(item.URL, item.TEXT) Lp().player.play_this_external(item.URL) return True def _on_button_clicked(self, button, item): """ Play the radio @param link as Gtk.Button @param item as TuneIn Item """ t = Thread(target=self._add_radio, args=(item,)) t.daemon = True t.start() self.hide()
class TuneinPopover(Gtk.Popover): """ Popover showing tunin radios """ def __init__(self, radios_manager=None): """ Init Popover @param radios_manager as Radios """ Gtk.Popover.__init__(self) self._tunein = TuneIn() if radios_manager is not None: self._radios_manager = radios_manager else: self._radios_manager = Radios() self._current_url = None self._timeout_id = None self._previous_urls = [] self._covers_to_download = [] self._stack = Gtk.Stack() self._stack.set_property('expand', True) self._stack.show() builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/TuneinPopover.ui') builder.connect_signals(self) widget = builder.get_object('widget') widget.attach(self._stack, 0, 2, 5, 1) self._back_btn = builder.get_object('back_btn') self._home_btn = builder.get_object('home_btn') self._label = builder.get_object('label') self._view = Gtk.FlowBox() self._view.set_selection_mode(Gtk.SelectionMode.NONE) self._view.set_max_children_per_line(100) self._view.set_property('row-spacing', 10) self._view.set_property('expand', True) self._view.show() self._spinner = builder.get_object('spinner') builder.get_object('viewport').add(self._view) builder.get_object('viewport').set_property('margin', 10) self._scrolled = builder.get_object('scrolled') self._stack.add_named(self._spinner, 'spinner') self._stack.add_named(builder.get_object('notfound'), 'notfound') self._stack.add_named(self._scrolled, 'scrolled') self.add(widget) self.connect('map', self._on_map) self.connect('unmap', self._on_unmap) def populate(self, url=None): """ Populate views @param url as string """ if url is None and self._current_url is not None: return self._spinner.start() self._clear() self._stack.set_visible_child_name('spinner') self._current_url = url self._back_btn.set_sensitive(False) self._home_btn.set_sensitive(False) self._label.set_text(_("Please wait...")) t = Thread(target=self._populate, args=(url,)) t.daemon = True t.start() ####################### # PRIVATE # ####################### def _show_not_found(self, message=""): """ Show not found message @param message as str """ # TODO Add a string self._label.set_text(message) self._stack.set_visible_child_name('notfound') self._home_btn.set_sensitive(True) def _populate(self, url): """ Same as populate() @param url as string @thread safe """ try: if url is None: items = self._tunein.get_items( "http://opml.radiotime.com/Browse.ashx?c=") else: items = self._tunein.get_items(url) if self._current_url == url: if items: self._add_items(items, url) else: GLib.idle_add(self._show_not_found) except: GLib.idle_add(self._show_not_found, _("Can't connect to TuneIn...")) def _add_items(self, items, url): """ Add current items @param items as [TuneItem] @parma url as str @thread safe """ GLib.idle_add(self._add_item, items, url) def _add_item(self, items, url): """ Add item @param items as [TuneItem] @param url as str """ if url != self._current_url: return if not items: self._home_btn.set_sensitive(self._current_url is not None) t = Thread(target=self._download_images, args=(url,)) t.daemon = True t.start() return item = items.pop(0) child = Gtk.Grid() child.set_column_spacing(5) child.set_property('halign', Gtk.Align.START) child.show() link = Gtk.LinkButton.new_with_label(item.URL, item.TEXT) # Hack link.get_children()[0].set_ellipsize(Pango.EllipsizeMode.END) link.connect('activate-link', self._on_activate_link, item) link.show() if item.TYPE == "audio": link.set_tooltip_text(_("Play")) button = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.MENU) button.connect('clicked', self._on_button_clicked, item) button.set_relief(Gtk.ReliefStyle.NONE) button.set_property('valign', Gtk.Align.CENTER) button.set_tooltip_text(_("Add")) button.show() child.add(button) image = Gtk.Image.new() image.set_property('width-request', ArtSize.MEDIUM) image.set_property('height-request', ArtSize.MEDIUM) image.show() child.add(image) self._covers_to_download.append((item, image)) else: link.set_tooltip_text('') child.add(link) self._view.add(child) # Remove spinner if exist if self._stack.get_visible_child_name() == 'spinner': self._stack.set_visible_child_name('scrolled') self._spinner.stop() self._label.set_text("") if self._current_url is not None: self._back_btn.set_sensitive(True) GLib.idle_add(self._add_items, items, url) def _download_images(self, url): """ Download and set image for TuneItem @param url as str @thread safe """ while self._covers_to_download and url == self._current_url: (item, image) = self._covers_to_download.pop(0) try: f = Gio.File.new_for_uri(item.LOGO) (status, data, tag) = f.load_contents() if status: stream = Gio.MemoryInputStream.new_from_data(data, None) if stream is not None: GLib.idle_add(self._set_image, image, stream) except Exception as e: GLib.idle_add(image.set_from_icon_name, "image-missing", Gtk.IconSize.LARGE_TOOLBAR) print("TuneinPopover::_download_images: %s" % e) def _set_image(self, image, stream): """ Set image with stream @param image as Gtk.Image @param stream as Gio.MemoryInputStream """ pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, ArtSize.MEDIUM, ArtSize.MEDIUM, True, None) surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, 0, None) del pixbuf image.set_from_surface(surface) del surface def _clear(self): """ Clear view """ for child in self._view.get_children(): self._view.remove(child) child.destroy() def _add_radio(self, item): """ Add selected radio @param item as TuneIn Item """ # Get cover art try: cache = Art._RADIOS_PATH s = Gio.File.new_for_uri(item.LOGO) d = Gio.File.new_for_path(cache+"/%s.png" % item.TEXT.replace('/', '-')) s.copy(d, Gio.FileCopyFlags.OVERWRITE, None, None) except Exception as e: print("TuneinPopover::_add_radio: %s" % e) url = item.URL # Tune in embbed uri in ashx files, so get content if possible try: f = Gio.File.new_for_uri(url) (status, data, tag) = f.load_contents() if status: url = data.decode('utf-8').split('\n')[0] except Exception as e: print("TuneinPopover::_add_radio: %s" % e) self._radios_manager.add(item.TEXT.replace('/', '-'), url) def _on_map(self, widget): """ Resize and disable global shortcuts @param widget as Gtk.Widget """ # FIXME Not needed with GTK >= 3.18 Lp().window.enable_global_shorcuts(False) size = Lp().window.get_size() self.set_size_request(size[0]*0.5, size[1]*0.7) def _on_unmap(self, widget): """ Enable global shorcuts @param widget as Gtk.Widget """ # FIXME Not needed with GTK >= 3.18 Lp().window.enable_global_shorcuts(True) def _on_back_btn_clicked(self, btn): """ Go to previous URL @param btn as Gtk.Button """ url = None self._current_url = None if self._previous_urls: url = self._previous_urls.pop() self._stack.set_visible_child_name('spinner') self._spinner.start() self._clear() self.populate(url) def _on_home_btn_clicked(self, btn): """ Go to root URL @param btn as Gtk.Button """ self._current_url = None self._previous_urls = [] self.populate() def _on_activate_link(self, link, item): """ Update header with new link @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self._scrolled.get_vadjustment().set_value(0.0) if self._current_url is not None: self._previous_urls.append(self._current_url) self.populate(item.URL) elif item.TYPE == "audio": # Only toolbar will get this one, so only create small in cache if Gio.NetworkMonitor.get_default().get_network_available(): t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, Lp().window.toolbar.artsize)) t.daemon = True t.start() t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, ArtSize.BIG)) t.daemon = True t.start() Lp().player.load_external(item.URL, item.TEXT) Lp().player.play_this_external(item.URL) return True def _on_button_clicked(self, button, item): """ Play the radio @param link as Gtk.Button @param item as TuneIn Item """ self._timeout_id = None t = Thread(target=self._add_radio, args=(item,)) t.daemon = True t.start() self.hide() def _on_search_changed(self, widget): """ Timeout filtering, call _really_do_filterting() after timeout @param widget as Gtk.TextEntry """ self._current_url = None if self._timeout_id is not None: GLib.source_remove(self._timeout_id) self._timeout_id = None text = widget.get_text() if text != "": self._home_btn.set_sensitive(True) self._timeout_id = GLib.timeout_add(1000, self._on_search_timeout, text) else: self._home_btn.set_sensitive(False) self._timeout_id = GLib.timeout_add(1000, self._on_home_btn_clicked, None) def _on_search_timeout(self, string): """ Populate widget @param string as str """ self._timeout_id = None url = "http://opml.radiotime.com/Search.ashx?query=%s" % escape(string) self.populate(url)
class RadiosView(View): """ Show radios in a grid """ def __init__(self): """ Init view """ View.__init__(self) self._signal = Lp().art.connect('radio-artwork-changed', self._on_logo_changed) self._radios_manager = Radios() self._radios_manager.connect('radios-changed', self._on_radios_changed) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/RadiosView.ui') builder.connect_signals(self) widget = builder.get_object('widget') self._empty = builder.get_object('empty') self._pop_tunein = TuneinPopover(self._radios_manager) self._pop_tunein.set_relative_to(builder.get_object('search_btn')) self._sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.BOTH) self._radiobox = Gtk.FlowBox() self._radiobox.set_selection_mode(Gtk.SelectionMode.NONE) self._radiobox.connect("child-activated", self._on_album_activated) self._radiobox.set_property('column-spacing', 5) self._radiobox.set_property('row-spacing', 5) self._radiobox.set_homogeneous(True) self._radiobox.set_max_children_per_line(1000) self._radiobox.show() self._stack = Gtk.Stack() self._stack.set_transition_duration(500) self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self._stack.add(self._scrolled) self._stack.add(self._empty) self._stack.show() self._viewport.set_property("valign", Gtk.Align.START) self._viewport.set_property('margin', 5) self._viewport.add(self._radiobox) self._scrolled.set_property('expand', True) self.add(widget) self.add(self._stack) def populate(self): """ Populate view with tracks from playlist Thread safe """ Lp().player.set_radios(self._radios_manager.get()) if Lp().player.current_track.id == Type.RADIOS: Lp().player.set_next() # We force next update Lp().player.set_prev() # We force prev update t = Thread(target=self._populate) t.daemon = True t.start() ####################### # PRIVATE # ####################### def _populate(self): """ Populate view with tracks from playlist Thread safe """ radios = [] # Get radios name for (name, url) in self._radios_manager.get(): radios.append(name) GLib.idle_add(self._show_stack, radios) def _get_children(self): """ Return view children @return [RadioWidget] """ children = [] for child in self._radiobox.get_children(): widget = child.get_child() children.append(widget) return children def _on_destroy(self, widget): """ Disconnect signals @param widget as Gtk.Widget """ if self._signal is not None: Lp().art.disconnect(self._signal) def _on_new_clicked(self, widget): """ Show popover for adding a new radio @param widget as Gtk.Widget """ popover = RadioPopover('', self._radios_manager) popover.set_relative_to(widget) popover.show() def _on_search_clicked(self, widget): """ Show popover for searching radios @param widget as Gtk.Widget """ self._pop_tunein.populate() self._pop_tunein.show() def _on_radios_changed(self, manager): """ Update radios @param manager as PlaylistManager """ radios_name = [] currents = [] new_name = None old_widget = None old_child = None # Get radios name for (name, url) in manager.get(): radios_name.append(name) # Get currents widget less removed for child in self._radiobox.get_children(): widget = child.get_child() if widget.get_name() not in radios_name: old_widget = widget old_child = child else: currents.append(widget.get_name()) # Add the new radio for name in radios_name: if name not in currents: new_name = name break # Rename widget if new_name is not None: if old_widget is not None: old_widget.set_name(new_name) else: radios = [new_name] self._show_stack(radios) # Delete widget elif old_widget is not None: self._radiobox.remove(old_child) old_widget.destroy() if not self._radiobox.get_children(): self._show_stack([]) # Update player state based on current view radios = [] for child in self._radiobox.get_children(): widget = child.get_child() name = widget.get_name() url = manager.get_url(name) radios.append((name, url)) Lp().player.set_radios(radios) if Lp().player.current_track.id == Type.RADIOS: Lp().player.set_next() # We force next update Lp().player.set_prev() # We force prev update def _on_logo_changed(self, player, name): """ Update radio logo @param player as Plyaer @param name as string """ for child in self._radiobox.get_children(): widget = child.get_child() if widget.get_name() == name: widget.update_cover() def _show_stack(self, radios): """ Switch empty/radios view based on radios @param [radio names as string] """ if radios: self._stack.set_visible_child(self._scrolled) self._add_radios(radios, True) else: self._stack.set_visible_child(self._empty) def _add_radios(self, radios, first=False): """ Pop a radio and add it to the view, repeat operation until radio list is empty @param [radio names as string] @param first as bool """ if radios and not self._stop: radio = radios.pop(0) widget = RadioWidget(radio, self._radios_manager) widget.show() self._sizegroup.add_widget(widget) if first: self._radiobox.insert(widget, 0) else: self._radiobox.insert(widget, -1) GLib.idle_add(self._add_radios, radios) else: self._stop = False return None def _on_album_activated(self, flowbox, child): """ Play album @param flowbox as Gtk.Flowbox @child as Gtk.FlowboxChild """ name = child.get_child().get_name() url = self._radios_manager.get_url(name) if url: track = Track() track.set_radio(name, url) Lp().player.load(track)