def propertiesbutton_callback(self, choice): if choice == 'download': self.request_status_box.show_all() self._cover_search_manager = self.viewmgr.current_view.get_default_manager( ) self._cover_search_manager.cover_man.search_covers( callback=self.update_request_status_bar) elif choice == 'random': self.play_random_album_menu_item_callback() elif choice == 'random favourite': self.play_random_album_menu_item_callback(True) elif choice == 'favourite': self.favourites = not self.favourites self.viewmgr.current_view.set_popup_menu(self.popup_menu) elif choice == 'browser prefs': if not self._browser_preferences: self._browser_preferences = Preferences() self._browser_preferences.display_preferences_dialog(self.plugin) elif choice == 'search prefs': try: if not self._search_preferences: from gi.repository import Peas peas = Peas.Engine.get_default() plugin_info = peas.get_plugin_info( 'coverart_search_providers') module_name = plugin_info.get_module_name() mod = __import__(module_name) sp = getattr(mod, "SearchPreferences") self._search_preferences = sp() self._search_preferences.plugin_info = plugin_info self._search_preferences.display_preferences_dialog( self._search_preferences) except: dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("Please install and activate the latest version of the Coverart Search Providers plugin" )) dialog.run() dialog.destroy() else: assert 1 == 2, ("unknown choice %s", choice)
def propertiesbutton_callback(self, choice): if choice == 'download': self.request_status_box.show_all() self._cover_search_manager = self.viewmgr.current_view.get_default_manager() self._cover_search_manager.cover_man.search_covers( callback=self.update_request_status_bar) elif choice == 'random': self.play_random_album_menu_item_callback() elif choice == 'random favourite': self.play_random_album_menu_item_callback(True) elif choice == 'favourite': self.favourites = not self.favourites self.viewmgr.current_view.set_popup_menu(self.popup_menu) elif choice == 'browser prefs': if not self._browser_preferences: self._browser_preferences = Preferences() self._browser_preferences.display_preferences_dialog(self.plugin) elif choice == 'search prefs': try: if not self._search_preferences: from gi.repository import Peas peas = Peas.Engine.get_default() plugin_info = peas.get_plugin_info('coverart_search_providers') module_name = plugin_info.get_module_name() mod = __import__(module_name) sp = getattr(mod, "SearchPreferences") self._search_preferences = sp() self._search_preferences.plugin_info = plugin_info self._search_preferences.display_preferences_dialog(self._search_preferences) except: dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("Please install and activate the latest version of the Coverart Search Providers plugin")) dialog.run() dialog.destroy() else: assert 1==2, ("unknown choice %s", choice)
def _translation_helper(self): ''' a method just to help out with translation strings it is not meant to be called by itself ''' # define .plugin text strings used for translation plugin = _('CoverArt Browser') desc = _('Browse and play your albums through their covers') # . TRANSLATORS: This is the icon-grid view that the user sees tile = _('Tiles') #. TRANSLATORS: This is the cover-flow view the user sees - they can swipe album covers from side-to-side artist = _('Flow') #. TRANSLATORS: percentage size that the image will be expanded scale = _('Scale by %:') # stop PyCharm removing the Preference import on optimisation pref = Preferences()
class CoverArtBrowserSource(RB.Source): ''' Source utilized by the plugin to show all it's ui. ''' rating_threshold = GObject.property(type=float, default=0) artist_paned_pos = GObject.property(type=str) min_paned_pos = 40 # unique instance of the source instance = None def __init__(self, **kargs): ''' Initializes the source. ''' super(CoverArtBrowserSource, self).__init__(**kargs) # create source_source_settings and connect the source's properties self.gs = GSetting() self._connect_properties() self.hasActivated = False self.last_width = 0 self.last_selected_album = None self.click_count = 0 self.favourites = False self.task_progress = None def _connect_properties(self): ''' Connects the source properties to the saved preferences. ''' print("CoverArtBrowser DEBUG - _connect_properties") setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting.bind( self.gs.PluginKey.RATING, self, 'rating_threshold', Gio.SettingsBindFlags.GET) print("CoverArtBrowser DEBUG - end _connect_properties") def do_get_status(self, *args): ''' Method called by Rhythmbox to figure out what to show on this source statusbar. If the custom statusbar is disabled, the source will show the selected album info. Also, it makes sure to show the progress on the album loading ''' try: # this will only work for RB3.0 and later if not self.task_progress: self.task_progress = RB.TaskProgressSimple.new() except: pass try: progress = self.album_manager.progress progress_text = _('Loading...') if progress < 1 else '' try: # this will only work for RB3.0 and later if progress < 1: if self.props.shell.props.task_list.get_model().n_items() == 0: self.props.shell.props.task_list.add_task(self.task_progress) self.task_progress.props.task_progress = progress self.task_progress.props.task_label=progress_text else: self.task_progress.props.task_outcome = RB.TaskOutcome.COMPLETE except: pass except: progress = 1 progress_text = '' try: # this will only work for RB3.0 and later self.task_progress.props.task_outcome = RB.TaskOutcome.COMPLETE except: pass return (self.status, progress_text, progress) def do_selected(self): ''' Called by Rhythmbox when the source is selected. It makes sure to create the ui the first time the source is showed. ''' print("CoverArtBrowser DEBUG - do_selected") # first time of activation -> add graphical stuff if not self.hasActivated: self.do_impl_activate() # indicate that the source was activated before self.hasActivated = True print("CoverArtBrowser DEBUG - end do_selected") def do_impl_activate(self): ''' Called by do_selected the first time the source is activated. It creates all the source ui and connects the necesary signals for it correct behavior. ''' print("CoverArtBrowser DEBUG - do_impl_activate") # initialise some variables self.plugin = self.props.plugin self.shell = self.props.shell self.status = '' self.search_text = '' self.actiongroup = ActionGroup(self.shell, 'coverplaylist_submenu') self._browser_preferences = None self._search_preferences = None # indicate that the source was activated before self.hasActivated = True self._create_ui() self._setup_source() self._apply_settings() print("CoverArtBrowser DEBUG - end do_impl_activate") def _create_ui(self): ''' Creates the ui for the source and saves the important widgets onto properties. ''' print("CoverArtBrowser DEBUG - _create_ui") # dialog has not been created so lets do so. cl = CoverLocale() ui = Gtk.Builder() ui.set_translation_domain(cl.Locale.LOCALE_DOMAIN) ui.add_from_file(rb.find_plugin_file(self.plugin, 'ui/coverart_browser.ui')) ui.connect_signals(self) # load the page and put it in the source self.page = ui.get_object('main_box') self.pack_start(self.page, True, True, 0) # get widgets for main icon-view self.status_label = ui.get_object('status_label') window = ui.get_object('scrolled_window') self.viewmgr = ViewManager(self, window) # get widgets for the artist paned self.artist_paned = ui.get_object('vertical_paned') Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 50, self._change_artist_paned_pos, self.viewmgr.view_name) self.viewmgr.connect('new-view', self.on_view_changed) self.artist_treeview = ui.get_object('artist_treeview') self.artist_scrolledwindow = ui.get_object('artist_scrolledwindow') # define menu's self.popup_menu = Menu(self.plugin, self.shell) self.popup_menu.load_from_file('ui/coverart_browser_pop_rb2.ui', 'ui/coverart_browser_pop_rb3.ui') self._external_plugins = None signals = \ { 'play_album_menu_item': self.play_album_menu_item_callback, 'queue_album_menu_item': self.queue_album_menu_item_callback, 'new_playlist': self.add_playlist_menu_item_callback, 'cover_search_menu_item': self.cover_search_menu_item_callback, 'export_embed_menu_item': self.export_embed_menu_item_callback, 'show_properties_menu_item': self.show_properties_menu_item_callback, 'play_similar_artist_menu_item': self.play_similar_artist_menu_item_callback} self.popup_menu.connect_signals(signals) self.popup_menu.connect('pre-popup', self.add_external_menu) self.status_label = ui.get_object('status_label') self.request_status_box = ui.get_object('request_status_box') self.request_spinner = ui.get_object('request_spinner') self.request_statusbar = ui.get_object('request_statusbar') self.request_cancel_button = ui.get_object('request_cancel_button') self.paned = ui.get_object('paned') self.notebook = ui.get_object('bottom_notebook') #---- set up info pane -----# info_scrolled_window = ui.get_object('info_scrolled_window') info_button_box = ui.get_object('info_button_box') artist_info_paned = ui.get_object('vertical_info_paned') self.artist_info = ArtistInfoPane(info_button_box, info_scrolled_window, artist_info_paned, self) # quick search self.quick_search = ui.get_object('quick_search_entry') print("CoverArtBrowser DEBUG - end _create_ui") def _setup_source(self): ''' Setups the differents parts of the source so they are ready to be used by the user. It also creates and configure some custom widgets. ''' print("CoverArtBrowser DEBUG - _setup_source") cl = CoverLocale() cl.switch_locale(cl.Locale.LOCALE_DOMAIN) # setup iconview popup self.viewmgr.current_view.set_popup_menu(self.popup_menu) # setup entry-view objects and widgets setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting.bind(self.gs.PluginKey.PANED_POSITION, self.paned, 'collapsible-y', Gio.SettingsBindFlags.DEFAULT) setting.bind(self.gs.PluginKey.DISPLAY_BOTTOM, self.paned.get_child2(), 'visible', Gio.SettingsBindFlags.DEFAULT) # create entry view. Don't allow to reorder until the load is finished self.entry_view = EV(self.shell, self) self.entry_view.set_columns_clickable(False) self.shell.props.library_source.get_entry_view().set_columns_clickable( False) self.stars = ReactiveStar() self.stars.set_rating(0) a = Gtk.Alignment.new(0.5, 0.5, 0, 0) a.add(self.stars) self.stars.connect('changed', self.rating_changed_callback) vbox = Gtk.Box() vbox.set_orientation(Gtk.Orientation.VERTICAL) vbox.pack_start(self.entry_view, True, True, 0) vbox.pack_start(a, False, False, 1) vbox.show_all() self.notebook.append_page(vbox, Gtk.Label.new_with_mnemonic(_("Tracks"))) # create an album manager self.album_manager = AlbumManager(self.plugin, self.viewmgr.current_view) self.viewmgr.current_view.initialise(self) # setup cover search pane colour = self.viewmgr.get_selection_colour() self.cover_search_pane = CoverSearchPane(self.plugin, colour) self.notebook.append_page(self.cover_search_pane, Gtk.Label.new_with_mnemonic( _("Covers"))) # connect a signal to when the info of albums is ready self.load_fin_id = self.album_manager.loader.connect( 'model-load-finished', self.load_finished_callback) # prompt the loader to load the albums self.album_manager.loader.load_albums(self.props.base_query_model) # initialise the variables of the quick search self.quick_search_controller = AlbumQuickSearchController( self.album_manager) self.quick_search_controller.connect_quick_search(self.quick_search) # set sensitivity of export menu item for iconview self.popup_menu.set_sensitive('export_embed_menu_item', CoverArtExport(self.plugin, self.shell, self.album_manager).is_search_plugin_enabled()) # setup the statusbar component self.statusbar = Statusbar(self) # initialise the toolbar manager self.toolbar_manager = ToolbarManager(self.plugin, self.page, self.viewmgr) self.viewmgr.current_view.emit('update-toolbar') cl.switch_locale(cl.Locale.RB) # setup the artist paned artist_pview = None for view in self.shell.props.library_source.get_property_views(): print (view.props.title) print (_("Artist")) if view.props.title == _("Artist"): artist_pview = view break assert artist_pview, "cannot find artist property view" self.artist_treeview.set_model(artist_pview.get_model()) setting.bind(self.gs.PluginKey.ARTIST_PANED_POSITION, self, 'artist-paned-pos', Gio.SettingsBindFlags.DEFAULT) self.artist_paned.connect('button-release-event', self.artist_paned_button_release_callback) # intercept JumpToPlaying Song action so that we can scroll to the playing album appshell = rb3compat.ApplicationShell(self.shell) action = appshell.lookup_action("", "jump-to-playing", "win") action.action.connect("activate", self.jump_to_playing, None) self.echonest_similar_playlist = None print("CoverArtBrowser DEBUG - end _setup_source") def add_external_menu(self, *args): ''' Callback when the popup menu is about to be displayed ''' if not self._external_plugins: # initialise external plugin menu support self._external_plugins = \ CreateExternalPluginMenu("ca_covers_view", 7, self.popup_menu) self._external_plugins.create_menu('popup_menu', True) self.playlist_menu_item_callback() def jump_to_playing(self, *args): ''' Callback when the JumpToPlaying action is invoked This will scroll the view to the playing song ''' if not self.shell.props.selected_page.props.name == self.props.name: # if the source page that was played from is not the plugin then # nothing to do return album = None entry = self.shell.props.shell_player.get_playing_entry() if entry: album = self.album_manager.model.get_from_dbentry(entry) self.viewmgr.current_view.scroll_to_album(album) def artist_paned_button_release_callback(self, *args): ''' Callback when the artist paned handle is released from its mouse click. ''' child_width = self._get_child_width() paned_positions = eval(self.artist_paned_pos) found = None for viewpos in paned_positions: if self.viewmgr.view_name in viewpos: found = viewpos break if not found: return paned_positions.remove(found) if child_width <= self.min_paned_pos: child_width = 0 self.artist_paned.set_position(child_width) paned_positions.append(self.viewmgr.view_name + ":" + str(child_width)) self.artist_paned_pos = repr(paned_positions) def on_view_changed(self, widget, view_name): self._change_artist_paned_pos(view_name) def _change_artist_paned_pos(self, view_name): paned_positions = eval(self.artist_paned_pos) print (paned_positions) found = None for viewpos in paned_positions: if view_name in viewpos: found = viewpos break print (found) if not found: return child_width = int(found.split(":")[1]) print (child_width) # odd case - if the pane is not visible but the position is zero # then the paned position on visible=true is some large arbitary value # hence - set it to be 1 px larger than the real value, then set it back # to its expected value self.artist_paned.set_position(child_width + 1) self.artist_paned.set_visible(True) self.artist_paned.set_position(child_width) def _get_child_width(self): child = self.artist_paned.get_child1() return child.get_allocated_width() def on_artist_treeview_selection_changed(self, view): model, artist_iter = view.get_selected() if artist_iter: artist = model[artist_iter][0] cl = CoverLocale() cl.switch_locale(cl.Locale.RB) #. TRANSLATORS - "All" is used in the context of "All artist names" if artist == _('All'): self.album_manager.model.remove_filter('quick_artist') else: self.album_manager.model.replace_filter('quick_artist', artist) cl.switch_locale(cl.Locale.LOCALE_DOMAIN) def _apply_settings(self): ''' Applies all the settings related to the source and connects those that must be updated when the preferences dialog changes it's values. Also enables differents parts of the ui if the settings says so. ''' print("CoverArtBrowser DEBUG - _apply_settings") # connect some signals to the loader to keep the source informed self.album_mod_id = self.album_manager.model.connect('album-updated', self.on_album_updated) self.notify_prog_id = self.album_manager.connect( 'notify::progress', lambda *args: self.notify_status_changed()) print("CoverArtBrowser DEBUG - end _apply_settings") def load_finished_callback(self, _): ''' Callback called when the loader finishes loading albums into the covers view model. ''' print("CoverArtBrowser DEBUG - load_finished_callback") #if not self.request_status_box.get_visible(): # it should only be enabled if no cover request is going on #self.source_menu_search_all_item.set_sensitive(True) # enable sorting on the entryview self.entry_view.set_columns_clickable(True) self.shell.props.library_source.get_entry_view().set_columns_clickable( True) print("CoverArtBrowser DEBUG - end load_finished_callback") def get_entry_view(self): return self.entry_view def on_album_updated(self, model, path, tree_iter): ''' Callback called by the album loader when one of the albums managed by him gets modified in some way. ''' album = model.get_from_path(path) selected = self.viewmgr.current_view.get_selected_objects() if album in selected: # update the selection since it may have changed self.viewmgr.current_view.selectionchanged_callback() if album is selected[0] and \ self.notebook.get_current_page() == \ self.notebook.page_num(self.cover_search_pane): # also, if it's the first, update the cover search pane self.cover_search_pane.clear() self.cover_search_pane.do_search(album, self.album_manager.cover_man.update_cover) def play_similar_artist_menu_item_callback(self, *args): ''' Callback called when the play similar artist option is selected from the cover view popup. It plays similar artists music. ''' def play_similar_artist_menu_item_callback(self, *args): if not self.echonest_similar_playlist: self.echonest_similar_playlist = \ EchoNestPlaylist( self.shell, self.shell.props.queue_source) selected_albums = self.viewmgr.current_view.get_selected_objects() album = selected_albums[0] tracks = album.get_tracks() entry = tracks[0].entry self.echonest_similar_playlist.start(entry, reinitialise=True) def show_properties_menu_item_callback(self, *args): ''' Callback called when the show album properties option is selected from the cover view popup. It shows a SongInfo dialog showing the selected albums' entries info, which can be modified. ''' print("CoverArtBrowser DEBUG - show_properties_menu_item_callback") self.entry_view.select_all() info_dialog = RB.SongInfo(source=self, entry_view=self.entry_view) info_dialog.show_all() print("CoverArtBrowser DEBUG - end show_properties_menu_item_callback") def play_selected_album(self, favourites=False): ''' Utilitary method that plays all entries from an album into the play queue. ''' # callback when play an album print("CoverArtBrowser DEBUG - play_selected_album") query_model = RB.RhythmDBQueryModel.new_empty(self.shell.props.db) self.queue_selected_album(query_model, favourites) if len(query_model) > 0: self.props.query_model = query_model # Start the music player = self.shell.props.shell_player player.play_entry(query_model[0][0], self) print("CoverArtBrowser DEBUG - end play_selected_album") def queue_selected_album(self, source, favourites=False): ''' Utilitary method that queues all entries from an album into the play queue. ''' print("CoverArtBrowser DEBUG - queue_selected_album") selected_albums = self.viewmgr.current_view.get_selected_objects() threshold = self.rating_threshold if favourites else 0 total = 0 for album in selected_albums: # Retrieve and sort the entries of the album tracks = album.get_tracks(threshold) total = total + len(tracks) # Add the songs to the play queue for track in tracks: source.add_entry(track.entry, -1) if total == 0 and threshold: dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("No tracks have been added because no tracks meet the favourite rating threshold")) dialog.run() dialog.destroy() print("CoverArtBrowser DEBUG - end queue_select_album") def play_album_menu_item_callback(self, *args): ''' Callback called when the play album item from the cover view popup is selected. It cleans the play queue and queues the selected album. ''' print("CoverArtBrowser DEBUG - play_album_menu_item_callback") self.play_selected_album(self.favourites) print("CoverArtBrowser DEBUG - end play_album_menu_item_callback") def queue_album_menu_item_callback(self, *args): ''' Callback called when the queue album item from the cover view popup is selected. It queues the selected album at the end of the play queue. ''' print("CoverArtBrowser DEBUG - queue_album_menu_item_callback()") self.queue_selected_album(self.shell.props.queue_source, self.favourites) print("CoverArtBrowser DEBUG - end queue_album_menu_item_callback()") def playlist_menu_item_callback(self, *args): print("CoverArtBrowser DEBUG - playlist_menu_item_callback") self.playlist_fillmenu(self.popup_menu, 'playlist_submenu', 'playlist_section', self.actiongroup, self.add_to_static_playlist_menu_item_callback, self.favourites) def playlist_fillmenu(self, popup_menu, menubar, section_name, actiongroup, func, favourite=False): print("CoverArtBrowser DEBUG - playlist_fillmenu") playlist_manager = self.shell.props.playlist_manager playlists_entries = playlist_manager.get_playlists() # tidy up old playlists menu items before recreating the list actiongroup.remove_actions() popup_menu.remove_menu_items(menubar, section_name) if playlists_entries: for playlist in playlists_entries: if playlist.props.is_local and \ isinstance(playlist, RB.StaticPlaylistSource): args=(playlist, favourite) # take the name of the playlist, strip out non-english characters and reduce the string # to just a-to-z characters i.e. this will make the action_name valid in RB3 ascii_name = unicodedata.normalize('NFKD', \ rb3compat.unicodestr(playlist.props.name, 'utf-8')).encode('ascii','ignore') ascii_name = ascii_name.decode(encoding='UTF-8') ascii_name = re.sub(r'[^a-zA-Z]', '', ascii_name) action = actiongroup.add_action(func=func, action_name=ascii_name, playlist=playlist,favourite=favourite, label=playlist.props.name) popup_menu.add_menu_item( menubar, section_name, action ) def add_to_static_playlist_menu_item_callback(self, action, param, args): print('''CoverArtBrowser DEBUG - add_to_static_playlist_menu_item_callback''') playlist=args['playlist'] favourite = args['favourite'] self.queue_selected_album(playlist, favourite) def add_playlist_menu_item_callback(self, *args): print('''CoverArtBrowser DEBUG - add_playlist_menu_item_callback''') playlist_manager = self.shell.props.playlist_manager playlist = playlist_manager.new_playlist(_('New Playlist'), False) self.queue_selected_album(playlist, self.favourites) def play_random_album_menu_item_callback(self, favourites=False): print('''CoverArtBrowser DEBUG - play_random_album_menu_item_callback''') query_model = RB.RhythmDBQueryModel.new_empty(self.shell.props.db) num_albums = len(self.album_manager.model.store) #random_list = [] selected_albums = [] gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) to_queue = settings[gs.PluginKey.RANDOM] if num_albums <= to_queue: dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("The number of albums to randomly play is less than that displayed.")) dialog.run() dialog.destroy() return album_col = self.album_manager.model.columns['album'] chosen = {} # now loop through finding unique random albums # i.e. ensure we dont queue the same album twice for loop in range(0, to_queue): while True: pos = random.randint(0, num_albums - 1) if pos not in chosen: chosen[pos] = None selected_albums.append(self.album_manager.model.store[pos][album_col]) break threshold = self.rating_threshold if favourites else 0 total = 0 for album in selected_albums: # Retrieve and sort the entries of the album tracks = album.get_tracks(threshold) total = total + len(tracks) # Add the songs to the play queue for track in tracks: query_model.add_entry(track.entry, -1) if total == 0 and threshold: dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("No tracks have been added because no tracks meet the favourite rating threshold")) dialog.run() dialog.destroy() self.props.query_model = query_model # Start the music player = self.shell.props.shell_player player.play_entry(query_model[0][0], self) print("CoverArtBrowser DEBUG - end play_selected_album") def cover_search_menu_item_callback(self, *args): ''' Callback called when the search cover option is selected from the cover view popup. It prompts the album loader to retrieve the selected album cover ''' print("CoverArtBrowser DEBUG - cover_search_menu_item_callback()") selected_albums = self.viewmgr.current_view.get_selected_objects() self.request_status_box.show_all() self.album_manager.cover_man.search_covers(selected_albums, self.update_request_status_bar) print("CoverArtBrowser DEBUG - end cover_search_menu_item_callback()") def export_embed_menu_item_callback(self, *args): ''' Callback called when the export and embed coverart option is selected from the cover view popup. It prompts the exporter to copy and embed art for the albums chosen ''' print("CoverArtBrowser DEBUG - export_embed_menu_item_callback()") selected_albums = self.viewmgr.current_view.get_selected_objects() CoverArtExport(self.plugin, self.shell, self.album_manager).embed_albums(selected_albums) print("CoverArtBrowser DEBUG - export_embed_menu_item_callback()") def update_request_status_bar(self, coverobject): ''' Callback called by the album loader starts performing a new cover request. It prompts the source to change the content of the request statusbar. ''' print("CoverArtBrowser DEBUG - update_request_status_bar") if coverobject: # for example "Requesting the picture cover for the music artist Michael Jackson" tranlation_string = _('Requesting cover for %s...') self.request_statusbar.set_text( rb3compat.unicodedecode(_('Requesting cover for %s...') % (coverobject.name), 'UTF-8')) else: self.request_status_box.hide() self.popup_menu.set_sensitive('cover_search_menu_item', True) self.request_cancel_button.set_sensitive(True) print("CoverArtBrowser DEBUG - end update_request_status_bar") def cancel_request_callback(self, _): ''' Callback connected to the cancel button on the request statusbar. When called, it prompts the album loader to cancel the full cover search after the current cover. ''' print("CoverArtBrowser DEBUG - cancel_request_callback") self.request_cancel_button.set_sensitive(False) self._cover_search_manager.cover_man.cancel_cover_request() print("CoverArtBrowser DEBUG - end cancel_request_callback") def notebook_switch_page_callback(self, notebook, page, page_num): ''' Callback called when the notebook page gets switched. It initiates the cover search when the cover search pane's page is selected. ''' print("CoverArtBrowser DEBUG - notebook_switch_page_callback") if page_num == 1: self.viewmgr.current_view.switch_to_coverpane(self.cover_search_pane) print("CoverArtBrowser DEBUG - end notebook_switch_page_callback") def rating_changed_callback(self, widget): ''' Callback called when the Rating stars is changed ''' print("CoverArtBrowser DEBUG - rating_changed_callback") rating = widget.get_rating() for album in self.viewmgr.current_view.get_selected_objects(): album.rating = rating print("CoverArtBrowser DEBUG - end rating_changed_callback") def show_hide_pane(self, params): ''' helper function - if the entry is manually expanded then if necessary scroll the view to the last selected album params is "album" or a tuple of "album" and "force_expand" boolean ''' if isinstance(params, tuple): album, force = params else: album = params force = PanedCollapsible.Paned.DEFAULT if (album and self.click_count == 1 \ and self.last_selected_album is album) or force != PanedCollapsible.Paned.DEFAULT: # check if it's a second or third click on the album and expand # or collapse the entry view accordingly self.paned.expand(force) # update the selected album selected = self.viewmgr.current_view.get_selected_objects() self.last_selected_album = selected[0] if len(selected) == 1 else None # clear the click count self.click_count = 0 def update_with_selection(self): ''' Update the source view when an item gets selected. ''' selected = self.viewmgr.current_view.get_selected_objects() # clear the entry view self.entry_view.clear() cover_search_pane_visible = self.notebook.get_current_page() == \ self.notebook.page_num(self.cover_search_pane) if not selected: # clean cover tab if selected if cover_search_pane_visible: self.cover_search_pane.clear() return elif len(selected) == 1: self.stars.set_rating(selected[0].rating) if selected[0] is not self.last_selected_album: # when the selection changes we've to take into account two # things if not self.click_count: # we may be using the arrows, so if there is no mouse # involved, we should change the last selected self.last_selected_album = selected[0] else: # we may've doing a fast change after a valid second click, # so it shouldn't be considered a double click self.click_count -= 1 else: self.stars.set_rating(0) if len(selected) == 1: self.artist_info.emit('selected', selected[0].artist, selected[0].name) for album in selected: # add the album to the entry_view self.entry_view.add_album(album) # update the cover search pane with the first selected album if cover_search_pane_visible: self.cover_search_pane.do_search(selected[0], self.album_manager.cover_man.update_cover) self.statusbar.emit('display-status', self.viewmgr.current_view) def propertiesbutton_callback(self, choice): if choice == 'download': self.request_status_box.show_all() self._cover_search_manager = self.viewmgr.current_view.get_default_manager() self._cover_search_manager.cover_man.search_covers( callback=self.update_request_status_bar) elif choice == 'random': self.play_random_album_menu_item_callback() elif choice == 'random favourite': self.play_random_album_menu_item_callback(True) elif choice == 'favourite': self.favourites = not self.favourites self.viewmgr.current_view.set_popup_menu(self.popup_menu) elif choice == 'browser prefs': if not self._browser_preferences: self._browser_preferences = Preferences() self._browser_preferences.display_preferences_dialog(self.plugin) elif choice == 'search prefs': try: if not self._search_preferences: from gi.repository import Peas peas = Peas.Engine.get_default() plugin_info = peas.get_plugin_info('coverart_search_providers') module_name = plugin_info.get_module_name() mod = __import__(module_name) sp = getattr(mod, "SearchPreferences") self._search_preferences = sp() self._search_preferences.plugin_info = plugin_info self._search_preferences.display_preferences_dialog(self._search_preferences) except: dialog = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("Please install and activate the latest version of the Coverart Search Providers plugin")) dialog.run() dialog.destroy() else: assert 1==2, ("unknown choice %s", choice) @classmethod def get_instance(cls, **kwargs): ''' Returns the unique instance of the manager. ''' if not cls.instance: cls.instance = CoverArtBrowserSource(**kwargs) return cls.instance
class CoverArtBrowserSource(RB.Source): ''' Source utilized by the plugin to show all it's ui. ''' rating_threshold = GObject.property(type=float, default=0) artist_paned_pos = GObject.property(type=str) min_paned_pos = 40 # unique instance of the source instance = None def __init__(self, **kargs): ''' Initializes the source. ''' super(CoverArtBrowserSource, self).__init__(**kargs) # create source_source_settings and connect the source's properties self.gs = GSetting() self._connect_properties() self.hasActivated = False self.last_width = 0 self.last_selected_album = None self.click_count = 0 self.favourites = False self.task_progress = None def _connect_properties(self): ''' Connects the source properties to the saved preferences. ''' print("CoverArtBrowser DEBUG - _connect_properties") setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting.bind(self.gs.PluginKey.RATING, self, 'rating_threshold', Gio.SettingsBindFlags.GET) print("CoverArtBrowser DEBUG - end _connect_properties") def do_get_status(self, *args): ''' Method called by Rhythmbox to figure out what to show on this source statusbar. If the custom statusbar is disabled, the source will show the selected album info. Also, it makes sure to show the progress on the album loading ''' try: # this will only work for RB3.0 and later if not self.task_progress: self.task_progress = RB.TaskProgressSimple.new() except: pass try: progress = self.album_manager.progress progress_text = _('Loading...') if progress < 1 else '' try: # this will only work for RB3.0 and later if progress < 1: if self.props.shell.props.task_list.get_model().n_items( ) == 0: self.props.shell.props.task_list.add_task( self.task_progress) self.task_progress.props.task_progress = progress self.task_progress.props.task_label = progress_text else: self.task_progress.props.task_outcome = RB.TaskOutcome.COMPLETE except: pass except: progress = 1 progress_text = '' try: # this will only work for RB3.0 and later self.task_progress.props.task_outcome = RB.TaskOutcome.COMPLETE except: pass return (self.status, progress_text, progress) def do_selected(self): ''' Called by Rhythmbox when the source is selected. It makes sure to create the ui the first time the source is showed. ''' print("CoverArtBrowser DEBUG - do_selected") # first time of activation -> add graphical stuff if not self.hasActivated: self.do_impl_activate() # indicate that the source was activated before self.hasActivated = True print("CoverArtBrowser DEBUG - end do_selected") def do_impl_activate(self): ''' Called by do_selected the first time the source is activated. It creates all the source ui and connects the necesary signals for it correct behavior. ''' print("CoverArtBrowser DEBUG - do_impl_activate") # initialise some variables self.plugin = self.props.plugin self.shell = self.props.shell self.status = '' self.search_text = '' self.actiongroup = ActionGroup(self.shell, 'coverplaylist_submenu') self._browser_preferences = None self._search_preferences = None # indicate that the source was activated before self.hasActivated = True self._create_ui() self._setup_source() self._apply_settings() print("CoverArtBrowser DEBUG - end do_impl_activate") def _create_ui(self): ''' Creates the ui for the source and saves the important widgets onto properties. ''' print("CoverArtBrowser DEBUG - _create_ui") # dialog has not been created so lets do so. cl = CoverLocale() ui = Gtk.Builder() ui.set_translation_domain(cl.Locale.LOCALE_DOMAIN) ui.add_from_file( rb.find_plugin_file(self.plugin, 'ui/coverart_browser.ui')) ui.connect_signals(self) # load the page and put it in the source self.page = ui.get_object('main_box') self.pack_start(self.page, True, True, 0) # get widgets for main icon-view self.status_label = ui.get_object('status_label') window = ui.get_object('scrolled_window') self.viewmgr = ViewManager(self, window) # get widgets for the artist paned self.artist_paned = ui.get_object('vertical_paned') Gdk.threads_add_timeout(GLib.PRIORITY_DEFAULT_IDLE, 50, self._change_artist_paned_pos, self.viewmgr.view_name) self.viewmgr.connect('new-view', self.on_view_changed) self.artist_treeview = ui.get_object('artist_treeview') self.artist_scrolledwindow = ui.get_object('artist_scrolledwindow') # define menu's self.popup_menu = Menu(self.plugin, self.shell) self.popup_menu.load_from_file('ui/coverart_browser_pop_rb2.ui', 'ui/coverart_browser_pop_rb3.ui') self._external_plugins = None signals = \ { 'play_album_menu_item': self.play_album_menu_item_callback, 'queue_album_menu_item': self.queue_album_menu_item_callback, 'new_playlist': self.add_playlist_menu_item_callback, 'cover_search_menu_item': self.cover_search_menu_item_callback, 'export_embed_menu_item': self.export_embed_menu_item_callback, 'show_properties_menu_item': self.show_properties_menu_item_callback, 'play_similar_artist_menu_item': self.play_similar_artist_menu_item_callback} self.popup_menu.connect_signals(signals) self.popup_menu.connect('pre-popup', self.add_external_menu) self.status_label = ui.get_object('status_label') self.request_status_box = ui.get_object('request_status_box') self.request_spinner = ui.get_object('request_spinner') self.request_statusbar = ui.get_object('request_statusbar') self.request_cancel_button = ui.get_object('request_cancel_button') self.paned = ui.get_object('paned') self.notebook = ui.get_object('bottom_notebook') #---- set up info pane -----# info_scrolled_window = ui.get_object('info_scrolled_window') info_button_box = ui.get_object('info_button_box') artist_info_paned = ui.get_object('vertical_info_paned') self.artist_info = ArtistInfoPane(info_button_box, info_scrolled_window, artist_info_paned, self) # quick search self.quick_search = ui.get_object('quick_search_entry') print("CoverArtBrowser DEBUG - end _create_ui") def _setup_source(self): ''' Setups the differents parts of the source so they are ready to be used by the user. It also creates and configure some custom widgets. ''' print("CoverArtBrowser DEBUG - _setup_source") cl = CoverLocale() cl.switch_locale(cl.Locale.LOCALE_DOMAIN) # setup iconview popup self.viewmgr.current_view.set_popup_menu(self.popup_menu) # setup entry-view objects and widgets setting = self.gs.get_setting(self.gs.Path.PLUGIN) setting.bind(self.gs.PluginKey.PANED_POSITION, self.paned, 'collapsible-y', Gio.SettingsBindFlags.DEFAULT) setting.bind(self.gs.PluginKey.DISPLAY_BOTTOM, self.paned.get_child2(), 'visible', Gio.SettingsBindFlags.DEFAULT) # create entry view. Don't allow to reorder until the load is finished self.entry_view = EV(self.shell, self) self.entry_view.set_columns_clickable(False) self.shell.props.library_source.get_entry_view().set_columns_clickable( False) self.stars = ReactiveStar() self.stars.set_rating(0) a = Gtk.Alignment.new(0.5, 0.5, 0, 0) a.add(self.stars) self.stars.connect('changed', self.rating_changed_callback) vbox = Gtk.Box() vbox.set_orientation(Gtk.Orientation.VERTICAL) vbox.pack_start(self.entry_view, True, True, 0) vbox.pack_start(a, False, False, 1) vbox.show_all() self.notebook.append_page(vbox, Gtk.Label.new_with_mnemonic(_("Tracks"))) # create an album manager self.album_manager = AlbumManager(self.plugin, self.viewmgr.current_view) self.viewmgr.current_view.initialise(self) # setup cover search pane colour = self.viewmgr.get_selection_colour() self.cover_search_pane = CoverSearchPane(self.plugin, colour) self.notebook.append_page(self.cover_search_pane, Gtk.Label.new_with_mnemonic(_("Covers"))) # connect a signal to when the info of albums is ready self.load_fin_id = self.album_manager.loader.connect( 'model-load-finished', self.load_finished_callback) # prompt the loader to load the albums self.album_manager.loader.load_albums(self.props.base_query_model) # initialise the variables of the quick search self.quick_search_controller = AlbumQuickSearchController( self.album_manager) self.quick_search_controller.connect_quick_search(self.quick_search) # set sensitivity of export menu item for iconview self.popup_menu.set_sensitive( 'export_embed_menu_item', CoverArtExport(self.plugin, self.shell, self.album_manager).is_search_plugin_enabled()) # setup the statusbar component self.statusbar = Statusbar(self) # initialise the toolbar manager self.toolbar_manager = ToolbarManager(self.plugin, self.page, self.viewmgr) self.viewmgr.current_view.emit('update-toolbar') cl.switch_locale(cl.Locale.RB) # setup the artist paned artist_pview = None for view in self.shell.props.library_source.get_property_views(): print(view.props.title) print(_("Artist")) if view.props.title == _("Artist"): artist_pview = view break assert artist_pview, "cannot find artist property view" self.artist_treeview.set_model(artist_pview.get_model()) setting.bind(self.gs.PluginKey.ARTIST_PANED_POSITION, self, 'artist-paned-pos', Gio.SettingsBindFlags.DEFAULT) self.artist_paned.connect('button-release-event', self.artist_paned_button_release_callback) # intercept JumpToPlaying Song action so that we can scroll to the playing album appshell = rb3compat.ApplicationShell(self.shell) action = appshell.lookup_action("", "jump-to-playing", "win") action.action.connect("activate", self.jump_to_playing, None) self.echonest_similar_playlist = None print("CoverArtBrowser DEBUG - end _setup_source") def add_external_menu(self, *args): ''' Callback when the popup menu is about to be displayed ''' if not self._external_plugins: # initialise external plugin menu support self._external_plugins = \ CreateExternalPluginMenu("ca_covers_view", 7, self.popup_menu) self._external_plugins.create_menu('popup_menu', True) self.playlist_menu_item_callback() def jump_to_playing(self, *args): ''' Callback when the JumpToPlaying action is invoked This will scroll the view to the playing song ''' if not self.shell.props.selected_page.props.name == self.props.name: # if the source page that was played from is not the plugin then # nothing to do return album = None entry = self.shell.props.shell_player.get_playing_entry() if entry: album = self.album_manager.model.get_from_dbentry(entry) self.viewmgr.current_view.scroll_to_album(album) def artist_paned_button_release_callback(self, *args): ''' Callback when the artist paned handle is released from its mouse click. ''' child_width = self._get_child_width() paned_positions = eval(self.artist_paned_pos) found = None for viewpos in paned_positions: if self.viewmgr.view_name in viewpos: found = viewpos break if not found: return paned_positions.remove(found) if child_width <= self.min_paned_pos: child_width = 0 self.artist_paned.set_position(child_width) paned_positions.append(self.viewmgr.view_name + ":" + str(child_width)) self.artist_paned_pos = repr(paned_positions) def on_view_changed(self, widget, view_name): self._change_artist_paned_pos(view_name) def _change_artist_paned_pos(self, view_name): paned_positions = eval(self.artist_paned_pos) print(paned_positions) found = None for viewpos in paned_positions: if view_name in viewpos: found = viewpos break print(found) if not found: return child_width = int(found.split(":")[1]) print(child_width) # odd case - if the pane is not visible but the position is zero # then the paned position on visible=true is some large arbitary value # hence - set it to be 1 px larger than the real value, then set it back # to its expected value self.artist_paned.set_position(child_width + 1) self.artist_paned.set_visible(True) self.artist_paned.set_position(child_width) def _get_child_width(self): child = self.artist_paned.get_child1() return child.get_allocated_width() def on_artist_treeview_selection_changed(self, view): model, artist_iter = view.get_selected() if artist_iter: artist = model[artist_iter][0] cl = CoverLocale() cl.switch_locale(cl.Locale.RB) #. TRANSLATORS - "All" is used in the context of "All artist names" if artist == _('All'): self.album_manager.model.remove_filter('quick_artist') else: self.album_manager.model.replace_filter('quick_artist', artist) cl.switch_locale(cl.Locale.LOCALE_DOMAIN) def _apply_settings(self): ''' Applies all the settings related to the source and connects those that must be updated when the preferences dialog changes it's values. Also enables differents parts of the ui if the settings says so. ''' print("CoverArtBrowser DEBUG - _apply_settings") # connect some signals to the loader to keep the source informed self.album_mod_id = self.album_manager.model.connect( 'album-updated', self.on_album_updated) self.notify_prog_id = self.album_manager.connect( 'notify::progress', lambda *args: self.notify_status_changed()) print("CoverArtBrowser DEBUG - end _apply_settings") def load_finished_callback(self, _): ''' Callback called when the loader finishes loading albums into the covers view model. ''' print("CoverArtBrowser DEBUG - load_finished_callback") #if not self.request_status_box.get_visible(): # it should only be enabled if no cover request is going on #self.source_menu_search_all_item.set_sensitive(True) # enable sorting on the entryview self.entry_view.set_columns_clickable(True) self.shell.props.library_source.get_entry_view().set_columns_clickable( True) print("CoverArtBrowser DEBUG - end load_finished_callback") def get_entry_view(self): return self.entry_view def on_album_updated(self, model, path, tree_iter): ''' Callback called by the album loader when one of the albums managed by him gets modified in some way. ''' album = model.get_from_path(path) selected = self.viewmgr.current_view.get_selected_objects() if album in selected: # update the selection since it may have changed self.viewmgr.current_view.selectionchanged_callback() if album is selected[0] and \ self.notebook.get_current_page() == \ self.notebook.page_num(self.cover_search_pane): # also, if it's the first, update the cover search pane self.cover_search_pane.clear() self.cover_search_pane.do_search( album, self.album_manager.cover_man.update_cover) def play_similar_artist_menu_item_callback(self, *args): ''' Callback called when the play similar artist option is selected from the cover view popup. It plays similar artists music. ''' def play_similar_artist_menu_item_callback(self, *args): if not self.echonest_similar_playlist: self.echonest_similar_playlist = \ EchoNestPlaylist( self.shell, self.shell.props.queue_source) selected_albums = self.viewmgr.current_view.get_selected_objects() album = selected_albums[0] tracks = album.get_tracks() entry = tracks[0].entry self.echonest_similar_playlist.start(entry, reinitialise=True) def show_properties_menu_item_callback(self, *args): ''' Callback called when the show album properties option is selected from the cover view popup. It shows a SongInfo dialog showing the selected albums' entries info, which can be modified. ''' print("CoverArtBrowser DEBUG - show_properties_menu_item_callback") self.entry_view.select_all() info_dialog = RB.SongInfo(source=self, entry_view=self.entry_view) info_dialog.show_all() print("CoverArtBrowser DEBUG - end show_properties_menu_item_callback") def play_selected_album(self, favourites=False): ''' Utilitary method that plays all entries from an album into the play queue. ''' # callback when play an album print("CoverArtBrowser DEBUG - play_selected_album") query_model = RB.RhythmDBQueryModel.new_empty(self.shell.props.db) self.queue_selected_album(query_model, favourites) if len(query_model) > 0: self.props.query_model = query_model # Start the music player = self.shell.props.shell_player player.play_entry(query_model[0][0], self) print("CoverArtBrowser DEBUG - end play_selected_album") def queue_selected_album(self, source, favourites=False): ''' Utilitary method that queues all entries from an album into the play queue. ''' print("CoverArtBrowser DEBUG - queue_selected_album") selected_albums = self.viewmgr.current_view.get_selected_objects() threshold = self.rating_threshold if favourites else 0 total = 0 for album in selected_albums: # Retrieve and sort the entries of the album tracks = album.get_tracks(threshold) total = total + len(tracks) # Add the songs to the play queue for track in tracks: source.add_entry(track.entry, -1) if total == 0 and threshold: dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("No tracks have been added because no tracks meet the favourite rating threshold" )) dialog.run() dialog.destroy() print("CoverArtBrowser DEBUG - end queue_select_album") def play_album_menu_item_callback(self, *args): ''' Callback called when the play album item from the cover view popup is selected. It cleans the play queue and queues the selected album. ''' print("CoverArtBrowser DEBUG - play_album_menu_item_callback") self.play_selected_album(self.favourites) print("CoverArtBrowser DEBUG - end play_album_menu_item_callback") def queue_album_menu_item_callback(self, *args): ''' Callback called when the queue album item from the cover view popup is selected. It queues the selected album at the end of the play queue. ''' print("CoverArtBrowser DEBUG - queue_album_menu_item_callback()") self.queue_selected_album(self.shell.props.queue_source, self.favourites) print("CoverArtBrowser DEBUG - end queue_album_menu_item_callback()") def playlist_menu_item_callback(self, *args): print("CoverArtBrowser DEBUG - playlist_menu_item_callback") self.playlist_fillmenu(self.popup_menu, 'playlist_submenu', 'playlist_section', self.actiongroup, self.add_to_static_playlist_menu_item_callback, self.favourites) def playlist_fillmenu(self, popup_menu, menubar, section_name, actiongroup, func, favourite=False): print("CoverArtBrowser DEBUG - playlist_fillmenu") playlist_manager = self.shell.props.playlist_manager playlists_entries = playlist_manager.get_playlists() # tidy up old playlists menu items before recreating the list actiongroup.remove_actions() popup_menu.remove_menu_items(menubar, section_name) if playlists_entries: for playlist in playlists_entries: if playlist.props.is_local and \ isinstance(playlist, RB.StaticPlaylistSource): args = (playlist, favourite) # take the name of the playlist, strip out non-english characters and reduce the string # to just a-to-z characters i.e. this will make the action_name valid in RB3 ascii_name = unicodedata.normalize('NFKD', \ rb3compat.unicodestr(playlist.props.name, 'utf-8')).encode('ascii','ignore') ascii_name = ascii_name.decode(encoding='UTF-8') ascii_name = re.sub(r'[^a-zA-Z]', '', ascii_name) action = actiongroup.add_action(func=func, action_name=ascii_name, playlist=playlist, favourite=favourite, label=playlist.props.name) popup_menu.add_menu_item(menubar, section_name, action) def add_to_static_playlist_menu_item_callback(self, action, param, args): print('''CoverArtBrowser DEBUG - add_to_static_playlist_menu_item_callback''') playlist = args['playlist'] favourite = args['favourite'] self.queue_selected_album(playlist, favourite) def add_playlist_menu_item_callback(self, *args): print('''CoverArtBrowser DEBUG - add_playlist_menu_item_callback''') playlist_manager = self.shell.props.playlist_manager playlist = playlist_manager.new_playlist(_('New Playlist'), False) self.queue_selected_album(playlist, self.favourites) def play_random_album_menu_item_callback(self, favourites=False): print( '''CoverArtBrowser DEBUG - play_random_album_menu_item_callback''') query_model = RB.RhythmDBQueryModel.new_empty(self.shell.props.db) num_albums = len(self.album_manager.model.store) #random_list = [] selected_albums = [] gs = GSetting() settings = gs.get_setting(gs.Path.PLUGIN) to_queue = settings[gs.PluginKey.RANDOM] if num_albums <= to_queue: dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("The number of albums to randomly play is less than that displayed." )) dialog.run() dialog.destroy() return album_col = self.album_manager.model.columns['album'] chosen = {} # now loop through finding unique random albums # i.e. ensure we dont queue the same album twice for loop in range(0, to_queue): while True: pos = random.randint(0, num_albums - 1) if pos not in chosen: chosen[pos] = None selected_albums.append( self.album_manager.model.store[pos][album_col]) break threshold = self.rating_threshold if favourites else 0 total = 0 for album in selected_albums: # Retrieve and sort the entries of the album tracks = album.get_tracks(threshold) total = total + len(tracks) # Add the songs to the play queue for track in tracks: query_model.add_entry(track.entry, -1) if total == 0 and threshold: dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("No tracks have been added because no tracks meet the favourite rating threshold" )) dialog.run() dialog.destroy() self.props.query_model = query_model # Start the music player = self.shell.props.shell_player player.play_entry(query_model[0][0], self) print("CoverArtBrowser DEBUG - end play_selected_album") def cover_search_menu_item_callback(self, *args): ''' Callback called when the search cover option is selected from the cover view popup. It prompts the album loader to retrieve the selected album cover ''' print("CoverArtBrowser DEBUG - cover_search_menu_item_callback()") selected_albums = self.viewmgr.current_view.get_selected_objects() self.request_status_box.show_all() self.album_manager.cover_man.search_covers( selected_albums, self.update_request_status_bar) print("CoverArtBrowser DEBUG - end cover_search_menu_item_callback()") def export_embed_menu_item_callback(self, *args): ''' Callback called when the export and embed coverart option is selected from the cover view popup. It prompts the exporter to copy and embed art for the albums chosen ''' print("CoverArtBrowser DEBUG - export_embed_menu_item_callback()") selected_albums = self.viewmgr.current_view.get_selected_objects() CoverArtExport(self.plugin, self.shell, self.album_manager).embed_albums(selected_albums) print("CoverArtBrowser DEBUG - export_embed_menu_item_callback()") def update_request_status_bar(self, coverobject): ''' Callback called by the album loader starts performing a new cover request. It prompts the source to change the content of the request statusbar. ''' print("CoverArtBrowser DEBUG - update_request_status_bar") if coverobject: # for example "Requesting the picture cover for the music artist Michael Jackson" tranlation_string = _('Requesting cover for %s...') self.request_statusbar.set_text( rb3compat.unicodedecode( _('Requesting cover for %s...') % (coverobject.name), 'UTF-8')) else: self.request_status_box.hide() self.popup_menu.set_sensitive('cover_search_menu_item', True) self.request_cancel_button.set_sensitive(True) print("CoverArtBrowser DEBUG - end update_request_status_bar") def cancel_request_callback(self, _): ''' Callback connected to the cancel button on the request statusbar. When called, it prompts the album loader to cancel the full cover search after the current cover. ''' print("CoverArtBrowser DEBUG - cancel_request_callback") self.request_cancel_button.set_sensitive(False) self._cover_search_manager.cover_man.cancel_cover_request() print("CoverArtBrowser DEBUG - end cancel_request_callback") def notebook_switch_page_callback(self, notebook, page, page_num): ''' Callback called when the notebook page gets switched. It initiates the cover search when the cover search pane's page is selected. ''' print("CoverArtBrowser DEBUG - notebook_switch_page_callback") if page_num == 1: self.viewmgr.current_view.switch_to_coverpane( self.cover_search_pane) print("CoverArtBrowser DEBUG - end notebook_switch_page_callback") def rating_changed_callback(self, widget): ''' Callback called when the Rating stars is changed ''' print("CoverArtBrowser DEBUG - rating_changed_callback") rating = widget.get_rating() for album in self.viewmgr.current_view.get_selected_objects(): album.rating = rating print("CoverArtBrowser DEBUG - end rating_changed_callback") def show_hide_pane(self, params): ''' helper function - if the entry is manually expanded then if necessary scroll the view to the last selected album params is "album" or a tuple of "album" and "force_expand" boolean ''' if isinstance(params, tuple): album, force = params else: album = params force = PanedCollapsible.Paned.DEFAULT if (album and self.click_count == 1 \ and self.last_selected_album is album) or force != PanedCollapsible.Paned.DEFAULT: # check if it's a second or third click on the album and expand # or collapse the entry view accordingly self.paned.expand(force) # update the selected album selected = self.viewmgr.current_view.get_selected_objects() self.last_selected_album = selected[0] if len(selected) == 1 else None # clear the click count self.click_count = 0 def update_with_selection(self): ''' Update the source view when an item gets selected. ''' selected = self.viewmgr.current_view.get_selected_objects() # clear the entry view self.entry_view.clear() cover_search_pane_visible = self.notebook.get_current_page() == \ self.notebook.page_num(self.cover_search_pane) if not selected: # clean cover tab if selected if cover_search_pane_visible: self.cover_search_pane.clear() return elif len(selected) == 1: self.stars.set_rating(selected[0].rating) if selected[0] is not self.last_selected_album: # when the selection changes we've to take into account two # things if not self.click_count: # we may be using the arrows, so if there is no mouse # involved, we should change the last selected self.last_selected_album = selected[0] else: # we may've doing a fast change after a valid second click, # so it shouldn't be considered a double click self.click_count -= 1 else: self.stars.set_rating(0) if len(selected) == 1: self.artist_info.emit('selected', selected[0].artist, selected[0].name) for album in selected: # add the album to the entry_view self.entry_view.add_album(album) # update the cover search pane with the first selected album if cover_search_pane_visible: self.cover_search_pane.do_search( selected[0], self.album_manager.cover_man.update_cover) self.statusbar.emit('display-status', self.viewmgr.current_view) def propertiesbutton_callback(self, choice): if choice == 'download': self.request_status_box.show_all() self._cover_search_manager = self.viewmgr.current_view.get_default_manager( ) self._cover_search_manager.cover_man.search_covers( callback=self.update_request_status_bar) elif choice == 'random': self.play_random_album_menu_item_callback() elif choice == 'random favourite': self.play_random_album_menu_item_callback(True) elif choice == 'favourite': self.favourites = not self.favourites self.viewmgr.current_view.set_popup_menu(self.popup_menu) elif choice == 'browser prefs': if not self._browser_preferences: self._browser_preferences = Preferences() self._browser_preferences.display_preferences_dialog(self.plugin) elif choice == 'search prefs': try: if not self._search_preferences: from gi.repository import Peas peas = Peas.Engine.get_default() plugin_info = peas.get_plugin_info( 'coverart_search_providers') module_name = plugin_info.get_module_name() mod = __import__(module_name) sp = getattr(mod, "SearchPreferences") self._search_preferences = sp() self._search_preferences.plugin_info = plugin_info self._search_preferences.display_preferences_dialog( self._search_preferences) except: dialog = Gtk.MessageDialog( None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, _("Please install and activate the latest version of the Coverart Search Providers plugin" )) dialog.run() dialog.destroy() else: assert 1 == 2, ("unknown choice %s", choice) @classmethod def get_instance(cls, **kwargs): ''' Returns the unique instance of the manager. ''' if not cls.instance: cls.instance = CoverArtBrowserSource(**kwargs) return cls.instance