class Container: """ Container for main view """ def __init__(self): """ Init container """ self._pulse_timeout = None # Index will start at -VOLUMES self._devices = {} self._devices_index = Type.DEVICES self._show_genres = Lp().settings.get_value('show-genres') self._stack = ViewContainer(500) self._stack.show() self._setup_view() self._setup_scanner() (list_one_ids, list_two_ids) = self._get_saved_view_state() if list_one_ids and list_one_ids[0] != Type.NONE: self._list_one.select_ids(list_one_ids) if list_two_ids and list_two_ids[0] != Type.NONE: self._list_two.select_ids(list_two_ids) # Volume manager self._vm = Gio.VolumeMonitor.get() self._vm.connect('mount-added', self._on_mount_added) self._vm.connect('mount-removed', self._on_mount_removed) for mount in self._vm.get_mounts(): self._add_device(mount, False) Lp().playlists.connect('playlists-changed', self._update_playlists) def update_db(self): """ Update db at startup only if needed """ # Stop previous scan if Lp().scanner.is_locked(): Lp().scanner.stop() GLib.timeout_add(250, self.update_db) else: # Something (device manager) is using progress bar if not self._progress.is_visible(): Lp().scanner.update(self._progress) def get_genre_id(self): """ Return current selected genre @return genre id as int """ if self._show_genres: return self._list_one.get_selected_id() else: return None def init_list_one(self): """ Init list one """ self._update_list_one(None) def save_view_state(self): """ Save view state """ Lp().settings.set_value( "list-one-ids", GLib.Variant('ai', self._list_one.get_selected_ids())) Lp().settings.set_value( "list-two-ids", GLib.Variant('ai', self._list_two.get_selected_ids())) def show_playlist_manager(self, object_id, genre_ids, artist_ids, is_album): """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre ids as [int] @param artist ids as [int] @param is_album as bool """ view = PlaylistsManageView(object_id, genre_ids, artist_ids, is_album) view.populate() view.show() self._stack.add(view) self._stack.set_visible_child(view) def show_playlist_editor(self, playlist_id): """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist id as int @param playlist name as str """ view = PlaylistEditView(playlist_id) view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) view.populate() def main_widget(self): """ Get main widget @return Gtk.HPaned """ return self._paned_main_list def get_view_width(self): """ Return view width @return width as int """ return self._stack.get_allocation().width def stop_all(self): """ Stop current view from processing """ view = self._stack.get_visible_child() if view is not None: self._stack.clean_old_views(None) def show_genres(self, show): """ Show/Hide genres @param bool """ self._show_genres = show self._list_one.clear() self._update_list_one(None) def destroy_current_view(self): """ Destroy current view """ view = self._stack.get_visible_child() for child in self._stack.get_children(): if child != view: self._stack.set_visible_child(child) self._stack.clean_old_views(child) break def disable_overlays(self): """ Disable overlays """ view = self._stack.get_visible_child() if view: view.disable_overlays() def update_view(self): """ Update current view """ view = self._stack.get_visible_child() if view: view.update_children() def reload_view(self): """ Reload current view """ values_two = self._list_two.get_selected_ids() if not values_two: values_one = self._list_one.get_selected_ids() if not values_one: values_one = [Type.POPULARS] self._list_one.select_ids([]) self._list_one.select_ids(values_one) if self._list_two.is_visible(): self._list_two.select_ids([]) self._list_two.select_ids(values_two) def pulse(self, pulse): """ Make progress bar visible/pulse if pulse is True @param pulse as bool """ if pulse and not self._progress.is_visible(): self._progress.show() if self._pulse_timeout is None: self._pulse_timeout = GLib.timeout_add(500, self._pulse) else: if self._pulse_timeout is not None: GLib.source_remove(self._pulse_timeout) self._pulse_timeout = None self._progress.hide() def on_scan_finished(self, scanner): """ Mark force scan as False, update lists @param scanner as CollectionScanner """ self._update_lists(scanner) def add_fake_phone(self): """ Emulate an Android Phone """ self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = "Android phone" dev.uri = "file:///tmp/android/" d = Gio.File.new_for_uri(dev.uri+"Internal Memory") if not d.query_exists(None): d.make_directory_with_parents(None) d = Gio.File.new_for_uri(dev.uri+"SD Card") if not d.query_exists(None): d.make_directory_with_parents(None) self._devices[self._devices_index] = dev ############ # Private # ############ def _pulse(self): """ Make progress bar pulse while visible @param pulse as bool """ if self._progress.is_visible() and not Lp().scanner.is_locked(): self._progress.pulse() return True else: self._progress.set_fraction(0.0) return False def _setup_view(self): """ Setup window main view: - genre list - artist list - main view as artist view or album view """ self._paned_main_list = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) self._paned_list_view = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self._list_one = SelectionList(SelectionMode.LIMITED) self._list_one.show() self._list_two = SelectionList(SelectionMode.NORMAL) self._list_one.connect('item-selected', self._on_list_one_selected) self._list_one.connect('populated', self._on_list_populated) self._list_two.connect('item-selected', self._on_list_two_selected) self._progress = Gtk.ProgressBar() self._progress.set_property('hexpand', True) vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() self._paned_list_view.add1(self._list_two) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self._list_one) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position( Lp().settings.get_value('paned-mainlist-width').get_int32()) self._paned_list_view.set_position( Lp().settings.get_value('paned-listview-width').get_int32()) self._paned_main_list.show() self._paned_list_view.show() def _get_saved_view_state(self): """ Get save view state @return (list one id, list two id) """ list_one_ids = [Type.POPULARS] list_two_ids = [Type.NONE] if Lp().settings.get_value('save-state'): list_one_ids = [] list_two_ids = [] ids = Lp().settings.get_value('list-one-ids') for i in ids: if isinstance(i, int): list_one_ids.append(i) ids = Lp().settings.get_value('list-two-ids') for i in ids: if isinstance(i, int): list_two_ids.append(i) return (list_one_ids, list_two_ids) def _add_genre(self, scanner, genre_id): """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int """ if self._show_genres: genre_name = Lp().genres.get_name(genre_id) self._list_one.add_value((genre_id, genre_name)) def _add_artist(self, scanner, artist_id, album_id): """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ artist_name = Lp().artists.get_name(artist_id) if self._show_genres: genre_ids = Lp().albums.get_genre_ids(album_id) genre_ids.append(Type.ALL) for i in self._list_one.get_selected_ids(): if i in genre_ids: self._list_two.add_value((artist_id, artist_name)) else: self._list_one.add_value((artist_id, artist_name)) def _setup_scanner(self): """ Run collection update if needed @return True if hard scan is running """ Lp().scanner.connect('scan-finished', self.on_scan_finished) Lp().scanner.connect('genre-added', self._add_genre) Lp().scanner.connect('artist-added', self._add_artist) def _update_playlists(self, playlists, playlist_id): """ Update playlists in second list @param playlists as Playlists @param playlist_id as int """ ids = self._list_one.get_selected_ids() if ids and ids[0] == Type.PLAYLISTS: if Lp().playlists.exists(playlist_id): self._list_two.update_value(playlist_id, Lp().playlists.get_name( playlist_id)) else: self._list_two.remove(playlist_id) def _update_lists(self, updater=None): """ Update lists @param updater as GObject """ self._update_list_one(updater) self._update_list_two(updater) def _update_list_one(self, updater): """ Update list one @param updater as GObject """ update = updater is not None if self._show_genres: self._setup_list_genres(self._list_one, update) else: self._setup_list_artists(self._list_one, [Type.ALL], update) def _update_list_two(self, updater): """ Update list two @param updater as GObject """ update = updater is not None ids = self._list_one.get_selected_ids() if ids and ids[0] == Type.PLAYLISTS: self._setup_list_playlists(update) elif self._show_genres and ids: self._setup_list_artists(self._list_two, ids, update) def _get_headers(self): """ Return list one headers """ items = [] items.append((Type.POPULARS, _("Popular albums"))) items.append((Type.RECENTS, _("Recently added albums"))) items.append((Type.RANDOMS, _("Random albums"))) items.append((Type.PLAYLISTS, _("Playlists"))) items.append((Type.RADIOS, _("Radios"))) if self._show_genres: items.append((Type.ALL, _("All artists"))) else: items.append((Type.ALL, _("All albums"))) return items def _setup_list_genres(self, selection_list, update): """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def load(): genres = Lp().genres.get() return genres def setup(genres): items = self._get_headers() items.append((Type.SEPARATOR, '')) items += genres selection_list.mark_as_artists(False) if update: selection_list.update_values(items) else: selection_list.populate(items) loader = Loader(target=load, view=selection_list, on_finished=setup) loader.start() def _setup_list_artists(self, selection_list, genre_ids, update): """ Setup list for artists @param list as SelectionList @param genre ids as [int] @param update as bool, if True, just update entries @thread safe """ def load(): artists = Lp().artists.get(genre_ids) compilations = Lp().albums.get_compilations(genre_ids) return (artists, compilations) def setup(artists, compilations): if selection_list == self._list_one: items = self._get_headers() items.append((Type.SEPARATOR, '')) else: items = [] if compilations: items.append((Type.COMPILATIONS, _("Compilations"))) items += artists selection_list.mark_as_artists(True) if update: selection_list.update_values(items) else: selection_list.populate(items) if selection_list == self._list_one: if self._list_two.is_visible(): self._list_two.hide() self._list_two_restore = Type.NONE loader = Loader(target=load, view=selection_list, on_finished=lambda r: setup(*r)) loader.start() def _setup_list_playlists(self, update): """ Setup list for playlists @param update as bool @thread safe """ playlists = [(Type.LOVED, Lp().playlists._LOVED)] playlists.append((Type.POPULARS, _("Popular tracks"))) playlists.append((Type.RECENTS, _("Recently played"))) playlists.append((Type.NEVER, _("Never played"))) playlists.append((Type.RANDOMS, _("Random tracks"))) playlists.append((Type.SEPARATOR, '')) playlists += Lp().playlists.get() if update: self._list_two.update_values(playlists) else: self._list_two.mark_as_artists(False) self._list_two.populate(playlists) def _update_view_device(self, device_id): """ Update current view with device view, Use existing view if available @param device id as int """ device = self._devices[device_id] child = self._stack.get_child_by_name(device.uri) if child is None: if DeviceView.get_files(device.uri): child = DeviceView(device, self._progress) self._stack.add_named(child, device.uri) else: child = DeviceLocked() self._stack.add(child) child.show() child.populate() self._stack.set_visible_child(child) self._stack.clean_old_views(child) def _update_view_artists(self, genre_ids, artist_ids): """ Update current view with artists view @param genre ids as [int] @param artist ids as [int] """ def load(): if genre_ids and genre_ids[0] == Type.ALL: albums = Lp().albums.get_ids(artist_ids, []) else: albums = [] if artist_ids and artist_ids[0] == Type.COMPILATIONS: albums += Lp().albums.get_compilations(genre_ids) albums += Lp().albums.get_ids(artist_ids, genre_ids) return albums view = ArtistView(artist_ids, genre_ids) loader = Loader(target=load, view=view) loader.start() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_albums(self, genre_ids, artist_ids): """ Update current view with albums view @param genre ids as [int] @param is compilation as bool """ def load(): albums = [] is_compilation = artist_ids and artist_ids[0] == Type.COMPILATIONS if genre_ids and genre_ids[0] == Type.ALL: if is_compilation or\ Lp().settings.get_value('show-compilations'): albums = Lp().albums.get_compilations() if not is_compilation: albums += Lp().albums.get_ids() elif genre_ids and genre_ids[0] == Type.POPULARS: albums = Lp().albums.get_populars() elif genre_ids and genre_ids[0] == Type.RECENTS: albums = Lp().albums.get_recents() elif genre_ids and genre_ids[0] == Type.RANDOMS: albums = Lp().albums.get_randoms() else: if is_compilation or\ Lp().settings.get_value('show-compilations'): albums = Lp().albums.get_compilations(genre_ids) if not is_compilation: albums += Lp().albums.get_ids([], genre_ids) return albums view = AlbumsView(genre_ids, artist_ids) loader = Loader(target=load, view=view) loader.start() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_playlists(self, playlist_ids=[]): """ Update current view with playlist view @param playlist ids as [int] """ def load(): 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) return track_ids view = None if playlist_ids: view = PlaylistsView(playlist_ids) loader = Loader(target=load, view=view) loader.start() else: view = PlaylistsManageView(Type.NONE, [], [], False) view.populate() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_radios(self): """ Update current view with radios view """ view = RadiosView() view.populate() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _add_device(self, mount, show=False): """ Add a device @param mount as Gio.Mount @param show as bool """ if mount.get_volume() is None: return name = mount.get_name() uri = mount.get_default_location().get_uri() # Add / to uri if needed if uri is not None and len(uri) > 1 and uri[-1:] != '/': uri += '/' if uri is not None and uri.find('mtp:') != -1: self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = name dev.uri = uri self._devices[self._devices_index] = dev if show: self._list_one.add_value((dev.id, dev.name)) def _remove_device(self, mount): """ Remove volume from device list @param mount as Gio.Mount """ if mount.get_volume() is None: return uri = mount.get_default_location().get_uri() for dev in self._devices.values(): if dev.uri == uri: self._list_one.remove(dev.id) child = self._stack.get_child_by_name(uri) if child is not None: child.destroy() del self._devices[dev.id] break def _on_list_one_selected(self, selection_list): """ Update view based on selected object @param list as SelectionList """ selected_ids = self._list_one.get_selected_ids() if not selected_ids: return if selected_ids[0] == Type.PLAYLISTS: self._list_two.clear() self._list_two.show() if not self._list_two.will_be_selected(): self._update_view_playlists() self._setup_list_playlists(False) elif Type.DEVICES - 999 < selected_ids[0] < Type.DEVICES: self._list_two.hide() if not self._list_two.will_be_selected(): self._update_view_device(selected_ids[0]) elif selected_ids[0] in [Type.POPULARS, Type.RECENTS, Type.RANDOMS]: self._list_two.hide() self._update_view_albums(selected_ids, []) elif selected_ids[0] == Type.RADIOS: self._list_two.hide() self._update_view_radios() elif selection_list.is_marked_as_artists(): self._list_two.hide() if selected_ids[0] == Type.ALL: self._update_view_albums(selected_ids, []) elif selected_ids[0] == Type.COMPILATIONS: self._update_view_albums([], selected_ids) else: self._update_view_artists([], selected_ids) else: self._list_two.clear() self._setup_list_artists(self._list_two, selected_ids, False) self._list_two.show() if not self._list_two.will_be_selected(): self._update_view_albums(selected_ids, []) def _on_list_populated(self, selection_list): """ Add device to list one and update db @param selection list as SelectionList """ for dev in self._devices.values(): self._list_one.add_value((dev.id, dev.name)) def _on_list_two_selected(self, selection_list): """ Update view based on selected object @param list as SelectionList """ genre_ids = self._list_one.get_selected_ids() selected_ids = self._list_two.get_selected_ids() if not selected_ids or not genre_ids: return if genre_ids[0] == Type.PLAYLISTS: self._update_view_playlists(selected_ids) elif selected_ids[0] == Type.COMPILATIONS: self._update_view_albums(genre_ids, selected_ids) else: self._update_view_artists(genre_ids, selected_ids) def _on_mount_added(self, vm, mount): """ On volume mounter @param vm as Gio.VolumeMonitor @param mount as Gio.Mount """ self._add_device(mount, True) def _on_mount_removed(self, vm, mount): """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mount as Gio.Mount """ self._remove_device(mount)
class Container: def __init__(self): # Try to update db on start, will be done after list one populating # finished self._need_to_update_db = Lp.settings.get_value('auto-update') or\ Lp.tracks.is_empty() # Index will start at -VOLUMES self._devices = {} self._devices_index = Type.DEVICES self._show_genres = Lp.settings.get_value('show-genres') self._stack = ViewContainer(500) self._stack.show() self._setup_view() self._setup_scanner() (list_one_id, list_two_id) = self._get_saved_view_state() self._list_one.select_id(list_one_id) self._list_two.select_id(list_two_id) # Volume manager self._vm = Gio.VolumeMonitor.get() self._vm.connect('mount-added', self._on_mount_added) self._vm.connect('mount-removed', self._on_mount_removed) Lp.playlists.connect('playlists-changed', self._update_lists) """ Update db at startup only if needed """ def update_db(self): # Stop previous scan if Lp.scanner.is_locked(): Lp.scanner.stop() GLib.timeout_add(250, self.update_db) else: # Something (device manager) is using progress bar progress = None if not self._progress.is_visible(): progress = self._progress Lp.scanner.update(progress) """ Return current selected genre @return genre id as int """ def get_genre_id(self): if self._show_genres: return self._list_one.get_selected_id() else: return None """ Init list one """ def init_list_one(self): self._update_list_one(None) """ Save view state """ def save_view_state(self): Lp.settings.set_value("list-one", GLib.Variant('i', self._list_one.get_selected_id())) Lp.settings.set_value("list-two", GLib.Variant('i', self._list_two.get_selected_id())) """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre id as int @param is_album as bool """ def show_playlist_manager(self, object_id, genre_id, is_album): view = PlaylistsManageView(object_id, genre_id, is_album, self._stack.get_allocated_width()/2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist name as str """ def show_playlist_editor(self, playlist_name): view = PlaylistEditView(playlist_name, self._stack.get_allocated_width()/2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) """ Get main widget @return Gtk.HPaned """ def main_widget(self): return self._paned_main_list """ Stop current view from processing """ def stop_all(self): view = self._stack.get_visible_child() if view is not None: self._stack.clean_old_views(None) """ Show/Hide genres @param bool """ def show_genres(self, show): self._show_genres = show self._list_one.clear() self._update_list_one(None) """ Destroy current view """ def destroy_current_view(self): view = self._stack.get_visible_child() for child in self._stack.get_children(): if child != view: self._stack.set_visible_child(child) self._stack.clean_old_views(child) break """ Update current view """ def update_view(self): view = self._stack.get_visible_child() if view: start_new_thread(view.update_covers, ()) """ Mark force scan as False, update lists @param scanner as CollectionScanner """ def on_scan_finished(self, scanner): if self._list_one.is_populating() or self._list_two.is_populating(): GLib.timeout_add(500, self.on_scan_finished, scanner) else: self._update_lists(scanner) ############ # Private # ############ """ Setup window main view: - genre list - artist list - main view as artist view or album view """ def _setup_view(self): self._paned_main_list = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) self._paned_list_view = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self._list_one = SelectionList() self._list_one.show() self._list_two = SelectionList() self._list_one.connect('item-selected', self._on_list_one_selected) self._list_one.connect('populated', self._on_list_populated) self._list_two.connect('item-selected', self._on_list_two_selected) self._progress = Gtk.ProgressBar() self._progress.set_property('hexpand', True) vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() separator = Gtk.Separator() separator.show() self._paned_list_view.add1(self._list_two) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self._list_one) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position( Lp.settings.get_value('paned-mainlist-width').get_int32()) self._paned_list_view.set_position( Lp.settings.get_value('paned-listview-width').get_int32()) self._paned_main_list.show() self._paned_list_view.show() """ Get save view state @return (list one id, list two id) """ def _get_saved_view_state(self): list_one_id = Type.POPULARS list_two_id = Type.NONE if Lp.settings.get_value('save-state'): position = Lp.settings.get_value('list-one').get_int32() if position != -1: list_one_id = position position = Lp.settings.get_value('list-two').get_int32() if position != -1: list_two_id = position return (list_one_id, list_two_id) """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int """ def _add_genre(self, scanner, genre_id): if self._show_genres: genre_name = Lp.genres.get_name(genre_id) self._list_one.add_value((genre_id, genre_name)) """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ def _add_artist(self, scanner, artist_id, album_id): artist_name = Lp.artists.get_name(artist_id) if self._show_genres: genre_ids = Lp.albums.get_genre_ids(album_id) genre_ids.append(Type.ALL) if self._list_one.get_selected_id() in genre_ids: self._list_two.add_value((artist_id, artist_name)) else: self._list_one.add_value((artist_id, artist_name)) """ Run collection update if needed @return True if hard scan is running """ def _setup_scanner(self): Lp.scanner.connect('scan-finished', self.on_scan_finished) Lp.scanner.connect('genre-update', self._add_genre) Lp.scanner.connect('artist-update', self._add_artist) """ Update lists @param updater as GObject """ def _update_lists(self, updater=None): self._update_list_one(updater) self._update_list_two(updater) """ Update list one @param updater as GObject """ def _update_list_one(self, updater): update = updater is not None # Do not update if updater is PlaylistsManager if not isinstance(updater, PlaylistsManager): if self._show_genres: self._setup_list_genres(self._list_one, update) else: self._setup_list_artists(self._list_one, Type.ALL, update) """ Update list two @param updater as GObject """ def _update_list_two(self, updater): update = updater is not None object_id = self._list_one.get_selected_id() if object_id == Type.PLAYLISTS: start_new_thread(self._setup_list_playlists, (update,)) elif self._show_genres and object_id != Type.NONE: self._setup_list_artists(self._list_two, object_id, update) """ Return list one headers """ def _get_headers(self): items = [] items.append((Type.POPULARS, _("Popular albums"))) items.append((Type.RECENTS, _("Recent albums"))) items.append((Type.RANDOMS, _("Random albums"))) items.append((Type.PLAYLISTS, _("Playlists"))) items.append((Type.RADIOS, _("Radios"))) if self._show_genres: items.append((Type.ALL, _("All artists"))) else: items.append((Type.ALL, _("All albums"))) return items """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_genres(self, selection_list, update): sql = Lp.db.get_cursor() selection_list.mark_as_artists(False) items = self._get_headers() items.append((Type.SEPARATOR, '')) items += Lp.genres.get(sql) if update: selection_list.update_values(items) else: selection_list.populate(items) sql.close() """ Hide list two base on current artist list """ def _pre_setup_list_artists(self, selection_list): if selection_list == self._list_one: if self._list_two.is_visible(): self._list_two.hide() self._list_two_restore = Type.NONE """ Setup list for artists @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_artists(self, selection_list, genre_id, update): GLib.idle_add(self._pre_setup_list_artists, selection_list) sql = Lp.db.get_cursor() items = [] selection_list.mark_as_artists(True) if selection_list == self._list_one: items = self._get_headers() if Lp.albums.get_compilations(genre_id, sql): items.append((Type.COMPILATIONS, _("Compilations"))) items.append((Type.SEPARATOR, '')) items += Lp.artists.get(genre_id, sql) if update: selection_list.update_values(items) else: selection_list.populate(items) sql.close() """ Setup list for playlists @param update as bool @thread safe """ def _setup_list_playlists(self, update): playlists = Lp.playlists.get() if update: self._list_two.update_values(playlists) else: self._list_two.mark_as_artists(False) self._list_two.populate(playlists) """ Update current view with device view, Use existing view if available @param device id as int """ def _update_view_device(self, device_id): device = self._devices[device_id] if device and device.view: view = device.view else: view = DeviceView(device, self._progress, self._stack.get_allocated_width()/2) device.view = view view.show() start_new_thread(view.populate, ()) self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with artists view @param artist id as int @param genre id as int """ def _update_view_artists(self, artist_id, genre_id): view = ArtistView(artist_id, genre_id) self._stack.add(view) view.show() start_new_thread(view.populate, ()) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with albums view @param genre id as int @param is compilation as bool """ def _update_view_albums(self, genre_id, is_compilation=False): view = AlbumsView(genre_id, is_compilation) self._stack.add(view) view.show() start_new_thread(view.populate, ()) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with playlist view @param playlist id as int """ def _update_view_playlists(self, playlist_id): view = None if playlist_id is not None: for (p_id, p_str) in Lp.playlists.get(): if p_id == playlist_id: view = PlaylistsView(p_str, self._stack) break else: view = PlaylistsManageView(-1, None, False, self._stack.get_allocated_width()/2) if view: view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) self._stack.clean_old_views(view) """ Update current view with radios view """ def _update_view_radios(self): view = RadiosView() self._stack.add(view) view.show() start_new_thread(view.populate, ()) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Add volume to device list @param volume as Gio.Volume """ def _add_device(self, volume): if volume is None: return root = volume.get_activation_root() if root is None: return uri = root.get_uri() # Just to be sure if uri is not None and len(uri) > 1 and uri[-1:] != '/': uri += '/' if uri is not None and uri.find('mtp:') != -1: self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = volume.get_name() dev.uri = uri self._devices[self._devices_index] = dev if not self._list_one.is_populating(): self._list_one.add_value((dev.id, dev.name)) """ Remove volume from device list @param volume as Gio.Volume """ def _remove_device(self, volume): if volume is None: return root = volume.get_activation_root() if root is None: return uri = root.get_uri() for dev in self._devices.values(): if dev.uri == uri: self._list_one.remove(dev.id) device = self._devices[dev.id] if device.view: device.view.destroy() del self._devices[dev.id] break """ Update view based on selected object @param list as SelectionList @param selected id as int """ def _on_list_one_selected(self, selection_list, selected_id): if selected_id == Type.PLAYLISTS: start_new_thread(self._setup_list_playlists, (False,)) self._list_two.clear() self._list_two.show() if not self._list_two.will_be_selected(): self._update_view_playlists(None) elif selected_id < Type.DEVICES: self._list_two.hide() if not self._list_two.will_be_selected(): self._update_view_device(selected_id) elif selected_id in [Type.POPULARS, Type.RECENTS, Type.RANDOMS]: self._list_two.hide() self._update_view_albums(selected_id) elif selected_id == Type.RADIOS: self._list_two.hide() self._update_view_radios() elif selection_list.is_marked_as_artists(): self._list_two.hide() if selected_id == Type.ALL: self._update_view_albums(selected_id) elif selected_id == Type.COMPILATIONS: self._update_view_albums(None, True) else: self._update_view_artists(selected_id, None) else: self._list_two.clear() start_new_thread(self._setup_list_artists, (self._list_two, selected_id, False)) self._list_two.show() if not self._list_two.will_be_selected(): self._update_view_albums(selected_id, False) """ Add device to list one and update db @param selection list as SelectionList """ def _on_list_populated(self, selection_list): if self._list_one.is_populating() or\ self._list_two.is_populating(): GLib.timeout_add(500, self._on_list_populated, selection_list) return for dev in self._devices.values(): self._list_one.add_value((dev.id, dev.name)) if self._need_to_update_db: self._need_to_update_db = False self.update_db() """ Update view based on selected object @param list as SelectionList @param selected id as int """ def _on_list_two_selected(self, selection_list, selected_id): genre_id = self._list_one.get_selected_id() if genre_id == Type.PLAYLISTS: self._update_view_playlists(selected_id) elif selected_id == Type.COMPILATIONS: self._update_view_albums(genre_id, True) else: self._update_view_artists(selected_id, genre_id) """ On volume mounter @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_added(self, vm, mnt): self._add_device(mnt.get_volume()) """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_removed(self, vm, mnt): self._remove_device(mnt.get_volume())
class Container: """ Container for main view """ def __init__(self): """ Init container """ self._pulse_timeout = None # Index will start at -VOLUMES self._devices = {} self._devices_index = Type.DEVICES self._show_genres = Lp().settings.get_value('show-genres') self._stack = ViewContainer(500) self._stack.show() self._setup_view() self._setup_scanner() (list_one_ids, list_two_ids) = self._get_saved_view_state() if list_one_ids and list_one_ids[0] != Type.NONE: self._list_one.select_ids(list_one_ids) if list_two_ids and list_two_ids[0] != Type.NONE: self._list_two.select_ids(list_two_ids) # Volume manager self._vm = Gio.VolumeMonitor.get() self._vm.connect('mount-added', self._on_mount_added) self._vm.connect('mount-removed', self._on_mount_removed) Lp().playlists.connect('playlists-changed', self._update_playlists) def update_db(self): """ Update db at startup only if needed """ # Stop previous scan if Lp().scanner.is_locked(): Lp().scanner.stop() GLib.timeout_add(250, self.update_db) else: # Something (device manager) is using progress bar progress = None if not self._progress.is_visible(): progress = self._progress Lp().scanner.update(progress) def get_genre_id(self): """ Return current selected genre @return genre id as int """ if self._show_genres: return self._list_one.get_selected_id() else: return None def init_list_one(self): """ Init list one """ self._update_list_one(None) def save_view_state(self): """ Save view state """ Lp().settings.set_value( "list-one-ids", GLib.Variant('ai', self._list_one.get_selected_ids())) Lp().settings.set_value( "list-two-ids", GLib.Variant('ai', self._list_two.get_selected_ids())) def show_playlist_manager(self, object_id, genre_id, is_album): """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre id as int @param is_album as bool """ view = PlaylistsManageView(object_id, genre_id, is_album) view.populate() view.show() self._stack.add(view) self._stack.set_visible_child(view) def show_playlist_editor(self, playlist_id): """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist id as int @param playlist name as str """ view = PlaylistEditView(playlist_id) view.show() self._stack.add(view) self._stack.set_visible_child(view) view.populate() def main_widget(self): """ Get main widget @return Gtk.HPaned """ return self._paned_main_list def stop_all(self): """ Stop current view from processing """ view = self._stack.get_visible_child() if view is not None: self._stack.clean_old_views(None) def show_genres(self, show): """ Show/Hide genres @param bool """ self._show_genres = show self._list_one.clear() self._update_list_one(None) def destroy_current_view(self): """ Destroy current view """ view = self._stack.get_visible_child() for child in self._stack.get_children(): if child != view: self._stack.set_visible_child(child) self._stack.clean_old_views(child) break def update_view(self): """ Update current view """ view = self._stack.get_visible_child() if view: view.update_children() def reload_view(self): """ Reload current view """ if self._list_two.is_visible(): values = self._list_two.get_selected_ids() self._list_two.select_ids(values) else: values = self._list_one.get_selected_ids() self._list_one.select_ids(values) def pulse(self, pulse): """ Make progress bar visible/pulse if pulse is True @param pulse as bool """ if pulse: self._progress.show() if self._pulse_timeout is None: self._pulse_timeout = GLib.timeout_add(500, self._pulse) else: if self._pulse_timeout is not None: GLib.source_remove(self._pulse_timeout) self._pulse_timeout = None self._progress.hide() def on_scan_finished(self, scanner): """ Mark force scan as False, update lists @param scanner as CollectionScanner """ self._update_lists(scanner) def add_fake_phone(self): """ Emulate an Android Phone """ self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = "Android phone" dev.uri = "file:///tmp/android/" d = Gio.File.new_for_uri(dev.uri + "Internal Memory") if not d.query_exists(None): d.make_directory_with_parents(None) d = Gio.File.new_for_uri(dev.uri + "SD Card") if not d.query_exists(None): d.make_directory_with_parents(None) self._devices[self._devices_index] = dev ############ # Private # ############ def _pulse(self): """ Make progress bar pulse while visible @param pulse as bool """ if self._progress.is_visible() and not Lp().scanner.is_locked(): self._progress.pulse() return True else: self._progress.set_fraction(0.0) return False def _setup_view(self): """ Setup window main view: - genre list - artist list - main view as artist view or album view """ self._paned_main_list = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) self._paned_list_view = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self._list_one = SelectionList() self._list_one.show() self._list_two = SelectionList() self._list_one.connect('item-selected', self._on_list_one_selected) self._list_one.connect('populated', self._on_list_populated) self._list_two.connect('item-selected', self._on_list_two_selected) self._progress = Gtk.ProgressBar() self._progress.set_property('hexpand', True) vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() self._paned_list_view.add1(self._list_two) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self._list_one) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position( Lp().settings.get_value('paned-mainlist-width').get_int32()) self._paned_list_view.set_position( Lp().settings.get_value('paned-listview-width').get_int32()) self._paned_main_list.show() self._paned_list_view.show() def _get_saved_view_state(self): """ Get save view state @return (list one id, list two id) """ list_one_ids = [Type.POPULARS] list_two_ids = [Type.NONE] if Lp().settings.get_value('save-state'): list_one_ids = [] list_two_ids = [] ids = Lp().settings.get_value('list-one-ids') for i in ids: if isinstance(i, int): list_one_ids.append(i) ids = Lp().settings.get_value('list-two-ids') for i in ids: if isinstance(i, int): list_two_ids.append(i) return (list_one_ids, list_two_ids) def _add_genre(self, scanner, genre_id): """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int """ if self._show_genres: genre_name = Lp().genres.get_name(genre_id) self._list_one.add_value((genre_id, genre_name)) def _add_artist(self, scanner, artist_id, album_id): """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ artist_name = Lp().artists.get_name(artist_id) if self._show_genres: genre_ids = Lp().albums.get_genre_ids(album_id) genre_ids.append(Type.ALL) for i in self._list_one.get_selected_ids(): if i in genre_ids: self._list_two.add_value((artist_id, artist_name)) else: self._list_one.add_value((artist_id, artist_name)) def _setup_scanner(self): """ Run collection update if needed @return True if hard scan is running """ Lp().scanner.connect('scan-finished', self.on_scan_finished) Lp().scanner.connect('genre-update', self._add_genre) Lp().scanner.connect('artist-update', self._add_artist) def _update_playlists(self, playlists, playlist_id): """ Update playlists in second list @param playlists as Playlists @param playlist_id as int """ ids = self._list_one.get_selected_ids() if ids and ids[0] == Type.PLAYLISTS: if Lp().playlists.exists(playlist_id): self._list_two.update_value( playlist_id, Lp().playlists.get_name(playlist_id)) else: self._list_two.remove(playlist_id) def _update_lists(self, updater=None): """ Update lists @param updater as GObject """ self._update_list_one(updater) self._update_list_two(updater) def _update_list_one(self, updater): """ Update list one @param updater as GObject """ update = updater is not None if self._show_genres: self._setup_list_genres(self._list_one, update) else: self._setup_list_artists(self._list_one, [Type.ALL], update) def _update_list_two(self, updater): """ Update list two @param updater as GObject """ update = updater is not None ids = self._list_one.get_selected_ids() if ids and ids[0] == Type.PLAYLISTS: self._setup_list_playlists(update) elif self._show_genres and ids: self._setup_list_artists(self._list_two, ids, update) def _get_headers(self): """ Return list one headers """ items = [] items.append((Type.POPULARS, _("Popular albums"))) items.append((Type.RECENTS, _("Recently added albums"))) items.append((Type.RANDOMS, _("Random albums"))) items.append((Type.PLAYLISTS, _("Playlists"))) items.append((Type.RADIOS, _("Radios"))) if self._show_genres: items.append((Type.ALL, _("All artists"))) else: items.append((Type.ALL, _("All albums"))) return items def _setup_list_genres(self, selection_list, update): """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def load(): genres = Lp().genres.get() return genres def setup(genres): items = self._get_headers() items.append((Type.SEPARATOR, '')) items += genres selection_list.mark_as_artists(False) if update: selection_list.update_values(items) else: selection_list.populate(items) loader = Loader(target=load, view=selection_list, on_finished=setup) loader.start() def _setup_list_artists(self, selection_list, genre_ids, update): """ Setup list for artists @param list as SelectionList @param genre ids as [int] @param update as bool, if True, just update entries @thread safe """ def load(): artists = Lp().artists.get(genre_ids) compilations = Lp().albums.get_compilations(genre_ids) return (artists, compilations) def setup(artists, compilations): if selection_list == self._list_one: items = self._get_headers() items.append((Type.SEPARATOR, '')) else: items = [] if compilations: items.append((Type.COMPILATIONS, _("Compilations"))) items += artists selection_list.mark_as_artists(True) if update: selection_list.update_values(items) else: selection_list.populate(items) if selection_list == self._list_one: if self._list_two.is_visible(): self._list_two.hide() self._list_two_restore = Type.NONE loader = Loader(target=load, view=selection_list, on_finished=lambda r: setup(*r)) loader.start() def _setup_list_playlists(self, update): """ Setup list for playlists @param update as bool @thread safe """ playlists = [(Type.LOVED, Lp().playlists._LOVED)] playlists.append((Type.POPULARS, _("Popular tracks"))) playlists.append((Type.RECENTS, _("Recently played"))) playlists.append((Type.NEVER, _("Never played"))) playlists.append((Type.RANDOMS, _("Random tracks"))) playlists.append((Type.MPD, _("Network control"))) playlists.append((Type.SEPARATOR, '')) playlists += Lp().playlists.get() if update: self._list_two.update_values(playlists) else: self._list_two.mark_as_artists(False) self._list_two.populate(playlists) def _update_view_device(self, device_id): """ Update current view with device view, Use existing view if available @param device id as int """ device = self._devices[device_id] child = self._stack.get_child_by_name(device.uri) if child is None: if DeviceView.get_files(device.uri): child = DeviceView(device, self._progress) self._stack.add_named(child, device.uri) else: child = DeviceLocked() self._stack.add(child) child.show() child.populate() self._stack.set_visible_child(child) self._stack.clean_old_views(child) def _update_view_artists(self, artist_ids, genre_ids): """ Update current view with artists view @param artist id as int @param genre id as int """ def load(): if artist_ids and artist_ids[0] == Type.COMPILATIONS: albums = Lp().albums.get_compilations(genre_ids) elif genre_ids and genre_ids[0] == Type.ALL: albums = Lp().albums.get_ids(artist_ids, []) else: albums = Lp().albums.get_ids(artist_ids, genre_ids) return albums view = ArtistView(artist_ids, genre_ids) loader = Loader(target=load, view=view) loader.start() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_albums(self, genre_ids, is_compilation=False): """ Update current view with albums view @param genre ids as [int] @param is compilation as bool """ def load(): albums = [] if genre_ids and genre_ids[0] == Type.ALL: if is_compilation: albums = Lp().albums.get_compilations() else: if Lp().settings.get_value('show-compilations'): albums = Lp().albums.get_compilations() albums += Lp().albums.get_ids() elif genre_ids and genre_ids[0] == Type.POPULARS: albums = Lp().albums.get_populars() elif genre_ids and genre_ids[0] == Type.RECENTS: albums = Lp().albums.get_recents() elif genre_ids and genre_ids[0] == Type.RANDOMS: albums = Lp().albums.get_randoms() elif is_compilation: albums = Lp().albums.get_compilations(genre_ids) else: if Lp().settings.get_value('show-compilations'): albums = Lp().albums.get_compilations(genre_ids) albums += Lp().albums.get_ids([], genre_ids) return albums view = AlbumsView(genre_ids, is_compilation) loader = Loader(target=load, view=view) loader.start() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_playlists(self, playlist_id): """ Update current view with playlist view @param playlist id as int """ def load(): if playlist_id == Lp().player.get_user_playlist_id(): tracks = [t.id for t in Lp().player.get_user_playlist()] elif 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) return tracks view = None if playlist_id is not None: view = PlaylistView(playlist_id) else: view = PlaylistsManageView(Type.NONE, None, False) if view: # Management or user playlist if playlist_id is None: view.populate() else: loader = Loader(target=load, view=view) loader.start() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_radios(self): """ Update current view with radios view """ view = RadiosView() view.populate() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _add_device(self, volume): """ Add volume to device list @param volume as Gio.Volume """ if volume is None: return root = volume.get_activation_root() if root is None: return uri = root.get_uri() # Just to be sure if uri is not None and len(uri) > 1 and uri[-1:] != '/': uri += '/' if uri is not None and uri.find('mtp:') != -1: self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = volume.get_name() dev.uri = uri self._devices[self._devices_index] = dev self._list_one.add_value((dev.id, dev.name)) def _remove_device(self, volume): """ Remove volume from device list @param volume as Gio.Volume """ if volume is None: return root = volume.get_activation_root() if root is None: return uri = root.get_uri() for dev in self._devices.values(): if dev.uri == uri: self._list_one.remove(dev.id) child = self._stack.get_child_by_name(uri) if child is not None: child.destroy() del self._devices[dev.id] break def _on_list_one_selected(self, selection_list): """ Update view based on selected object @param list as SelectionList """ selected_ids = self._list_one.get_selected_ids() if not selected_ids: return if selected_ids[0] == Type.PLAYLISTS: self._list_two.clear() self._list_two.set_mode(Gtk.SelectionMode.SINGLE) self._list_two.show() if not self._list_two.will_be_selected(): self._update_view_playlists(None) self._setup_list_playlists(False) elif Type.DEVICES - 999 < selected_ids[0] < Type.DEVICES: self._list_two.hide() if not self._list_two.will_be_selected(): self._update_view_device(selected_ids[0]) elif selected_ids[0] in [Type.POPULARS, Type.RECENTS, Type.RANDOMS]: self._list_two.hide() self._update_view_albums(selected_ids) elif selected_ids[0] == Type.RADIOS: self._list_two.hide() self._update_view_radios() elif selection_list.is_marked_as_artists(): self._list_two.hide() if selected_ids[0] == Type.ALL: self._update_view_albums(selected_ids) elif selected_ids[0] == Type.COMPILATIONS: self._update_view_albums([], True) else: self._update_view_artists(selected_ids, []) else: self._list_two.clear() self._list_two.set_mode(Gtk.SelectionMode.MULTIPLE) self._setup_list_artists(self._list_two, selected_ids, False) self._list_two.show() if not self._list_two.will_be_selected(): self._update_view_albums(selected_ids, False) def _on_list_populated(self, selection_list): """ Add device to list one and update db @param selection list as SelectionList """ for dev in self._devices.values(): self._list_one.add_value((dev.id, dev.name)) def _on_list_two_selected(self, selection_list): """ Update view based on selected object @param list as SelectionList """ genre_ids = self._list_one.get_selected_ids() selected_ids = self._list_two.get_selected_ids() if not selected_ids or not genre_ids: return if genre_ids[0] == Type.PLAYLISTS: self._update_view_playlists(selected_ids[0]) elif selected_ids[0] == Type.COMPILATIONS: self._update_view_albums(genre_ids, True) else: self._update_view_artists(selected_ids, genre_ids) def _on_mount_added(self, vm, mnt): """ On volume mounter @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ self._add_device(mnt.get_volume()) def _on_mount_removed(self, vm, mnt): """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ self._remove_device(mnt.get_volume())
class Container: """ Container for main view """ def __init__(self): """ Init container """ self.__pulse_timeout = None # Index will start at -VOLUMES self.__devices = {} self.__devices_index = Type.DEVICES self.__show_genres = Lp().settings.get_value('show-genres') self.__stack = ViewContainer(500) self.__stack.show() self.__setup_view() self.__setup_scanner() (list_one_ids, list_two_ids) = self.__get_saved_view_state() if list_one_ids and list_one_ids[0] != Type.NONE: self.__list_one.select_ids(list_one_ids) if list_two_ids and list_two_ids[0] != Type.NONE: self.__list_two.select_ids(list_two_ids) # Volume manager self.__vm = Gio.VolumeMonitor.get() self.__vm.connect('mount-added', self.__on_mount_added) self.__vm.connect('mount-removed', self.__on_mount_removed) for mount in self.__vm.get_mounts(): self.__add_device(mount, False) Lp().playlists.connect('playlists-changed', self.__update_playlists) def update_db(self): """ Update db at startup only if needed """ # Stop previous scan if Lp().scanner.is_locked(): Lp().scanner.stop() GLib.timeout_add(250, self.update_db) else: Lp().scanner.update() def get_genre_id(self): """ Return current selected genre @return genre id as int """ if self.__show_genres: return self.__list_one.get_selected_id() else: return None def init_list_one(self): """ Init list one """ self.__update_list_one(None) def save_view_state(self): """ Save view state """ Lp().settings.set_value( "list-one-ids", GLib.Variant('ai', self.__list_one.selected_ids)) Lp().settings.set_value( "list-two-ids", GLib.Variant('ai', self.__list_two.selected_ids)) def show_playlist_manager(self, object_id, genre_ids, artist_ids, is_album): """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre ids as [int] @param artist ids as [int] @param is_album as bool """ from lollypop.view_playlists import PlaylistsManageView current = self.__stack.get_visible_child() view = PlaylistsManageView(object_id, genre_ids, artist_ids, is_album) view.populate() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) current.disable_overlay() def show_playlist_editor(self, playlist_id): """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist id as int @param playlist name as str """ from lollypop.view_playlists import PlaylistEditView view = PlaylistEditView(playlist_id) view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) view.populate() def get_view_width(self): """ Return view width @return width as int """ return self.__stack.get_allocation().width def stop_all(self): """ Stop current view from processing """ view = self.__stack.get_visible_child() if view is not None: self.__stack.clean_old_views(None) def show_genres(self, show): """ Show/Hide genres @param bool """ self.__show_genres = show self.__list_one.clear() self.__list_two.clear() self.__list_two.hide() self.__update_list_one(None) self.__list_one.select_ids([Type.POPULARS]) def destroy_current_view(self): """ Destroy current view """ view = self.__stack.get_visible_child() for child in self.__stack.get_children(): if child != view: self.__stack.set_visible_child(child) self.__stack.clean_old_views(child) break @property def view(self): """ Disable overlays """ return self.__stack.get_visible_child() @property def progress(self): """ Progress bar @return ProgressBar """ return self.__progress def add_remove_from(self, value, list_one, add): """ Add or remove value to list @param value as (int, str) @param list one as bool @param add as bool """ if list_one: l = self.__list_one else: l = self.__list_two if add: l.add_value(value) else: l.remove_value(value[0]) def reload_view(self): """ Reload current view """ values_two = self.__list_two.selected_ids values_one = self.__list_one.selected_ids if not values_one: values_one = [Type.POPULARS] self.__list_one.select_ids([]) self.__list_one.clear() self.__update_list_one(None) self.__list_one.select_ids(values_one) if self.__list_two.is_visible(): self.__list_two.select_ids([]) self.__list_two.clear() self.__update_list_two(None) self.__list_two.select_ids(values_two) def pulse(self, pulse): """ Make progress bar visible/pulse if pulse is True @param pulse as bool """ if pulse and not self.__progress.is_visible(): self.__progress.show() if self.__pulse_timeout is None: self.__pulse_timeout = GLib.timeout_add(500, self.__pulse) else: if self.__pulse_timeout is not None: GLib.source_remove(self.__pulse_timeout) self.__pulse_timeout = None self.__progress.hide() def on_scan_finished(self, scanner): """ Mark force scan as False, update lists @param scanner as CollectionScanner """ self.__update_lists(scanner) def add_fake_phone(self): """ Emulate an Android Phone """ self.__devices_index -= 1 dev = Device() dev.id = self.__devices_index dev.name = "Android phone" dev.uri = "file:///tmp/android/" d = Gio.File.new_for_uri(dev.uri + "Internal Memory") if not d.query_exists(): d.make_directory_with_parents() d = Gio.File.new_for_uri(dev.uri + "SD Card") if not d.query_exists(): d.make_directory_with_parents() self.__devices[self.__devices_index] = dev def show_artists_albums(self, artist_ids): """ Show albums from artists """ self.__update_view_artists([], artist_ids) GLib.idle_add(self.__list_two.hide) GLib.idle_add(self.__list_one.select_ids, []) ############ # Private # ############ def __pulse(self): """ Make progress bar pulse while visible @param pulse as bool """ if self.__progress.is_visible() and not Lp().scanner.is_locked(): self.__progress.pulse() return True else: self.__progress.set_fraction(0.0, self) return False def __setup_view(self): """ Setup window main view: - genre list - artist list - main view as artist view or album view """ self._paned_main_list = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) self._paned_list_view = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self.__list_one = SelectionList(True) self.__list_one.show() self.__list_two = SelectionList(False) self.__list_one.connect('item-selected', self.__on_list_one_selected) self.__list_one.connect('populated', self.__on_list_populated) self.__list_two.connect('item-selected', self.__on_list_two_selected) self.__progress = ProgressBar() self.__progress.set_property('hexpand', True) vgrid.add(self.__stack) vgrid.add(self.__progress) vgrid.show() self._paned_list_view.add1(self.__list_two) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self.__list_one) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position( Lp().settings.get_value('paned-mainlist-width').get_int32()) self._paned_list_view.set_position( Lp().settings.get_value('paned-listview-width').get_int32()) self._paned_main_list.show() self._paned_list_view.show() def __get_saved_view_state(self): """ Get save view state @return (list one id, list two id) """ list_one_ids = [Type.POPULARS] list_two_ids = [Type.NONE] if Lp().settings.get_value('save-state'): list_one_ids = [] list_two_ids = [] ids = Lp().settings.get_value('list-one-ids') for i in ids: if isinstance(i, int): list_one_ids.append(i) ids = Lp().settings.get_value('list-two-ids') for i in ids: if isinstance(i, int): list_two_ids.append(i) return (list_one_ids, list_two_ids) def __setup_scanner(self): """ Run collection update if needed @return True if hard scan is running """ Lp().scanner.connect('scan-finished', self.on_scan_finished) Lp().scanner.connect('genre-updated', self.__on_genre_updated) Lp().scanner.connect('artist-updated', self.__on_artist_updated) def __update_playlists(self, playlists, playlist_id): """ Update playlists in second list @param playlists as Playlists @param playlist_id as int """ ids = self.__list_one.selected_ids if ids and ids[0] == Type.PLAYLISTS: if Lp().playlists.exists(playlist_id): self.__list_two.update_value( playlist_id, Lp().playlists.get_name(playlist_id)) else: self.__list_two.remove_value(playlist_id) def __update_lists(self, updater=None): """ Update lists @param updater as GObject """ self.__update_list_one(updater) self.__update_list_two(updater) def __update_list_one(self, updater): """ Update list one @param updater as GObject """ update = updater is not None if self.__show_genres: self.__setup_list_genres(self.__list_one, update) else: self.__setup_list_artists(self.__list_one, [Type.ALL], update) def __update_list_two(self, updater): """ Update list two @param updater as GObject """ update = updater is not None ids = self.__list_one.selected_ids if ids and ids[0] == Type.PLAYLISTS: self.__setup_list_playlists(update) elif self.__show_genres and ids: self.__setup_list_artists(self.__list_two, ids, update) def __setup_list_genres(self, selection_list, update): """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def load(): genres = Lp().genres.get() return genres def setup(genres): items = selection_list.get_headers() items.append((Type.SEPARATOR, '')) items += genres selection_list.mark_as_artists(False) if update: selection_list.update_values(items) else: selection_list.populate(items) loader = Loader(target=load, view=selection_list, on_finished=setup) loader.start() def __setup_list_artists(self, selection_list, genre_ids, update): """ Setup list for artists @param list as SelectionList @param genre ids as [int] @param update as bool, if True, just update entries @thread safe """ def load(): artists = Lp().artists.get(genre_ids) compilations = Lp().albums.get_compilation_ids(genre_ids) return (artists, compilations) def setup(artists, compilations): if selection_list == self.__list_one: items = selection_list.get_headers() if not compilations: items.append((Type.SEPARATOR, '')) else: items = [] if compilations: items.append((Type.COMPILATIONS, _("Compilations"))) items.append((Type.SEPARATOR, '')) items += artists selection_list.mark_as_artists(True) if update: selection_list.update_values(items) else: selection_list.populate(items) if selection_list == self.__list_one: if self.__list_two.is_visible(): self.__list_two.hide() self.__list_two_restore = Type.NONE loader = Loader(target=load, view=selection_list, on_finished=lambda r: setup(*r)) loader.start() def __setup_list_playlists(self, update): """ Setup list for playlists @param update as bool @thread safe """ playlists = [(Type.LOVED, Lp().playlists.LOVED)] playlists.append((Type.POPULARS, _("Popular tracks"))) playlists.append((Type.RECENTS, _("Recently played"))) playlists.append((Type.NEVER, _("Never played"))) playlists.append((Type.RANDOMS, _("Random tracks"))) playlists.append((Type.SEPARATOR, '')) playlists += Lp().playlists.get() if update: self.__list_two.update_values(playlists) else: self.__list_two.mark_as_artists(False) self.__list_two.populate(playlists) def __stop_current_view(self): """ Stop current view """ child = self.__stack.get_visible_child() if child is not None: if hasattr(child, "stop"): child.stop() def __update_view_device(self, device_id): """ Update current view with device view, Use existing view if available @param device id as int """ from lollypop.view_device import DeviceView, DeviceLocked self.__stop_current_view() device = self.__devices[device_id] child = self.__stack.get_child_by_name(device.uri) if child is None: files = DeviceView.get_files(device.uri) if files: if child is None: child = DeviceView(device) self.__stack.add_named(child, device.uri) else: child = DeviceLocked() self.__stack.add(child) child.show() child.populate() self.__stack.set_visible_child(child) self.__stack.clean_old_views(child) def __update_view_artists(self, genre_ids, artist_ids): """ Update current view with artists view @param genre ids as [int] @param artist ids as [int] """ def load(): if genre_ids and genre_ids[0] == Type.ALL: albums = Lp().albums.get_ids(artist_ids, []) else: albums = [] if artist_ids and artist_ids[0] == Type.COMPILATIONS: albums += Lp().albums.get_compilation_ids(genre_ids) albums += Lp().albums.get_ids(artist_ids, genre_ids) return albums from lollypop.view_artist import ArtistView self.__stop_current_view() view = ArtistView(artist_ids, genre_ids) loader = Loader(target=load, view=view) loader.start() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) def __update_view_albums(self, genre_ids, artist_ids): """ Update current view with albums view @param genre ids as [int] @param is compilation as bool """ def load(): albums = [] is_compilation = artist_ids and artist_ids[0] == Type.COMPILATIONS if genre_ids and genre_ids[0] == Type.ALL: if is_compilation or\ Lp().settings.get_value('show-compilations'): albums = Lp().albums.get_compilation_ids() if not is_compilation: albums += Lp().albums.get_ids() elif genre_ids and genre_ids[0] == Type.POPULARS: albums = Lp().albums.get_populars() elif genre_ids and genre_ids[0] == Type.RECENTS: albums = Lp().albums.get_recents() elif genre_ids and genre_ids[0] == Type.RANDOMS: albums = Lp().albums.get_randoms() else: if is_compilation or\ Lp().settings.get_value('show-compilations'): albums = Lp().albums.get_compilation_ids(genre_ids) if not is_compilation: albums += Lp().albums.get_ids([], genre_ids) return albums from lollypop.view_albums import AlbumsView self.__stop_current_view() view = AlbumsView(genre_ids, artist_ids) loader = Loader(target=load, view=view) loader.start() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) def __update_view_playlists(self, playlist_ids=[]): """ Update current view with playlist view @param playlist ids as [int] """ def load(): 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) return track_ids self.__stop_current_view() view = None if playlist_ids: from lollypop.view_playlists import PlaylistsView view = PlaylistsView(playlist_ids) loader = Loader(target=load, view=view) loader.start() else: from lollypop.view_playlists import PlaylistsManageView view = PlaylistsManageView(Type.NONE, [], [], False) view.populate() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) def __update_view_radios(self): """ Update current view with radios view """ from lollypop.view_radios import RadiosView self.__stop_current_view() view = RadiosView() view.populate() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) def __add_device(self, mount, show=False): """ Add a device @param mount as Gio.Mount @param show as bool """ if mount.get_volume() is None: return name = mount.get_name() uri = mount.get_default_location().get_uri() if uri is not None and (mount.can_eject() or uri.startswith('mtp')): self.__devices_index -= 1 dev = Device() dev.id = self.__devices_index dev.name = name dev.uri = uri self.__devices[self.__devices_index] = dev if show: self.__list_one.add_value((dev.id, dev.name)) def __remove_device(self, mount): """ Remove volume from device list @param mount as Gio.Mount """ uri = mount.get_default_location().get_uri() for dev in self.__devices.values(): if dev.uri == uri: self.__list_one.remove_value(dev.id) child = self.__stack.get_child_by_name(uri) if child is not None: child.destroy() del self.__devices[dev.id] break def __on_list_one_selected(self, selection_list): """ Update view based on selected object @param list as SelectionList """ selected_ids = self.__list_one.selected_ids if not selected_ids: return self.__list_two.clear() if selected_ids[0] == Type.PLAYLISTS: self.__list_two.show() if not self.__list_two.will_be_selected(): self.__update_view_playlists() self.__setup_list_playlists(False) elif Type.DEVICES - 999 < selected_ids[0] < Type.DEVICES: self.__list_two.hide() if not self.__list_two.will_be_selected(): self.__update_view_device(selected_ids[0]) elif selected_ids[0] in [ Type.POPULARS, Type.RECENTS, Type.RANDOMS, Type.CHARTS ]: self.__list_two.hide() self.__update_view_albums(selected_ids, []) elif selected_ids[0] == Type.RADIOS: self.__list_two.hide() self.__update_view_radios() elif selection_list.is_marked_as_artists(): self.__list_two.hide() if selected_ids[0] == Type.ALL: self.__update_view_albums(selected_ids, []) elif selected_ids[0] == Type.COMPILATIONS: self.__update_view_albums([], selected_ids) else: self.__update_view_artists([], selected_ids) else: self.__setup_list_artists(self.__list_two, selected_ids, False) self.__list_two.show() if not self.__list_two.will_be_selected(): self.__update_view_albums(selected_ids, []) def __on_list_populated(self, selection_list): """ Add device to list one and update db @param selection list as SelectionList """ for dev in self.__devices.values(): self.__list_one.add_value((dev.id, dev.name)) def __on_list_two_selected(self, selection_list): """ Update view based on selected object @param list as SelectionList """ genre_ids = self.__list_one.selected_ids selected_ids = self.__list_two.selected_ids if not selected_ids or not genre_ids: return if genre_ids[0] == Type.PLAYLISTS: self.__update_view_playlists(selected_ids) elif selected_ids[0] == Type.COMPILATIONS: self.__update_view_albums(genre_ids, selected_ids) else: self.__update_view_artists(genre_ids, selected_ids) def __on_genre_updated(self, scanner, genre_id, add): """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int @param add as bool """ if self.__show_genres: if add: genre_name = Lp().genres.get_name(genre_id) self.__list_one.add_value((genre_id, genre_name)) else: genre_ids = Lp().genres.get_ids() if genre_id not in genre_ids: self.__list_one.remove_value(genre_id) def __on_artist_updated(self, scanner, artist_id, add): """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param add as bool """ artist_name = Lp().artists.get_name(artist_id) if self.__show_genres: l = self.__list_two artist_ids = Lp().artists.get_ids(self.__list_one.selected_ids) else: l = self.__list_one artist_ids = Lp().artists.get_ids() if add: if artist_id in artist_ids: l.add_value((artist_id, artist_name)) else: if artist_id not in artist_ids: l.remove_value(artist_id) def __on_mount_added(self, vm, mount): """ On volume mounter @param vm as Gio.VolumeMonitor @param mount as Gio.Mount """ self.__add_device(mount, True) def __on_mount_removed(self, vm, mount): """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mount as Gio.Mount """ self.__remove_device(mount)
class Container: def __init__(self): # Try to update db on start, will be done after list one poplating # finished self._need_to_update_db = True # Index will start at -VOLUMES self._devices = {} self._devices_index = Navigation.DEVICES self._show_genres = Objects.settings.get_value('show-genres') self._stack = ViewContainer(500) self._stack.show() self._setup_view() self._setup_scanner() self._list_one_restore = Navigation.POPULARS self._list_two_restore = Navigation.NONE if Objects.settings.get_value('save-state'): self._restore_view_state() # Volume manager self._vm = Gio.VolumeMonitor.get() self._vm.connect('mount-added', self._on_mount_added) self._vm.connect('mount-removed', self._on_mount_removed) Objects.playlists.connect("playlists-changed", self.update_lists) """ Update db at startup only if needed @param force as bool to force update (if possible) """ def update_db(self, force=False): # Stop previous scan if self._scanner.is_locked(): self._scanner.stop() GLib.timeout_add(250, self.update_db, force) # Something is using progress bar, do nothing elif not self._progress.is_visible(): if force: Objects.tracks.remove_outside() self._list_one_restore = self._list_one.get_selected_id() self._list_two_restore = self._list_two.get_selected_id() self.update_lists(True) self._scanner.update(False) elif Objects.tracks.is_empty(): self._scanner.update(False) elif Objects.settings.get_value('startup-scan'): self._scanner.update(True) """ Save view state """ def save_view_state(self): Objects.settings.set_value( "list-one", GLib.Variant('i', self._list_one.get_selected_id())) Objects.settings.set_value( "list-two", GLib.Variant('i', self._list_two.get_selected_id())) """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre id as int @param is_album as bool """ def show_playlist_manager(self, object_id, genre_id, is_album): view = PlaylistManageView(object_id, genre_id, is_album, self._stack.get_allocated_width() / 2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist name as str """ def show_playlist_editor(self, playlist_name): view = PlaylistEditView(playlist_name, self._stack.get_allocated_width() / 2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) """ Update lists @param updater as GObject """ def update_lists(self, updater=None): self._update_list_one(updater) self._update_list_two(updater) """ Load external files @param files as [Gio.Files] """ def load_external(self, files): self._need_to_update_db = False # We wait as selection list is threaded, # we don't want to insert item before populated # Same for locked db if self._list_one.is_populating() or\ self._list_one.is_populating() or\ self._scanner.is_locked(): self._scanner.stop() GLib.timeout_add(250, self.load_external, files) else: self._scanner.add(files) """ Get main widget @return Gtk.HPaned """ def main_widget(self): return self._paned_main_list """ Stop current view from processing """ def stop_all(self): self._scanner.stop() view = self._stack.get_visible_child() if view is not None: self._stack.clean_old_views(None) """ Show/Hide genres @param bool """ def show_genres(self, show): self._show_genres = show self._update_list_one(None) """ Destroy current view """ def destroy_current_view(self): view = self._stack.get_visible_child() for child in self._stack.get_children(): if child != view: self._stack.set_visible_child(child) self._stack.clean_old_views(child) break """ Update current view """ def update_view(self): view = self._stack.get_visible_child() if view: start_new_thread(view.update_covers, ()) ############ # Private # ############ """ Setup window main view: - genre list - artist list - main view as artist view or album view """ def _setup_view(self): self._paned_main_list = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) self._paned_list_view = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self._list_one = SelectionList() self._list_one.widget.show() self._list_two = SelectionList() self._list_one.connect('item-selected', self._on_list_one_selected) self._list_one.connect('populated', self._on_list_one_populated) self._list_two.connect('item-selected', self._on_list_two_selected) self._list_two.connect('populated', self._on_list_two_populated) self._progress = Gtk.ProgressBar() self._progress.set_property('hexpand', True) vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() separator = Gtk.Separator() separator.show() self._paned_list_view.add1(self._list_two.widget) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self._list_one.widget) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position( Objects.settings.get_value("paned-mainlist-width").get_int32()) self._paned_list_view.set_position( Objects.settings.get_value("paned-listview-width").get_int32()) self._paned_main_list.show() self._paned_list_view.show() """ Restore saved view """ def _restore_view_state(self): position = Objects.settings.get_value('list-one').get_int32() if position != -1: self._list_one_restore = position else: self._list_one_restore = Navigation.POPULARS position = Objects.settings.get_value('list-two').get_int32() if position != -1: self._list_two_restore = position """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int """ def _add_genre(self, scanner, genre_id): if self._show_genres: genre_name = Objects.genres.get_name(genre_id) self._list_one.add((genre_id, genre_name)) """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ def _add_artist(self, scanner, artist_id, album_id): artist_name = Objects.artists.get_name(artist_id) if self._show_genres: genre_ids = Objects.albums.get_genre_ids(album_id) genre_ids.append(Navigation.ALL) if self._list_one.get_selected_id() in genre_ids: self._list_two.add((artist_id, artist_name)) else: self._list_one.add((artist_id, artist_name)) """ Run collection update if needed @return True if hard scan is running """ def _setup_scanner(self): self._scanner = CollectionScanner(self._progress) self._scanner.connect("scan-finished", self._on_scan_finished) self._scanner.connect("genre-update", self._add_genre) self._scanner.connect("artist-update", self._add_artist) self._scanner.connect("added", self._play_track) """ Update list one @param updater as GObject """ def _update_list_one(self, updater): update = updater is not None # Do not update if updater is PlaylistsManager if not isinstance(updater, PlaylistsManager): if self._show_genres: self._setup_list_genres(self._list_one, update) else: self._setup_list_artists(self._list_one, Navigation.ALL, update) """ Update list two @param updater as GObject """ def _update_list_two(self, updater): update = updater is not None object_id = self._list_one.get_selected_id() if object_id == Navigation.PLAYLISTS: start_new_thread(self._setup_list_playlists, (update, )) elif self._show_genres and object_id != Navigation.NONE: self._setup_list_artists(self._list_two, object_id, update) """ Return list one headers """ def _get_headers(self): items = [] items.append((Navigation.POPULARS, _("Popular albums"))) items.append((Navigation.RECENTS, _("Recent albums"))) items.append((Navigation.PLAYLISTS, _("Playlists"))) if self._show_genres: items.append((Navigation.ALL, _("All artists"))) else: items.append((Navigation.ALL, _("All albums"))) return items """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_genres(self, selection_list, update): sql = Objects.db.get_cursor() selection_list.mark_as_artists(False) items = self._get_headers() + Objects.genres.get(sql) if update: selection_list.update(items) else: selection_list.populate(items) sql.close() """ Hide list two base on current artist list """ def _pre_setup_list_artists(self, selection_list): if selection_list == self._list_one: if self._list_two.widget.is_visible(): self._list_two.widget.hide() self._list_two_restore = Navigation.NONE """ Setup list for artists @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_artists(self, selection_list, genre_id, update): GLib.idle_add(self._pre_setup_list_artists, selection_list) sql = Objects.db.get_cursor() items = [] selection_list.mark_as_artists(True) if selection_list == self._list_one: items = self._get_headers() if Objects.albums.get_compilations(genre_id, sql): items.append((Navigation.COMPILATIONS, _("Compilations"))) items += Objects.artists.get(genre_id, sql) if update: selection_list.update(items) else: selection_list.populate(items) sql.close() """ Setup list for playlists @param update as bool @thread safe """ def _setup_list_playlists(self, update): playlists = Objects.playlists.get() if update: self._list_two.update(playlists) else: self._list_two.mark_as_artists(False) self._list_two.populate(playlists) GLib.idle_add(self._update_view_playlists, None) """ Update current view with device view, Use existing view if available @param object id as int """ def _update_view_device(self, object_id): device = self._devices[object_id] if device and device.view: view = device.view else: view = DeviceView(device, self._progress, self._stack.get_allocated_width() / 2) device.view = view view.show() start_new_thread(view.populate, ()) self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with artists view @param object id as int @param genre id as int """ def _update_view_artists(self, object_id, genre_id): view = ArtistView(object_id, True) self._stack.add(view) view.show() start_new_thread(view.populate, (genre_id, )) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with albums view @param object id as int @param genre id as int """ def _update_view_albums(self, object_id, genre_id): view = AlbumView(object_id) self._stack.add(view) view.show() start_new_thread(view.populate, (genre_id, )) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with playlist view @param playlist id as int """ def _update_view_playlists(self, playlist_id): view = None if playlist_id is not None: for (p_id, p_str) in Objects.playlists.get(): if p_id == playlist_id: view = PlaylistView(p_str, self._stack) break else: view = PlaylistManageView(-1, None, False, self._stack.get_allocated_width() / 2) if view: view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) self._stack.clean_old_views(view) """ Add volume to device list @param volume as Gio.Volume """ def _add_device(self, volume): if volume is None: return root = volume.get_activation_root() if root is None: return path = root.get_path() if path and path.find('mtp:') != -1: self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = volume.get_name() dev.path = path self._devices[self._devices_index] = dev self._list_one.add_device(dev.name, dev.id) """ Remove volume from device list @param volume as Gio.Volume """ def _remove_device(self, volume): for dev in self._devices.values(): if not os.path.exists(dev.path): self._list_one.remove(dev.id) device = self._devices[dev.id] if device.view: device.view.destroy() del self._devices[dev.id] break """ Update view based on selected object @param list as SelectionList @param object id as int """ def _on_list_one_selected(self, selection_list, object_id): if object_id == Navigation.PLAYLISTS: start_new_thread(self._setup_list_playlists, (False, )) self._list_two.widget.show() elif object_id < Navigation.DEVICES: self._list_two.widget.hide() self._update_view_device(object_id) elif object_id in [Navigation.POPULARS, Navigation.RECENTS]: self._list_two.widget.hide() self._update_view_albums(object_id, None) elif selection_list.is_marked_as_artists(): self._list_two.widget.hide() if object_id == Navigation.ALL or\ object_id == Navigation.COMPILATIONS: self._update_view_albums(object_id, None) else: self._update_view_artists(object_id, None) else: start_new_thread(self._setup_list_artists, (self._list_two, object_id, False)) self._list_two.widget.show() if self._list_two_restore == Navigation.NONE: self._update_view_albums(object_id, None) """ Restore previous state @param selection list as SelectionList """ def _on_list_one_populated(self, selection_list): for dev in self._devices.values(): self._list_one.add_device(dev.name, dev.id) if self._list_one_restore is not None: self._list_one.select_id(self._list_one_restore) self._list_one_restore = None if self._need_to_update_db: self._need_to_update_db = False self.update_db() """ Update view based on selected object @param list as SelectionList @param object id as int """ def _on_list_two_selected(self, selection_list, object_id): selected_id = self._list_one.get_selected_id() if selected_id == Navigation.PLAYLISTS: self._update_view_playlists(object_id) elif object_id == Navigation.COMPILATIONS: self._update_view_albums(object_id, selected_id) else: self._update_view_artists(object_id, selected_id) """ Restore previous state @param selection list as SelectionList """ def _on_list_two_populated(self, selection_list): if self._list_two_restore != Navigation.NONE: self._list_two.select_id(self._list_two_restore) self._list_two_restore = Navigation.NONE """ Play tracks as user playlist @param scanner as collection scanner @param track id as int @param play as bool """ def _play_track(self, scanner, track_id, play): tracks = [track_id] if play: Objects.player.clear_user_playlist() if not Objects.player.is_party(): Objects.player.set_user_playlist(tracks, track_id) Objects.player.load(track_id) elif not Objects.player.is_party(): Objects.player.add_to_user_playlist(track_id) """ Mark force scan as False, update lists @param scanner as CollectionScanner """ def _on_scan_finished(self, scanner): if self._list_one.is_populating() or self._list_two.is_populating(): GLib.timeout_add(500, self._on_scan_finished, scanner) else: self.update_lists(scanner) """ On volume mounter @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_added(self, vm, mnt): self._add_device(mnt.get_volume()) """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_removed(self, vm, mnt): self._remove_device(mnt.get_volume())
class Container: def __init__(self): # Try to update db on start, will be done after list one poplating # finished self._need_to_update_db = True # Index will start at -VOLUMES self._devices = {} self._devices_index = Navigation.DEVICES self._show_genres = Objects.settings.get_value('show-genres') self._stack = ViewContainer(500) self._stack.show() self._setup_view() self._setup_scanner() self._list_one_restore = Navigation.POPULARS self._list_two_restore = Navigation.NONE if Objects.settings.get_value('save-state'): self._restore_view_state() # Volume manager self._vm = Gio.VolumeMonitor.get() self._vm.connect('mount-added', self._on_mount_added) self._vm.connect('mount-removed', self._on_mount_removed) Objects.playlists.connect("playlists-changed", self.update_lists) """ Update db at startup only if needed @param force as bool to force update (if possible) """ def update_db(self, force=False): # Stop previous scan if self._scanner.is_locked(): self._scanner.stop() GLib.timeout_add(250, self.update_db, force) # Something is using progress bar, do nothing elif not self._progress.is_visible(): if force: Objects.tracks.remove_outside() self._list_one_restore = self._list_one.get_selected_id() self._list_two_restore = self._list_two.get_selected_id() self.update_lists(True) self._scanner.update(False) elif Objects.tracks.is_empty(): self._scanner.update(False) elif Objects.settings.get_value('startup-scan'): self._scanner.update(True) """ Save view state """ def save_view_state(self): Objects.settings.set_value("list-one", GLib.Variant( 'i', self._list_one.get_selected_id())) Objects.settings.set_value("list-two", GLib.Variant( 'i', self._list_two.get_selected_id())) """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre id as int @param is_album as bool """ def show_playlist_manager(self, object_id, genre_id, is_album): view = PlaylistManageView(object_id, genre_id, is_album, self._stack.get_allocated_width()/2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist name as str """ def show_playlist_editor(self, playlist_name): view = PlaylistEditView(playlist_name, self._stack.get_allocated_width()/2) view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) """ Update lists @param updater as GObject """ def update_lists(self, updater=None): self._update_list_one(updater) self._update_list_two(updater) """ Load external files @param files as [Gio.Files] """ def load_external(self, files): self._need_to_update_db = False # We wait as selection list is threaded, # we don't want to insert item before populated # Same for locked db if self._list_one.is_populating() or\ self._list_one.is_populating() or\ self._scanner.is_locked(): self._scanner.stop() GLib.timeout_add(250, self.load_external, files) else: self._scanner.add(files) """ Get main widget @return Gtk.HPaned """ def main_widget(self): return self._paned_main_list """ Stop current view from processing """ def stop_all(self): self._scanner.stop() view = self._stack.get_visible_child() if view is not None: self._stack.clean_old_views(None) """ Show/Hide genres @param bool """ def show_genres(self, show): self._show_genres = show self._update_list_one(None) """ Destroy current view """ def destroy_current_view(self): view = self._stack.get_visible_child() for child in self._stack.get_children(): if child != view: self._stack.set_visible_child(child) self._stack.clean_old_views(child) break """ Update current view """ def update_view(self): view = self._stack.get_visible_child() if view: start_new_thread(view.update_covers, ()) ############ # Private # ############ """ Setup window main view: - genre list - artist list - main view as artist view or album view """ def _setup_view(self): self._paned_main_list = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) self._paned_list_view = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self._list_one = SelectionList() self._list_one.widget.show() self._list_two = SelectionList() self._list_one.connect('item-selected', self._on_list_one_selected) self._list_one.connect('populated', self._on_list_one_populated) self._list_two.connect('item-selected', self._on_list_two_selected) self._list_two.connect('populated', self._on_list_two_populated) self._progress = Gtk.ProgressBar() self._progress.set_property('hexpand', True) vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() separator = Gtk.Separator() separator.show() self._paned_list_view.add1(self._list_two.widget) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self._list_one.widget) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position( Objects.settings.get_value( "paned-mainlist-width").get_int32()) self._paned_list_view.set_position( Objects.settings.get_value( "paned-listview-width").get_int32()) self._paned_main_list.show() self._paned_list_view.show() """ Restore saved view """ def _restore_view_state(self): position = Objects.settings.get_value('list-one').get_int32() if position != -1: self._list_one_restore = position else: self._list_one_restore = Navigation.POPULARS position = Objects.settings.get_value('list-two').get_int32() if position != -1: self._list_two_restore = position """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int """ def _add_genre(self, scanner, genre_id): if self._show_genres: genre_name = Objects.genres.get_name(genre_id) self._list_one.add((genre_id, genre_name)) """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ def _add_artist(self, scanner, artist_id, album_id): artist_name = Objects.artists.get_name(artist_id) if self._show_genres: genre_ids = Objects.albums.get_genre_ids(album_id) genre_ids.append(Navigation.ALL) if self._list_one.get_selected_id() in genre_ids: self._list_two.add((artist_id, artist_name)) else: self._list_one.add((artist_id, artist_name)) """ Run collection update if needed @return True if hard scan is running """ def _setup_scanner(self): self._scanner = CollectionScanner(self._progress) self._scanner.connect("scan-finished", self._on_scan_finished) self._scanner.connect("genre-update", self._add_genre) self._scanner.connect("artist-update", self._add_artist) self._scanner.connect("added", self._play_track) """ Update list one @param updater as GObject """ def _update_list_one(self, updater): update = updater is not None # Do not update if updater is PlaylistsManager if not isinstance(updater, PlaylistsManager): if self._show_genres: self._setup_list_genres(self._list_one, update) else: self._setup_list_artists(self._list_one, Navigation.ALL, update) """ Update list two @param updater as GObject """ def _update_list_two(self, updater): update = updater is not None object_id = self._list_one.get_selected_id() if object_id == Navigation.PLAYLISTS: start_new_thread(self._setup_list_playlists, (update,)) elif self._show_genres and object_id != Navigation.NONE: self._setup_list_artists(self._list_two, object_id, update) """ Return list one headers """ def _get_headers(self): items = [] items.append((Navigation.POPULARS, _("Popular albums"))) items.append((Navigation.RECENTS, _("Recent albums"))) items.append((Navigation.PLAYLISTS, _("Playlists"))) if self._show_genres: items.append((Navigation.ALL, _("All artists"))) else: items.append((Navigation.ALL, _("All albums"))) return items """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_genres(self, selection_list, update): sql = Objects.db.get_cursor() selection_list.mark_as_artists(False) items = self._get_headers() + Objects.genres.get(sql) if update: selection_list.update(items) else: selection_list.populate(items) sql.close() """ Hide list two base on current artist list """ def _pre_setup_list_artists(self, selection_list): if selection_list == self._list_one: if self._list_two.widget.is_visible(): self._list_two.widget.hide() self._list_two_restore = Navigation.NONE """ Setup list for artists @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def _setup_list_artists(self, selection_list, genre_id, update): GLib.idle_add(self._pre_setup_list_artists, selection_list) sql = Objects.db.get_cursor() items = [] selection_list.mark_as_artists(True) if selection_list == self._list_one: items = self._get_headers() if Objects.albums.get_compilations(genre_id, sql): items.append((Navigation.COMPILATIONS, _("Compilations"))) items += Objects.artists.get(genre_id, sql) if update: selection_list.update(items) else: selection_list.populate(items) sql.close() """ Setup list for playlists @param update as bool @thread safe """ def _setup_list_playlists(self, update): playlists = Objects.playlists.get() if update: self._list_two.update(playlists) else: self._list_two.mark_as_artists(False) self._list_two.populate(playlists) GLib.idle_add(self._update_view_playlists, None) """ Update current view with device view, Use existing view if available @param object id as int """ def _update_view_device(self, object_id): device = self._devices[object_id] if device and device.view: view = device.view else: view = DeviceView(device, self._progress, self._stack.get_allocated_width()/2) device.view = view view.show() start_new_thread(view.populate, ()) self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with artists view @param object id as int @param genre id as int """ def _update_view_artists(self, object_id, genre_id): view = ArtistView(object_id, True) self._stack.add(view) view.show() start_new_thread(view.populate, (genre_id,)) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with albums view @param object id as int @param genre id as int """ def _update_view_albums(self, object_id, genre_id): view = AlbumView(object_id) self._stack.add(view) view.show() start_new_thread(view.populate, (genre_id,)) self._stack.set_visible_child(view) self._stack.clean_old_views(view) """ Update current view with playlist view @param playlist id as int """ def _update_view_playlists(self, playlist_id): view = None if playlist_id is not None: for (p_id, p_str) in Objects.playlists.get(): if p_id == playlist_id: view = PlaylistView(p_str, self._stack) break else: view = PlaylistManageView(-1, None, False, self._stack.get_allocated_width()/2) if view: view.show() self._stack.add(view) self._stack.set_visible_child(view) start_new_thread(view.populate, ()) self._stack.clean_old_views(view) """ Add volume to device list @param volume as Gio.Volume """ def _add_device(self, volume): if volume is None: return root = volume.get_activation_root() if root is None: return path = root.get_path() if path and path.find('mtp:') != -1: self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = volume.get_name() dev.path = path self._devices[self._devices_index] = dev self._list_one.add_device(dev.name, dev.id) """ Remove volume from device list @param volume as Gio.Volume """ def _remove_device(self, volume): for dev in self._devices.values(): if not os.path.exists(dev.path): self._list_one.remove(dev.id) device = self._devices[dev.id] if device.view: device.view.destroy() del self._devices[dev.id] break """ Update view based on selected object @param list as SelectionList @param object id as int """ def _on_list_one_selected(self, selection_list, object_id): if object_id == Navigation.PLAYLISTS: start_new_thread(self._setup_list_playlists, (False,)) self._list_two.widget.show() elif object_id < Navigation.DEVICES: self._list_two.widget.hide() self._update_view_device(object_id) elif object_id in [Navigation.POPULARS, Navigation.RECENTS]: self._list_two.widget.hide() self._update_view_albums(object_id, None) elif selection_list.is_marked_as_artists(): self._list_two.widget.hide() if object_id == Navigation.ALL or\ object_id == Navigation.COMPILATIONS: self._update_view_albums(object_id, None) else: self._update_view_artists(object_id, None) else: start_new_thread(self._setup_list_artists, (self._list_two, object_id, False)) self._list_two.widget.show() if self._list_two_restore == Navigation.NONE: self._update_view_albums(object_id, None) """ Restore previous state @param selection list as SelectionList """ def _on_list_one_populated(self, selection_list): for dev in self._devices.values(): self._list_one.add_device(dev.name, dev.id) if self._list_one_restore is not None: self._list_one.select_id(self._list_one_restore) self._list_one_restore = None if self._need_to_update_db: self._need_to_update_db = False self.update_db() """ Update view based on selected object @param list as SelectionList @param object id as int """ def _on_list_two_selected(self, selection_list, object_id): selected_id = self._list_one.get_selected_id() if selected_id == Navigation.PLAYLISTS: self._update_view_playlists(object_id) elif object_id == Navigation.COMPILATIONS: self._update_view_albums(object_id, selected_id) else: self._update_view_artists(object_id, selected_id) """ Restore previous state @param selection list as SelectionList """ def _on_list_two_populated(self, selection_list): if self._list_two_restore != Navigation.NONE: self._list_two.select_id(self._list_two_restore) self._list_two_restore = Navigation.NONE """ Play tracks as user playlist @param scanner as collection scanner @param track id as int @param play as bool """ def _play_track(self, scanner, track_id, play): tracks = [track_id] if play: Objects.player.clear_user_playlist() if not Objects.player.is_party(): Objects.player.set_user_playlist(tracks, track_id) Objects.player.load(track_id) elif not Objects.player.is_party(): Objects.player.add_to_user_playlist(track_id) """ Mark force scan as False, update lists @param scanner as CollectionScanner """ def _on_scan_finished(self, scanner): if self._list_one.is_populating() or self._list_two.is_populating(): GLib.timeout_add(500, self._on_scan_finished, scanner) else: self.update_lists(scanner) """ On volume mounter @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_added(self, vm, mnt): self._add_device(mnt.get_volume()) """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ def _on_mount_removed(self, vm, mnt): self._remove_device(mnt.get_volume())
class Container: """ Container for main view """ def __init__(self): """ Init container """ self.__pulse_timeout = None # Index will start at -VOLUMES self.__devices = {} self.__devices_index = Type.DEVICES self.__show_genres = Lp().settings.get_value('show-genres') self.__stack = ViewContainer(500) self.__stack.show() self.__setup_view() self.__setup_scanner() (list_one_ids, list_two_ids) = self.__get_saved_view_state() if list_one_ids and list_one_ids[0] != Type.NONE: self.__list_one.select_ids(list_one_ids) if list_two_ids and list_two_ids[0] != Type.NONE: self.__list_two.select_ids(list_two_ids) # Volume manager self.__vm = Gio.VolumeMonitor.get() self.__vm.connect('mount-added', self.__on_mount_added) self.__vm.connect('mount-removed', self.__on_mount_removed) for mount in self.__vm.get_mounts(): self.__add_device(mount, False) Lp().playlists.connect('playlists-changed', self.__update_playlists) def update_db(self): """ Update db at startup only if needed """ # Stop previous scan if Lp().scanner.is_locked(): Lp().scanner.stop() GLib.timeout_add(250, self.update_db) else: Lp().scanner.update() def get_genre_id(self): """ Return current selected genre @return genre id as int """ if self.__show_genres: return self.__list_one.get_selected_id() else: return None def init_list_one(self): """ Init list one """ self.__update_list_one(None) def save_view_state(self): """ Save view state """ Lp().settings.set_value( "list-one-ids", GLib.Variant('ai', self.__list_one.selected_ids)) Lp().settings.set_value( "list-two-ids", GLib.Variant('ai', self.__list_two.selected_ids)) def show_playlist_manager(self, object_id, genre_ids, artist_ids, is_album): """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre ids as [int] @param artist ids as [int] @param is_album as bool """ from lollypop.view_playlists import PlaylistsManageView current = self.__stack.get_visible_child() view = PlaylistsManageView(object_id, genre_ids, artist_ids, is_album) view.populate() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) current.disable_overlay() def show_playlist_editor(self, playlist_id): """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist id as int @param playlist name as str """ from lollypop.view_playlists import PlaylistEditView view = PlaylistEditView(playlist_id) view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) view.populate() def get_view_width(self): """ Return view width @return width as int """ return self.__stack.get_allocation().width def stop_all(self): """ Stop current view from processing """ view = self.__stack.get_visible_child() if view is not None: self.__stack.clean_old_views(None) def show_genres(self, show): """ Show/Hide genres @param bool """ self.__show_genres = show self.__list_one.clear() self.__list_two.clear() self.__list_two.hide() self.__update_list_one(None) self.__list_one.select_ids([Type.POPULARS]) def destroy_current_view(self): """ Destroy current view """ view = self.__stack.get_visible_child() for child in self.__stack.get_children(): if child != view: self.__stack.set_visible_child(child) self.__stack.clean_old_views(child) break @property def view(self): """ Disable overlays """ return self.__stack.get_visible_child() @property def progress(self): """ Progress bar @return ProgressBar """ return self.__progress def add_remove_from(self, value, list_one, add): """ Add or remove value to list @param value as (int, str) @param list one as bool @param add as bool """ if list_one: l = self.__list_one else: l = self.__list_two if add: l.add_value(value) else: l.remove_value(value[0]) def reload_view(self): """ Reload current view """ values_two = self.__list_two.selected_ids values_one = self.__list_one.selected_ids if not values_one: values_one = [Type.POPULARS] self.__list_one.select_ids([]) self.__list_one.clear() self.__update_list_one(None) self.__list_one.select_ids(values_one) if self.__list_two.is_visible(): self.__list_two.select_ids([]) self.__list_two.clear() self.__update_list_two(None) self.__list_two.select_ids(values_two) def pulse(self, pulse): """ Make progress bar visible/pulse if pulse is True @param pulse as bool """ if pulse and not self.__progress.is_visible(): self.__progress.show() if self.__pulse_timeout is None: self.__pulse_timeout = GLib.timeout_add(500, self.__pulse) else: if self.__pulse_timeout is not None: GLib.source_remove(self.__pulse_timeout) self.__pulse_timeout = None self.__progress.hide() def on_scan_finished(self, scanner): """ Mark force scan as False, update lists @param scanner as CollectionScanner """ self.__update_lists(scanner) def add_fake_phone(self): """ Emulate an Android Phone """ self.__devices_index -= 1 dev = Device() dev.id = self.__devices_index dev.name = "Android phone" dev.uri = "file:///tmp/android/" d = Lio.File.new_for_uri(dev.uri+"Internal Memory") if not d.query_exists(): d.make_directory_with_parents() d = Lio.File.new_for_uri(dev.uri+"SD Card") if not d.query_exists(): d.make_directory_with_parents() self.__devices[self.__devices_index] = dev def show_artists_albums(self, artist_ids): """ Show albums from artists """ self.__update_view_artists([], artist_ids) GLib.idle_add(self.__list_two.hide) GLib.idle_add(self.__list_one.select_ids, []) ############## # PROTECTED # ############## def _hide_pane(self): """ Hide navigation pane Internally hide list one and list two """ if self.__list_one.is_visible(): self.__list_two.hide() self.__list_one.hide() else: self.__list_one.show() if self.__list_two.was_visible: self.__list_two.show() Lp().settings.set_value('show-navigation-list', GLib.Variant('b', self.__list_one.is_visible())) ############ # PRIVATE # ############ def __pulse(self): """ Make progress bar pulse while visible @param pulse as bool """ if self.__progress.is_visible() and not Lp().scanner.is_locked(): self.__progress.pulse() return True else: self.__progress.set_fraction(0.0, self) return False def __setup_view(self): """ Setup window main view: - genre list - artist list - main view as artist view or album view """ self._paned_main_list = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) self._paned_list_view = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self.__list_one = SelectionList() if Lp().settings.get_value('show-navigation-list'): self.__list_one.show() self.__list_two = SelectionList() self.__list_one.connect('item-selected', self.__on_list_one_selected) self.__list_one.connect('populated', self.__on_list_populated) self.__list_two.connect('item-selected', self.__on_list_two_selected) self.__progress = ProgressBar() self.__progress.set_property('hexpand', True) vgrid.add(self.__stack) vgrid.add(self.__progress) vgrid.show() self._paned_list_view.add1(self.__list_two) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self.__list_one) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position( Lp().settings.get_value('paned-mainlist-width').get_int32()) self._paned_list_view.set_position( Lp().settings.get_value('paned-listview-width').get_int32()) self._paned_main_list.show() self._paned_list_view.show() def __get_saved_view_state(self): """ Get save view state @return (list one id, list two id) """ list_one_ids = [Type.POPULARS] list_two_ids = [Type.NONE] if Lp().settings.get_value('save-state'): list_one_ids = [] list_two_ids = [] ids = Lp().settings.get_value('list-one-ids') for i in ids: if isinstance(i, int): list_one_ids.append(i) ids = Lp().settings.get_value('list-two-ids') for i in ids: if isinstance(i, int): list_two_ids.append(i) return (list_one_ids, list_two_ids) def __setup_scanner(self): """ Run collection update if needed @return True if hard scan is running """ Lp().scanner.connect('scan-finished', self.on_scan_finished) Lp().scanner.connect('genre-updated', self.__on_genre_updated) Lp().scanner.connect('artist-updated', self.__on_artist_updated) def __update_playlists(self, playlists, playlist_id): """ Update playlists in second list @param playlists as Playlists @param playlist_id as int """ ids = self.__list_one.selected_ids if ids and ids[0] == Type.PLAYLISTS: if Lp().playlists.exists(playlist_id): self.__list_two.update_value(playlist_id, Lp().playlists.get_name( playlist_id)) else: self.__list_two.remove_value(playlist_id) def __update_lists(self, updater=None): """ Update lists @param updater as GObject """ self.__update_list_one(updater) self.__update_list_two(updater) def __update_list_one(self, updater): """ Update list one @param updater as GObject """ update = updater is not None if self.__show_genres: self.__update_list_genres(self.__list_one, update) else: self.__update_list_artists(self.__list_one, [Type.ALL], update) def __update_list_two(self, updater): """ Update list two @param updater as GObject """ update = updater is not None ids = self.__list_one.selected_ids if ids and ids[0] == Type.PLAYLISTS: self.__update_list_playlists(update) elif self.__show_genres and ids: self.__update_list_artists(self.__list_two, ids, update) def __update_list_genres(self, selection_list, update): """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def load(): genres = Lp().genres.get() return genres def setup(genres): items = selection_list.get_headers() items.append((Type.SEPARATOR, '')) items += genres selection_list.mark_as_artists(False) if update: selection_list.update_values(items) else: selection_list.populate(items) loader = Loader(target=load, view=selection_list, on_finished=setup) loader.start() def __update_list_artists(self, selection_list, genre_ids, update): """ Setup list for artists @param list as SelectionList @param genre ids as [int] @param update as bool, if True, just update entries @thread safe """ def load(): artists = Lp().artists.get(genre_ids) compilations = Lp().albums.get_compilation_ids(genre_ids) return (artists, compilations) def setup(artists, compilations): if selection_list == self.__list_one: items = selection_list.get_headers() if not compilations: items.append((Type.SEPARATOR, '')) else: items = [] if compilations: items.append((Type.COMPILATIONS, _("Compilations"))) items.append((Type.SEPARATOR, '')) items += artists selection_list.mark_as_artists(True) if update: selection_list.update_values(items) else: selection_list.populate(items) if selection_list == self.__list_one: if self.__list_two.is_visible(): self.__list_two.hide() self.__list_two_restore = Type.NONE loader = Loader(target=load, view=selection_list, on_finished=lambda r: setup(*r)) loader.start() def __update_list_charts(self): """ Setup list for charts @thread safe """ def load(): genres = Lp().genres.get_charts() return genres def setup(genres): genres.insert(0, (Type.SEPARATOR, '')) genres.insert(0, (Type.ITUNES, 'Itunes')) genres.insert(0, (Type.LASTFM, 'Last.fm')) genres.insert(0, (Type.SPOTIFY, "Spotify")) self.__list_two.populate(genres) self.__list_two.mark_as_artists(False) loader = Loader(target=load, view=self.__list_two, on_finished=setup) loader.start() def __update_list_playlists(self, update): """ Setup list for playlists @param update as bool @thread safe """ items = self.__list_two.get_pl_headers() items += Lp().playlists.get() if update: self.__list_two.update_values(items) else: self.__list_two.mark_as_artists(False) self.__list_two.populate(items) def __stop_current_view(self): """ Stop current view """ child = self.__stack.get_visible_child() if child is not None: if hasattr(child, "stop"): child.stop() def __update_view_device(self, device_id): """ Update current view with device view, Use existing view if available @param device id as int """ from lollypop.view_device import DeviceView, DeviceLocked self.__stop_current_view() device = self.__devices[device_id] child = self.__stack.get_child_by_name(device.uri) if child is None: files = DeviceView.get_files(device.uri) if files: if child is None: child = DeviceView(device) self.__stack.add_named(child, device.uri) else: child = DeviceLocked() self.__stack.add(child) child.show() child.populate() self.__stack.set_visible_child(child) self.__stack.clean_old_views(child) def __update_view_artists(self, genre_ids, artist_ids): """ Update current view with artists view @param genre ids as [int] @param artist ids as [int] """ def load(): if genre_ids and genre_ids[0] == Type.ALL: albums = Lp().albums.get_ids(artist_ids, []) else: albums = [] if artist_ids and artist_ids[0] == Type.COMPILATIONS: albums += Lp().albums.get_compilation_ids(genre_ids) albums += Lp().albums.get_ids(artist_ids, genre_ids) return albums from lollypop.view_artist import ArtistView self.__stop_current_view() view = ArtistView(artist_ids, genre_ids) loader = Loader(target=load, view=view) loader.start() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) def __update_view_albums(self, genre_ids, artist_ids): """ Update current view with albums view @param genre ids as [int] @param is compilation as bool """ def load(): items = [] is_compilation = artist_ids and artist_ids[0] == Type.COMPILATIONS if genre_ids and genre_ids[0] == Type.ALL: if is_compilation or\ Lp().settings.get_value('show-compilations'): items = Lp().albums.get_compilation_ids() if not is_compilation: items += Lp().albums.get_ids() elif genre_ids and genre_ids[0] == Type.POPULARS: items = Lp().albums.get_rated() count = 100 - len(items) for album in Lp().albums.get_populars(count): if album not in items: items.append(album) elif genre_ids and genre_ids[0] == Type.LOVED: items = Lp().albums.get_loves() elif genre_ids and genre_ids[0] == Type.RECENTS: items = Lp().albums.get_recents() elif genre_ids and genre_ids[0] == Type.RANDOMS: items = Lp().albums.get_randoms() elif genre_ids and genre_ids[0] in [Type.SPOTIFY, Type.LASTFM]: items = Lp().tracks.get_charts_ids(genre_ids) elif genre_ids and genre_ids[0] == Type.ITUNES: items = Lp().albums.get_charts_ids(genre_ids) elif artist_ids and artist_ids[0] == Type.CHARTS: items = Lp().albums.get_charts_ids(genre_ids) else: if is_compilation or\ Lp().settings.get_value('show-compilations'): items = Lp().albums.get_compilation_ids(genre_ids) if not is_compilation: items += Lp().albums.get_ids([], genre_ids) return items # Spotify albums contains only one tracks, show playlist view if genre_ids and genre_ids[0] in [Type.SPOTIFY, Type.LASTFM]: from lollypop.view_playlists import PlaylistsView view = PlaylistsView(genre_ids) loader = Loader(target=load, view=view) loader.start() else: from lollypop.view_albums import AlbumsView self.__stop_current_view() view = AlbumsView(genre_ids, artist_ids) loader = Loader(target=load, view=view) loader.start() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) def __update_view_playlists(self, playlist_ids=[]): """ Update current view with playlist view @param playlist ids as [int] """ def load(): track_ids = [] for playlist_id in playlist_ids: if playlist_id == Type.POPULARS: tracks = Lp().tracks.get_rated() for track in Lp().tracks.get_populars(): tracks.append(track) 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() elif playlist_id == Type.LOVED: tracks = Lp().playlists.get_track_ids_sorted(playlist_id) 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) return track_ids self.__stop_current_view() view = None if playlist_ids: from lollypop.view_playlists import PlaylistsView view = PlaylistsView(playlist_ids) loader = Loader(target=load, view=view) loader.start() else: from lollypop.view_playlists import PlaylistsManageView view = PlaylistsManageView(Type.NONE, [], [], False) view.populate() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) def __update_view_radios(self): """ Update current view with radios view """ from lollypop.view_radios import RadiosView self.__stop_current_view() view = RadiosView() view.populate() view.show() self.__stack.add(view) self.__stack.set_visible_child(view) self.__stack.clean_old_views(view) def __add_device(self, mount, show=False): """ Add a device @param mount as Gio.Mount @param show as bool """ if mount.get_volume() is None: return name = mount.get_name() uri = mount.get_default_location().get_uri() if uri is not None and ( mount.can_eject() or uri.startswith('mtp')): self.__devices_index -= 1 dev = Device() dev.id = self.__devices_index dev.name = name dev.uri = uri self.__devices[self.__devices_index] = dev if show: self.__list_one.add_value((dev.id, dev.name)) def __remove_device(self, mount): """ Remove volume from device list @param mount as Gio.Mount """ uri = mount.get_default_location().get_uri() for dev in self.__devices.values(): if dev.uri == uri: self.__list_one.remove_value(dev.id) child = self.__stack.get_child_by_name(uri) if child is not None: child.destroy() del self.__devices[dev.id] break def __on_list_one_selected(self, selection_list): """ Update view based on selected object @param list as SelectionList """ selected_ids = self.__list_one.selected_ids if not selected_ids: return self.__list_two.clear() if selected_ids[0] == Type.PLAYLISTS: if Lp().settings.get_value('show-navigation-list'): self.__list_two.show() if not self.__list_two.will_be_selected(): self.__update_view_playlists() self.__update_list_playlists(False) elif Type.DEVICES - 999 < selected_ids[0] < Type.DEVICES: self.__list_two.hide() if not self.__list_two.will_be_selected(): self.__update_view_device(selected_ids[0]) elif selected_ids[0] == Type.CHARTS: self.__list_two.show() self.__update_list_charts() self.__update_view_albums(selected_ids, []) elif selected_ids[0] in [Type.POPULARS, Type.LOVED, Type.RECENTS, Type.RANDOMS]: self.__list_two.hide() self.__update_view_albums(selected_ids, []) elif selected_ids[0] == Type.RADIOS: self.__list_two.hide() self.__update_view_radios() elif selection_list.is_marked_as_artists(): self.__list_two.hide() if selected_ids[0] == Type.ALL: self.__update_view_albums(selected_ids, []) elif selected_ids[0] == Type.COMPILATIONS: self.__update_view_albums([], selected_ids) else: self.__update_view_artists([], selected_ids) else: self.__update_list_artists(self.__list_two, selected_ids, False) if Lp().settings.get_value('show-navigation-list'): self.__list_two.show() if not self.__list_two.will_be_selected(): self.__update_view_albums(selected_ids, []) def __on_list_populated(self, selection_list): """ Add device to list one and update db @param selection list as SelectionList """ for dev in self.__devices.values(): self.__list_one.add_value((dev.id, dev.name)) def __on_list_two_selected(self, selection_list): """ Update view based on selected object @param list as SelectionList """ genre_ids = self.__list_one.selected_ids selected_ids = self.__list_two.selected_ids if not selected_ids or not genre_ids: return if genre_ids[0] == Type.PLAYLISTS: self.__update_view_playlists(selected_ids) elif genre_ids[0] == Type.CHARTS: self.__update_view_albums(selected_ids, [Type.CHARTS]) elif selected_ids[0] == Type.COMPILATIONS: self.__update_view_albums(genre_ids, selected_ids) else: self.__update_view_artists(genre_ids, selected_ids) def __on_genre_updated(self, scanner, genre_id, add): """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int @param add as bool """ # Ignore static genres, can happend with Charts if genre_id < 0: return if self.__show_genres: if add: genre_name = Lp().genres.get_name(genre_id) self.__list_one.add_value((genre_id, genre_name)) else: genre_ids = Lp().genres.get_ids() if genre_id not in genre_ids: self.__list_one.remove_value(genre_id) def __on_artist_updated(self, scanner, artist_id, add): """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param add as bool """ artist_name = Lp().artists.get_name(artist_id) sortname = Lp().artists.get_sortname(artist_id) if self.__show_genres: l = self.__list_two artist_ids = Lp().artists.get_ids(self.__list_one.selected_ids) else: l = self.__list_one artist_ids = Lp().artists.get_ids() if add: if artist_id in artist_ids: l.add_value((artist_id, artist_name, sortname)) else: if artist_id not in artist_ids: l.remove_value(artist_id) def __on_mount_added(self, vm, mount): """ On volume mounter @param vm as Gio.VolumeMonitor @param mount as Gio.Mount """ self.__add_device(mount, True) def __on_mount_removed(self, vm, mount): """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mount as Gio.Mount """ self.__remove_device(mount)
class Container: """ Container for main view """ def __init__(self): """ Init container """ # Index will start at -VOLUMES self._devices = {} self._devices_index = Type.DEVICES self._show_genres = Lp.settings.get_value("show-genres") self._stack = ViewContainer(500) self._stack.show() self._setup_view() self._setup_scanner() (list_one_id, list_two_id) = self._get_saved_view_state() self._list_one.select_id(list_one_id) self._list_two.select_id(list_two_id) # Volume manager self._vm = Gio.VolumeMonitor.get() self._vm.connect("mount-added", self._on_mount_added) self._vm.connect("mount-removed", self._on_mount_removed) Lp.playlists.connect("playlists-changed", self._update_lists) def update_db(self): """ Update db at startup only if needed """ # Stop previous scan if Lp.scanner.is_locked(): Lp.scanner.stop() GLib.timeout_add(250, self.update_db) else: # Something (device manager) is using progress bar progress = None if not self._progress.is_visible(): progress = self._progress Lp.scanner.update(progress) def get_genre_id(self): """ Return current selected genre @return genre id as int """ if self._show_genres: return self._list_one.get_selected_id() else: return None def init_list_one(self): """ Init list one """ self._update_list_one(None) def save_view_state(self): """ Save view state """ Lp.settings.set_value("list-one", GLib.Variant("i", self._list_one.get_selected_id())) Lp.settings.set_value("list-two", GLib.Variant("i", self._list_two.get_selected_id())) def show_playlist_manager(self, object_id, genre_id, is_album): """ Show playlist manager for object_id Current view stay present in ViewContainer @param object id as int @param genre id as int @param is_album as bool """ view = PlaylistsManageView(object_id, genre_id, is_album) view.populate() view.show() self._stack.add(view) self._stack.set_visible_child(view) def show_playlist_editor(self, playlist_name): """ Show playlist editor for playlist Current view stay present in ViewContainer @param playlist name as str """ view = PlaylistEditView(playlist_name) view.show() self._stack.add(view) self._stack.set_visible_child(view) view.populate() def main_widget(self): """ Get main widget @return Gtk.HPaned """ return self._paned_main_list def stop_all(self): """ Stop current view from processing """ view = self._stack.get_visible_child() if view is not None: self._stack.clean_old_views(None) def show_genres(self, show): """ Show/Hide genres @param bool """ self._show_genres = show self._list_one.clear() self._update_list_one(None) def destroy_current_view(self): """ Destroy current view """ view = self._stack.get_visible_child() for child in self._stack.get_children(): if child != view: self._stack.set_visible_child(child) self._stack.clean_old_views(child) break def update_view(self): """ Update current view """ view = self._stack.get_visible_child() if view: view.update_covers() def on_scan_finished(self, scanner): """ Mark force scan as False, update lists @param scanner as CollectionScanner """ self._update_lists(scanner) ############ # Private # ############ def _setup_view(self): """ Setup window main view: - genre list - artist list - main view as artist view or album view """ self._paned_main_list = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) self._paned_list_view = Gtk.Paned.new(Gtk.Orientation.HORIZONTAL) vgrid = Gtk.Grid() vgrid.set_orientation(Gtk.Orientation.VERTICAL) self._list_one = SelectionList() self._list_one.show() self._list_two = SelectionList() self._list_one.connect("item-selected", self._on_list_one_selected) self._list_one.connect("populated", self._on_list_populated) self._list_two.connect("item-selected", self._on_list_two_selected) self._progress = Gtk.ProgressBar() self._progress.set_property("hexpand", True) vgrid.add(self._stack) vgrid.add(self._progress) vgrid.show() separator = Gtk.Separator() separator.show() self._paned_list_view.add1(self._list_two) self._paned_list_view.add2(vgrid) self._paned_main_list.add1(self._list_one) self._paned_main_list.add2(self._paned_list_view) self._paned_main_list.set_position(Lp.settings.get_value("paned-mainlist-width").get_int32()) self._paned_list_view.set_position(Lp.settings.get_value("paned-listview-width").get_int32()) self._paned_main_list.show() self._paned_list_view.show() def _get_saved_view_state(self): """ Get save view state @return (list one id, list two id) """ list_one_id = Type.POPULARS list_two_id = Type.NONE if Lp.settings.get_value("save-state"): position = Lp.settings.get_value("list-one").get_int32() if position != -1: list_one_id = position position = Lp.settings.get_value("list-two").get_int32() if position != -1: list_two_id = position return (list_one_id, list_two_id) def _add_genre(self, scanner, genre_id): """ Add genre to genre list @param scanner as CollectionScanner @param genre id as int """ if self._show_genres: genre_name = Lp.genres.get_name(genre_id) self._list_one.add_value((genre_id, genre_name)) def _add_artist(self, scanner, artist_id, album_id): """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ artist_name = Lp.artists.get_name(artist_id) if self._show_genres: genre_ids = Lp.albums.get_genre_ids(album_id) genre_ids.append(Type.ALL) if self._list_one.get_selected_id() in genre_ids: self._list_two.add_value((artist_id, artist_name)) else: self._list_one.add_value((artist_id, artist_name)) def _setup_scanner(self): """ Run collection update if needed @return True if hard scan is running """ Lp.scanner.connect("scan-finished", self.on_scan_finished) Lp.scanner.connect("genre-update", self._add_genre) Lp.scanner.connect("artist-update", self._add_artist) def _update_lists(self, updater=None): """ Update lists @param updater as GObject """ self._update_list_one(updater) self._update_list_two(updater) def _update_list_one(self, updater): """ Update list one @param updater as GObject """ update = updater is not None # Do not update if updater is PlaylistsManager if not isinstance(updater, PlaylistsManager): if self._show_genres: self._setup_list_genres(self._list_one, update) else: self._setup_list_artists(self._list_one, Type.ALL, update) def _update_list_two(self, updater): """ Update list two @param updater as GObject """ update = updater is not None object_id = self._list_one.get_selected_id() if object_id == Type.PLAYLISTS: self._setup_list_playlists(update) elif self._show_genres and object_id != Type.NONE: self._setup_list_artists(self._list_two, object_id, update) def _get_headers(self): """ Return list one headers """ items = [] items.append((Type.POPULARS, _("Popular albums"))) items.append((Type.RECENTS, _("Recently added albums"))) items.append((Type.RANDOMS, _("Random albums"))) items.append((Type.PLAYLISTS, _("Playlists"))) items.append((Type.RADIOS, _("Radios"))) if self._show_genres: items.append((Type.ALL, _("All artists"))) else: items.append((Type.ALL, _("All albums"))) return items def _setup_list_genres(self, selection_list, update): """ Setup list for genres @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def load(): sql = Lp.db.get_cursor() genres = Lp.genres.get(sql) sql.close() return genres def setup(genres): items = self._get_headers() items.append((Type.SEPARATOR, "")) items += genres selection_list.mark_as_artists(False) if update: selection_list.update_values(items) else: selection_list.populate(items) loader = Loader(target=load, view=selection_list, on_finished=setup) loader.start() def _setup_list_artists(self, selection_list, genre_id, update): """ Setup list for artists @param list as SelectionList @param update as bool, if True, just update entries @thread safe """ def load(): sql = Lp.db.get_cursor() artists = Lp.artists.get(genre_id, sql) compilations = Lp.albums.get_compilations(genre_id, sql) sql.close() return (artists, compilations) def setup(artists, compilations): if selection_list == self._list_one: items = self._get_headers() items.append((Type.SEPARATOR, "")) else: items = [] if compilations: items.append((Type.COMPILATIONS, _("Compilations"))) items += artists selection_list.mark_as_artists(True) if update: selection_list.update_values(items) else: selection_list.populate(items) if selection_list == self._list_one: if self._list_two.is_visible(): self._list_two.hide() self._list_two_restore = Type.NONE loader = Loader(target=load, view=selection_list, on_finished=lambda r: setup(*r)) loader.start() def _setup_list_playlists(self, update): """ Setup list for playlists @param update as bool @thread safe """ playlists = [(Type.LOVED, Lp.playlists._LOVED)] playlists += [(Type.POPULARS, _("Popular tracks"))] playlists += [(Type.RECENTS, _("Recently played"))] playlists += [(Type.NEVER, _("Never played"))] playlists += [(Type.RANDOMS, _("Random tracks"))] playlists.append((Type.SEPARATOR, "")) playlists += Lp.playlists.get() if update: self._list_two.update_values(playlists) else: self._list_two.mark_as_artists(False) self._list_two.populate(playlists) def _update_view_device(self, device_id): """ Update current view with device view, Use existing view if available @param device id as int """ device = self._devices[device_id] child = self._stack.get_child_by_name(device.uri) if child is None: child = DeviceView(device, self._progress) self._stack.add_named(child, device.uri) child.show() child.populate() self._stack.set_visible_child(child) self._stack.clean_old_views(child) def _update_view_artists(self, artist_id, genre_id): """ Update current view with artists view @param artist id as int @param genre id as int """ def load(): sql = Lp.db.get_cursor() if artist_id == Type.COMPILATIONS: albums = Lp.albums.get_compilations(genre_id, sql) elif genre_id == Type.ALL: albums = Lp.albums.get_ids(artist_id, None, sql) else: albums = Lp.albums.get_ids(artist_id, genre_id, sql) sql.close() return albums view = ArtistView(artist_id, genre_id) loader = Loader(target=load, view=view) loader.start() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_albums(self, genre_id, is_compilation=False): """ Update current view with albums view @param genre id as int @param is compilation as bool """ def load(): sql = Lp.db.get_cursor() albums = [] if genre_id == Type.ALL: if is_compilation: albums = Lp.albums.get_compilations(None, sql) else: if Lp.settings.get_value("show-compilations"): albums = Lp.albums.get_compilations(None, sql) albums += Lp.albums.get_ids(None, None, sql) elif genre_id == Type.POPULARS: albums = Lp.albums.get_populars(sql) elif genre_id == Type.RECENTS: albums = Lp.albums.get_recents(sql) elif genre_id == Type.RANDOMS: albums = Lp.albums.get_randoms(sql) elif is_compilation: albums = Lp.albums.get_compilations(genre_id, sql) else: if Lp.settings.get_value("show-compilations"): albums = Lp.albums.get_compilations(genre_id, sql) albums += Lp.albums.get_ids(None, genre_id, sql) sql.close() return albums view = AlbumsView(genre_id, is_compilation) loader = Loader(target=load, view=view) loader.start() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_playlists(self, playlist_id): """ Update current view with playlist view @param playlist id as int """ def load(): sql = Lp.db.get_cursor() if playlist_id == Lp.player.get_user_playlist_id(): tracks = [t.id for t in Lp.player.get_user_playlist()] elif playlist_id == Type.POPULARS: tracks = Lp.tracks.get_populars(sql) elif playlist_id == Type.RECENTS: tracks = Lp.tracks.get_recently_listened_to(sql) elif playlist_id == Type.NEVER: tracks = Lp.tracks.get_never_listened_to(sql) elif playlist_id == Type.RANDOMS: tracks = Lp.tracks.get_randoms(sql) else: tracks = Lp.playlists.get_tracks_id(name, sql) sql.close() return tracks view = None if playlist_id is not None: name = self._list_two.get_value(playlist_id) view = PlaylistView(playlist_id, name, self._stack) else: view = PlaylistsManageView(-1, None, False) if view: # Management or user playlist if playlist_id is None: view.populate() else: loader = Loader(target=load, view=view) loader.start() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _update_view_radios(self): """ Update current view with radios view """ view = RadiosView() view.populate() view.show() self._stack.add(view) self._stack.set_visible_child(view) self._stack.clean_old_views(view) def _add_device(self, volume): """ Add volume to device list @param volume as Gio.Volume """ if volume is None: return root = volume.get_activation_root() if root is None: return uri = root.get_uri() # Just to be sure if uri is not None and len(uri) > 1 and uri[-1:] != "/": uri += "/" if uri is not None and uri.find("mtp:") != -1: self._devices_index -= 1 dev = Device() dev.id = self._devices_index dev.name = volume.get_name() dev.uri = uri self._devices[self._devices_index] = dev self._list_one.add_value((dev.id, dev.name)) def _remove_device(self, volume): """ Remove volume from device list @param volume as Gio.Volume """ if volume is None: return root = volume.get_activation_root() if root is None: return uri = root.get_uri() for dev in self._devices.values(): if dev.uri == uri: self._list_one.remove(dev.id) child = self._stack.get_child_by_name(uri) if child is not None: child.destroy() del self._devices[dev.id] break def _on_list_one_selected(self, selection_list, selected_id): """ Update view based on selected object @param list as SelectionList @param selected id as int """ if selected_id == Type.PLAYLISTS: self._list_two.clear() self._list_two.show() if not self._list_two.will_be_selected(): self._update_view_playlists(None) self._setup_list_playlists(False) elif selected_id < Type.DEVICES: self._list_two.hide() if not self._list_two.will_be_selected(): self._update_view_device(selected_id) elif selected_id in [Type.POPULARS, Type.RECENTS, Type.RANDOMS]: self._list_two.hide() self._update_view_albums(selected_id) elif selected_id == Type.RADIOS: self._list_two.hide() self._update_view_radios() elif selection_list.is_marked_as_artists(): self._list_two.hide() if selected_id == Type.ALL: self._update_view_albums(selected_id) elif selected_id == Type.COMPILATIONS: self._update_view_albums(None, True) else: self._update_view_artists(selected_id, None) else: self._list_two.clear() self._setup_list_artists(self._list_two, selected_id, False) self._list_two.show() if not self._list_two.will_be_selected(): self._update_view_albums(selected_id, False) def _on_list_populated(self, selection_list): """ Add device to list one and update db @param selection list as SelectionList """ for dev in self._devices.values(): self._list_one.add_value((dev.id, dev.name)) def _on_list_two_selected(self, selection_list, selected_id): """ Update view based on selected object @param list as SelectionList @param selected id as int """ genre_id = self._list_one.get_selected_id() if genre_id == Type.PLAYLISTS: self._update_view_playlists(selected_id) elif selected_id == Type.COMPILATIONS: self._update_view_albums(genre_id, True) else: self._update_view_artists(selected_id, genre_id) def _on_mount_added(self, vm, mnt): """ On volume mounter @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ self._add_device(mnt.get_volume()) def _on_mount_removed(self, vm, mnt): """ On volume removed, clean selection list @param vm as Gio.VolumeMonitor @param mnt as Gio.Mount """ self._remove_device(mnt.get_volume())