def _album(self, songs, box): text = [] discs = {} for song in songs: try: discs[song("~#disc")] = int( song["tracknumber"].split("/")[1]) except (AttributeError, ValueError, IndexError, KeyError): discs[song("~#disc")] = max([ song("~#track", discs.get(song("~#disc"), 0))]) tracks = sum(discs.values()) discs = len(discs) length = sum([song.get("~#length", 0) for song in songs]) if tracks == 0 or tracks < len(songs): tracks = len(songs) parts = [] if discs > 1: parts.append( ngettext("%d disc", "%d discs", discs) % discs) parts.append( ngettext("%d track", "%d tracks", tracks) % tracks) if tracks != len(songs): parts.append(ngettext("%d selected", "%d selected", len(songs)) % len(songs)) text.append(", ".join(parts)) text.append(util.format_time_preferred(length)) if "location" in song: text.append(util.escape(song["location"])) if "organization" in song or "labelid" in song: t = util.escape(song.comma("~organization~labelid")) text.append(t) if "producer" in song: text.append(_("Produced by %s") % ( util.escape(song.comma("producer")))) w = Label("") w.set_ellipsize(Pango.EllipsizeMode.END) w.set_markup("\n".join(text)) hb = Gtk.HBox(spacing=12) cover = CoverImage() cover.set_property('no-show-all', True) hb.pack_start(cover, False, True, 0) def show_cover(cover, success): if success: cover.show() cover.disconnect(signal_id) signal_id = cover.connect('cover-visible', show_cover) cover.set_song(song) hb.pack_start(w, True, True, 0) box.pack_start(hb, False, False, 0)
def _album(self, songs, box): text = [] discs = {} for song in songs: try: discs[song("~#disc")] = int(song["tracknumber"].split("/")[1]) except (AttributeError, ValueError, IndexError, KeyError): discs[song("~#disc")] = max( [song("~#track", discs.get(song("~#disc"), 0))]) tracks = sum(discs.values()) discs = len(discs) length = sum([song.get("~#length", 0) for song in songs]) if tracks == 0 or tracks < len(songs): tracks = len(songs) parts = [] if discs > 1: parts.append(ngettext("%d disc", "%d discs", discs) % discs) parts.append(ngettext("%d track", "%d tracks", tracks) % tracks) if tracks != len(songs): parts.append( ngettext("%d selected", "%d selected", len(songs)) % len(songs)) text.append(", ".join(parts)) text.append(util.format_time_preferred(length)) if "location" in song: text.append(util.escape(song["location"])) if "organization" in song or "labelid" in song: t = util.escape(song.comma("~organization~labelid")) text.append(t) if "producer" in song: text.append( _("Produced by %s") % (util.escape(song.comma("producer")))) w = Label("") w.set_ellipsize(Pango.EllipsizeMode.END) w.set_markup("\n".join(text)) hb = Gtk.HBox(spacing=12) cover = CoverImage() cover.set_property('no-show-all', True) hb.pack_start(cover, False, True, 0) def show_cover(cover, success): if success: cover.show() cover.disconnect(signal_id) signal_id = cover.connect('cover-visible', show_cover) cover.set_song(song) hb.pack_start(w, True, True, 0) box.pack_start(hb, False, False, 0)
def plugin_songs(self, songs): desc = ngettext("%d song", "%d songs", len(songs)) % len(songs) win = ConsoleWindow(create_console(songs), title=desc) win.set_icon_name(self.PLUGIN_ICON) win.set_title(_("{plugin_name} for {songs} ({app})").format( plugin_name=self.PLUGIN_NAME, songs=desc, app=app.name)) win.show_all()
def do_import(parent, library): db_path = expanduser(BansheeImport.USR_PATH) importer = BansheeDBImporter(library) try: db = sqlite3.connect(db_path) importer.read(db) db.close() except sqlite3.OperationalError: msg = _("Specified Banshee database is malformed or missing") WarningMessage(parent, BansheeImport.PLUGIN_NAME, msg).run() except Exception: util.print_exc() importer.finish() msg = _("Import Failed") # FIXME: don't depend on the plugin class here ErrorMessage(parent, BansheeImport.PLUGIN_NAME, msg).run() else: count = importer.finish() msg = ngettext( "Successfully imported ratings and statistics for %d song", "Successfully imported ratings and statistics for %d songs", count) % count Message(Gtk.MessageType.INFO, parent, BansheeImport.PLUGIN_NAME, msg).run()
def _album(self, songs, box): albums, noalbum = _sort_albums(songs) def format(args): date, song, album = args markup = "<big><i>%s</i></big>" % util.escape(album) return "%s (%s)" % (markup, date[:4]) if date else markup get_cover = app.cover_manager.get_cover covers = [(a, get_cover(s), s) for d, s, a in albums] albums = [format(a) for a in albums] if noalbum: albums.append(ngettext("%d song with no album", "%d songs with no album", noalbum) % noalbum) l = Label(markup="\n".join(albums), ellipsize=True) box.pack_start(Frame(_("Selected Discography"), l), False, False, 0) covers = [ac for ac in covers if bool(ac[1])] t = Gtk.Table(n_rows=4, n_columns=(len(covers) // 4) + 1) t.set_col_spacings(12) t.set_row_spacings(12) added = set() for i, (album, cover, song) in enumerate(covers): if cover.name in added: continue cov = ReactiveCoverImage(song=song, tooltip=album) c = i % 4 r = i // 4 t.attach(cov, c, c + 1, r, r + 1, xoptions=Gtk.AttachOptions.EXPAND, yoptions=0) added.add(cover.name) box.pack_start(t, True, True, 0)
def _album(self, songs, box): text = [] discs = {} for song in songs: try: discs[song("~#disc")] = int( song["tracknumber"].split("/")[1]) except (AttributeError, ValueError, IndexError, KeyError): discs[song("~#disc")] = max([ song("~#track", discs.get(song("~#disc"), 0))]) tracks = sum(discs.values()) discs = len(discs) length = sum([song.get("~#length", 0) for song in songs]) if tracks == 0 or tracks < len(songs): tracks = len(songs) parts = [] if discs > 1: parts.append( ngettext("%d disc", "%d discs", discs) % discs) parts.append( ngettext("%d track", "%d tracks", tracks) % tracks) if tracks != len(songs): parts.append(ngettext("%d selected", "%d selected", len(songs)) % len(songs)) text.append(", ".join(parts)) text.append("(%s)" % util.format_time_preferred(length)) if "location" in song: text.append(util.escape(song["location"])) if "organization" in song or "labelid" in song: t = util.escape(song.comma("~organization~labelid")) text.append(t) if "producer" in song: text.append(_("Produced by %s") % ( util.escape(song.comma("producer")))) w = Label(markup="\n".join(text), ellipsize=True) hb = Gtk.HBox(spacing=12) hb.pack_start(w, True, True, 0) hb.pack_start(ReactiveCoverImage(song=song), False, True, 0) box.pack_start(hb, False, False, 0)
def confirm_multi_album_invoke(parent, plugin_name, count): """Dialog to confirm invoking a plugin with X albums in case X is high""" title = ngettext("Run the plugin \"%(name)s\" on %(count)d album?", "Run the plugin \"%(name)s\" on %(count)d albums?", count) % {"name": plugin_name, "count": count} description = "" ok_text = _("_Run Plugin") prompt = ConfirmationPrompt(parent, title, description, ok_text).run() return prompt == ConfirmationPrompt.RESPONSE_INVOKE
def __update_count(self, model, path, lab): if len(model) == 0: text = "" else: time = sum([row[0].get("~#length", 0) for row in model]) text = ngettext("%(count)d song (%(time)s)", "%(count)d songs (%(time)s)", len(model)) % { "count": len(model), "time": format_time_preferred(time)} lab.set_text(text)
def suggested_name_for(cls, songs): if len(songs) == 1: title = songs[0].comma("title") else: title = ngettext( "%(title)s and %(count)d more", "%(title)s and %(count)d more", len(songs) - 1) % ( {'title': songs[0].comma("title"), 'count': len(songs) - 1}) return title
def suggested_name_for(songs): if len(songs) == 0: return _("Empty Playlist") elif len(songs) == 1: return songs[0].comma("title") else: return ngettext("%(title)s and %(count)d more", "%(title)s and %(count)d more", len(songs) - 1) % ({ "title": songs[0].comma("title"), "count": len(songs) - 1 })
def confirm_multi_playlist_invoke(parent, plugin_name, count): """Dialog to confirm invoking a plugin with X playlists in case X is high """ params = {"name": plugin_name, "count": format_int_locale(count)} title = ngettext("Run the plugin \"%(name)s\" on %(count)s playlist?", "Run the plugin \"%(name)s\" on %(count)s playlists?", count) % params description = "" ok_text = _("_Run Plugin") prompt = ConfirmationPrompt(parent, title, description, ok_text).run() return prompt == ConfirmationPrompt.RESPONSE_INVOKE
def _update_sync_summary(self): """ Update the synchronization summary text field. """ sync_summary_prefix = _('Synchronization has:') + self.summary_sep sync_summary = [] if self.c_files_copy > 0 or self.c_files_skip > 0: text = [] counter = self.c_files_copy text.append( ngettext('written {count}/{total} file', 'written {count}/{total} files', counter).format(count=counter, total=self.c_songs_copy)) if self.c_files_skip > 0: counter = self.c_files_skip text.append( ngettext('(skipped {count} existing file)', '(skipped {count} existing files)', counter).format(count=counter)) sync_summary.append(self.summary_sep.join(text)) if self.c_files_dupes > 0: counter = self.c_files_dupes sync_summary.append( ngettext('skipped {count}/{total} duplicate file', 'skipped {count}/{total} duplicate files', counter).format(count=counter, total=self.c_song_dupes)) if self.c_files_delete > 0: counter = self.c_files_delete sync_summary.append( ngettext('deleted {count}/{total} file', 'deleted {count}/{total} files', counter).format(count=counter, total=self.c_songs_delete)) if self.c_files_failed > 0: counter = self.c_files_failed sync_summary.append( ngettext('failed to sync {count} file', 'failed to sync {count} files', counter).format(count=counter)) if self.c_files_skip_previous > 0: counter = self.c_files_skip_previous sync_summary.append( ngettext('skipped {count} file synchronized previously', 'skipped {count} files synchronized previously', counter).format(count=counter)) sync_summary_text = self.summary_sep_list.join(sync_summary) sync_summary_text = sync_summary_prefix + sync_summary_text self.status_progress.set_label(sync_summary_text) print_d(sync_summary_text)
def __init__(self, parent, playlist, songs): desc = ngettext("What do you want to do with that %d song?", "What do you want to do with those %d songs?", len(songs)) % len(songs) title = _("Confirm action for playlist \"%s\"") % playlist.name super().__init__(Gtk.MessageType.QUESTION, parent, title, desc, Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Add"), Icons.LIST_ADD, self.ADD) self.add_icon_button(_("_Remove"), Icons.LIST_REMOVE, self.REMOVE)
def _update_preview_summary(self): """ Update the preview summary text field. """ prefix = _('Synchronization will:') + self.summary_sep preview_progress = [] if self.c_songs_copy > 0: counter = self.c_songs_copy preview_progress.append( ngettext('attempt to write {count} file', 'attempt to write {count} files', counter).format(count=counter)) if self.c_song_dupes > 0: counter = self.c_song_dupes preview_progress.append( ngettext('skip {count} duplicate file', 'skip {count} duplicate files', counter).format(count=counter)) for child in self.status_duplicates.get_children(): child.set_visible(True) self.status_duplicates.set_visible(True) if self.c_songs_delete > 0: counter = self.c_songs_delete preview_progress.append( ngettext('delete {count} file', 'delete {count} files', counter).format(count=counter)) for child in self.status_deletions.get_children(): child.set_visible(True) self.status_deletions.set_visible(True) preview_progress_text = self.summary_sep_list.join(preview_progress) if preview_progress_text: preview_progress_text = prefix + preview_progress_text self.status_progress.set_label(preview_progress_text) self.status_progress.set_visible(True) print_d(preview_progress_text)
def _album(self, songs, box): noalbum = 0 albums = {} for song in songs: if "album" in song: albums[song.list("album")[0]] = song else: noalbum += 1 albums = [(song.get("date"), song, album) for album, song in albums.items()] albums.sort() def format(args): date, song, album = args if date: return "%s (%s)" % (album, date[:4]) else: return album get_cover = app.cover_manager.get_cover covers = [(a, get_cover(s), s) for d, s, a in albums] albums = map(format, albums) if noalbum: albums.append( ngettext("%d song with no album", "%d songs with no album", noalbum) % noalbum) l = Label("\n".join(albums)) l.set_ellipsize(Pango.EllipsizeMode.END) box.pack_start(Frame(_("Selected Discography"), l), False, False, 0) covers = [ac for ac in covers if bool(ac[1])] t = Gtk.Table(n_rows=4, n_columns=(len(covers) // 4) + 1) t.set_col_spacings(12) t.set_row_spacings(12) added = set() for i, (album, cover, song) in enumerate(covers): if cover.name in added: continue cov = CoverImage(song=song) cov.get_child().set_tooltip_text(album) c = i % 4 r = i // 4 t.attach(cov, c, c + 1, r, r + 1, xoptions=Gtk.AttachOptions.EXPAND, yoptions=0) added.add(cover.name) box.pack_start(t, True, True, 0)
def __init__(self, parent, playlist, count): title = ngettext("Are you sure you want to remove %d duplicate song?", "Are you sure you want to remove %d duplicate songs?", count) % count description = (_("The duplicate songs will be removed " "from the playlist '%s'.") % playlist.name) super(ConfirmRemoveDuplicatesDialog, self).__init__( Gtk.MessageType.WARNING, parent, title, description, Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Remove"), Icons.LIST_REMOVE, Gtk.ResponseType.YES)
def __init__(self, parent, playlist, count): title = ngettext( "Are you sure you want to remove %d duplicate song?", "Are you sure you want to remove %d duplicate songs?", count) % count description = (_("The duplicate songs will be removed " "from the playlist '%s'.") % playlist.name) super().__init__(Gtk.MessageType.WARNING, parent, title, description, Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Remove"), Icons.LIST_REMOVE, Gtk.ResponseType.YES)
def confirm_song_removal_invoke(parent, songs): songs = set(songs) if not songs: return True count = len(songs) song = next(iter(songs)) title = (ngettext("Remove track: \"%%(title)s\" from library?", "Remove %(count)d tracks from library?", count) % {'count': count}) % { 'title': song('title') or song('~basename')} return ConfirmationPrompt.RESPONSE_INVOKE == ConfirmationPrompt( parent, title, "", _("Remove from Library")).run()
def __init__(self, parent, plugin_name, count): title = ngettext("Run the plugin \"%(name)s\" on %(count)d album?", "Run the plugin \"%(name)s\" on %(count)d albums?", count) % {'name': plugin_name, 'count': count} super(ConfirmMultiAlbumInvoke, self).__init__( get_top_parent(parent), title, "", buttons=Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Run Plugin"), Icons.SYSTEM_RUN, self.RESPONSE_INVOKE) self.set_default_response(Gtk.ResponseType.CANCEL)
def __init__(self, parent, playlist, songs): desc = ngettext("What do you want to do with that %d song?", "What do you want to do with those %d songs?", len(songs)) % len(songs) title = _("Confirm action for playlist \"%s\"") % playlist.name super(ConfirmMultipleSongsAction, self).__init__( Gtk.MessageType.QUESTION, parent, title, desc, Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Add"), Icons.LIST_ADD, self.ADD) self.add_icon_button(_("_Remove"), Icons.LIST_REMOVE, self.REMOVE)
def format_time_long(time, limit=2): """Turn a time value in seconds into x hours, x minutes, etc. `limit` limits the count of units used, so the result will be <= time. 0 means no limit. """ from quodlibet import ngettext if time < 1: return _("No time information") cutoffs = [ (60, lambda n: ngettext("%d second", "%d seconds", n)), (60, lambda n: ngettext("%d minute", "%d minutes", n)), (24, lambda n: ngettext("%d hour", "%d hours", n)), (365, lambda n: ngettext("%d day", "%d days", n)), (None, lambda n: ngettext("%d year", "%d years", n)), ] time_str = [] for divisor, gettext_partial in cutoffs: if time < 1: break if divisor is None: time, unit = 0, time else: time, unit = divmod(time, divisor) if unit: time_str.append(gettext_partial(unit) % unit) time_str.reverse() if limit: time_str = time_str[:limit] return ", ".join(time_str)
def __on_changed(self, widget, songs): if songs: if len(songs) == 1: title = songs[0].comma("title") else: title = ngettext( "%(title)s and %(count)d more", "%(title)s and %(count)d more", len(songs) - 1) % {'title': songs[0].comma("title"), 'count': len(songs) - 1} self.set_title("%s - %s" % (title, _("Properties"))) else: self.set_title(_("Properties")) self.set_pending(None)
def __init__(self, parent, plugin_name, count): params = {"name": plugin_name, "count": format_int_locale(count)} title = ngettext("Run the plugin \"%(name)s\" on %(count)s playlist?", "Run the plugin \"%(name)s\" on %(count)s playlists?", count) % params super(ConfirmMultiPlaylistInvoke, self).__init__( get_top_parent(parent), title, "", buttons=Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button( _("_Run Plugin"), Icons.SYSTEM_RUN, self.RESPONSE_INVOKE) self.set_default_response(Gtk.ResponseType.CANCEL)
def __popup(self, view, library): albums = self.__get_selected_albums() songs = self.__get_songs_from_albums(albums) items = [] if self.__cover_column.get_visible(): num = len(albums) button = MenuItem( ngettext("Reload album _cover", "Reload album _covers", num), Icons.VIEW_REFRESH) button.connect('activate', self.__refresh_album, view) items.append(button) menu = SongsMenu(library, songs, items=[items]) menu.show_all() return view.popup_menu(menu, 0, Gtk.get_current_event_time())
def cell_data(view, cell, model, iter_, data): album = model.get_album(iter_) if album is None: text = "<b>%s</b>" % _("All Albums") text += "\n" + ngettext("%d album", "%d albums", len(model) - 1) % (len(model) - 1) markup = text else: markup = self.display_pattern % album if self.__last_render == markup: return self.__last_render = markup cell.markup = markup cell.set_property('markup', markup)
def __popup(self, view, library): albums = self.__get_selected_albums() songs = self.__get_songs_from_albums(albums) items = [] num = len(albums) button = MenuItem( ngettext("Reload album _cover", "Reload album _covers", num), Icons.VIEW_REFRESH) button.connect('activate', self.__refresh_album, view) items.append(button) menu = SongsMenu(library, songs, items=[items]) menu.show_all() popup_menu_at_widget(menu, view, Gdk.BUTTON_SECONDARY, Gtk.get_current_event_time())
def __init__(self, parent, plugin_name, count): params = {"name": plugin_name, "count": format_int_locale(count)} title = ngettext( "Run the plugin \"%(name)s\" on %(count)s playlist?", "Run the plugin \"%(name)s\" on %(count)s playlists?", count) % params super(ConfirmMultiPlaylistInvoke, self).__init__(get_top_parent(parent), title, "", buttons=Gtk.ButtonsType.NONE) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Run Plugin"), Icons.SYSTEM_RUN, self.RESPONSE_INVOKE) self.set_default_response(Gtk.ResponseType.CANCEL)
def _album(self, songs, box): noalbum = 0 albums = {} for song in songs: if "album" in song: albums[song.list("album")[0]] = song else: noalbum += 1 albums = [(song.get("date"), song, album) for album, song in albums.items()] albums.sort() def format(args): date, song, album = args if date: return "%s (%s)" % (album, date[:4]) else: return album get_cover = app.cover_manager.get_cover covers = [(a, get_cover(s), s) for d, s, a in albums] albums = map(format, albums) if noalbum: albums.append(ngettext("%d song with no album", "%d songs with no album", noalbum) % noalbum) l = Label("\n".join(albums)) l.set_ellipsize(Pango.EllipsizeMode.END) box.pack_start(Frame(_("Selected Discography"), l), False, False, 0) covers = [ac for ac in covers if bool(ac[1])] t = Gtk.Table(n_rows=4, n_columns=(len(covers) // 4) + 1) t.set_col_spacings(12) t.set_row_spacings(12) added = set() for i, (album, cover, song) in enumerate(covers): if cover.name in added: continue cov = CoverImage(song=song) cov.get_child().set_tooltip_text(album) c = i % 4 r = i // 4 t.attach(cov, c, c + 1, r, r + 1, xoptions=Gtk.AttachOptions.EXPAND, yoptions=0) added.add(cover.name) box.pack_start(t, True, True, 0)
def _people(self, songs, box): artists = set() none = 0 for song in songs: if "artist" in song: artists.update(song.list("artist")) else: none += 1 artists = sorted(artists) num_artists = len(artists) if none: artists.append(ngettext("%d song with no artist", "%d songs with no artist", none) % none) label = Label(markup=util.escape("\n".join(artists)), ellipsize=True) frame = Frame("%s (%d)" % (util.capitalize(_("artists")), num_artists), label) box.pack_start(frame, False, False, 0)
def _show_tooltip(self, widget, x, y, keyboard_tip, tooltip): w = self.scrollwin.get_hadjustment().get_value() z = self.scrollwin.get_vadjustment().get_value() path = widget.get_path_at_pos(int(x + w), int(y + z)) if path is None: return False model = widget.get_model() iter = model.get_iter(path) album = model.get_album(iter) if album is None: text = "<b>%s</b>" % _("All Albums") text += "\n" + ngettext("%d album", "%d albums", len(model) - 1) % (len(model) - 1) markup = text else: markup = self.display_pattern % album tooltip.set_markup(markup) return True
def _album(self, songs, box): albums = set() none = 0 for song in songs: if "album" in song: albums.update(song.list("album")) else: none += 1 albums = sorted(albums) num_albums = len(albums) if none: albums.append(ngettext("%d song with no album", "%d songs with no album", none) % none) box.pack_start(Frame( "%s (%d)" % (util.capitalize(_("albums")), num_albums), Label("\n".join(albums))), False, False, 0)
def _album(self, songs, box): albums = set() none = 0 for song in songs: if "album" in song: albums.update(song.list("album")) else: none += 1 albums = sorted(albums) num_albums = len(albums) if none: albums.append( ngettext("%d song with no album", "%d songs with no album", none) % none) box.pack_start( Frame("%s (%d)" % (util.capitalize(_("albums")), num_albums), Label("\n".join(albums))), False, False, 0)
def __remove(self, view) -> None: selection = self.view.get_selection() model, paths = selection.get_selected_rows() gone_dirs = [model[p][0] for p in paths or []] total = len(gone_dirs) if not total: return msg = (_("Remove {dir!r} and all its tracks?").format( dir=gone_dirs[0]) if total == 1 else _("Remove {n} library paths and their tracks?").format(n=total)) title = ngettext("Remove library path?", "Remove library paths?", total) prompt = ConfirmationPrompt(self, title, msg, _("Remove"), ok_button_icon=Icons.LIST_REMOVE) if prompt.run() == ConfirmationPrompt.RESPONSE_INVOKE: view.remove_selection() self.__save()
def download_cb(menu_item): songs = relevant total = len(songs) msg = ngettext("Download {name!r} to", "Download {total} files to", total) msg = msg.format( name=next(iter(songs))("title")[:99] if total else "?", total=total) chooser = folder_chooser or choose_folders paths = chooser(None, msg, _("Download here"), allow_multiple=False) if not paths: print_d("Cancelling download") return path = paths[0] progress = DownloadProgress(songs) progress.connect('finished', _finished) copool.add(progress.download_songs, path)
def __changed(self, selector, selection, label): model, rows = selection.get_selected_rows() files = [] if len(rows) < 2: count = len(model or []) else: count = len(rows) label.set_text(numeric_phrase("%d song", "%d songs", count)) for row in rows: filename = model[row][0] if not os.path.exists(filename): pass elif filename in self.__library: song = self.__library[filename] if song("~#mtime") + 1. < mtime(filename): try: song.reload() except AudioFileError: pass files.append(song) else: files.append(formats.MusicFile(filename)) files = list(filter(None, files)) if len(files) == 0: self.set_title("Ex Falso") elif len(files) == 1: self.set_title("%s - Ex Falso" % files[0].comma("title")) else: params = ({'title': files[0].comma("title"), 'count': format_int_locale(len(files) - 1)}) self.set_title( "%s - Ex Falso" % (ngettext("%(title)s and %(count)s more", "%(title)s and %(count)s more", len(files) - 1) % params)) self.__library.add(files) self.emit('changed', files)
def _album(self, songs, box): albums = set() none = 0 for song in songs: if "album" in song: albums.update(song.list("album")) else: none += 1 albums = sorted(albums) num_albums = len(albums) markup = "\n".join("<i>%s</i>" % util.escape(a) for a in albums) if none: text = ngettext("%d song with no album", "%d songs with no album", none) % none markup += "\n%s" % util.escape(text) label = Label() label.set_markup(markup) box.pack_start( Frame("%s (%d)" % (util.capitalize(_("albums")), num_albums), label), False, False, 0)
def __changed(self, selector, selection, label): model, rows = selection.get_selected_rows() files = [] if len(rows) < 2: count = len(model or []) else: count = len(rows) label.set_text(numeric_phrase("%d song", "%d songs", count)) for row in rows: filename = model[row][0] if not os.path.exists(filename): pass elif filename in self.__library: song = self.__library[filename] if song("~#mtime") + 1. < mtime(filename): try: song.reload() except AudioFileError: pass files.append(song) else: files.append(formats.MusicFile(filename)) files = list(filter(None, files)) if len(files) == 0: self.set_title("Ex Falso") elif len(files) == 1: self.set_title("%s - Ex Falso" % files[0].comma("title")) else: params = ({ 'title': files[0].comma("title"), 'count': format_int_locale(len(files) - 1) }) self.set_title("%s - Ex Falso" % (ngettext( "%(title)s and %(count)s more", "%(title)s and %(count)s more", len(files) - 1) % params)) self.__library.add(files) self.emit('changed', files)
def __popup(self, view, event, library): x = int(event.x) y = int(event.y) current_path = view.get_path_at_pos(x, y) if event.button == Gdk.BUTTON_SECONDARY and current_path: if not view.path_is_selected(current_path): view.unselect_all() view.select_path(current_path) albums = self.__get_selected_albums() songs = self.__get_songs_from_albums(albums) items = [] num = len(albums) button = MenuItem( ngettext("Reload album _cover", "Reload album _covers", num), Icons.VIEW_REFRESH) button.connect('activate', self.__refresh_album, view) items.append(button) menu = SongsMenu(library, songs, items=[items]) menu.show_all() menu.popup(None, None, None, event.button, event.time, Gtk.get_current_event_time())
def _album(self, songs, box): albums = set() none = 0 for song in songs: if "album" in song: albums.update(song.list("album")) else: none += 1 albums = sorted(albums) num_albums = len(albums) markup = "\n".join("<i>%s</i>" % util.escape(a) for a in albums) if none: text = ngettext("%d song with no album", "%d songs with no album", none) % none markup += "\n%s" % util.escape(text) label = Label() label.set_markup(markup) box.pack_start(Frame( "%s (%d)" % (util.capitalize(_("albums")), num_albums), label), False, False, 0)
def format_time_seconds(time): from quodlibet import ngettext time_str = locale.format("%d", time, grouping=True) return ngettext("%s second", "%s seconds", time) % time_str
def format_time_seconds(time): from quodlibet import ngettext time_str = format_int_locale(time) return ngettext("%s second", "%s seconds", time) % time_str
def __str__(self): songs_text = (ngettext("%d song", "%d songs", len(self.songs)) % len(self.songs)) return u"\"%s\" (%s)" % (self.name, songs_text)
def __get_value(self, key): """This is similar to __call__ in the AudioFile class. All internal tags are changed to represent a collection of songs. """ # Using key:<func> runs the resulting list of values # through the function before returning it. # Numeric keys without a func will default to a reasonable function if key.startswith("~#"): key = key[2:] if key[-4:-3] == ":": func = key[-3:] key = key[:-4] elif key == "tracks": return len(self.songs) elif key == "discs": return len({song("~#disc", 1) for song in self.songs}) elif key == "bitrate": length = self.__get_value("~#length") if not length: return 0 w = lambda s: s("~#bitrate", 0) * s("~#length", 0) return sum(w(song) for song in self.songs) / length else: # Standard or unknown numeric key. # AudioFile will try to cast the values to int, # default to avg func = NUM_DEFAULT_FUNCS.get(key, "avg") key = "~#" + key func = NUM_FUNCS.get(func) if func: # If none of the songs can return a numeric key, # the album returns default values = (song(key) for song in self.songs) values = [v for v in values if v != ""] return func(values) if values else None elif key in NUMERIC_ZERO_DEFAULT: return 0 return None elif key[:1] == "~": key = key[1:] numkey = key.split(":")[0] keys = {"people": {}, "peoplesort": {}} if key in keys: people = keys["people"] peoplesort = keys["peoplesort"] for song in self.songs: # Rank people by "relevance" -- artists before composers # before performers, then by number of appearances. for w, k in enumerate(ELPOEP): persons = song.list(k) for person in persons: people[person] = (people.get(person, 0) - PEOPLE_SCORE[w]) if k in TAG_TO_SORT: persons = song.list(TAG_TO_SORT[k]) or persons for person in persons: peoplesort[person] = (peoplesort.get(person, 0) - PEOPLE_SCORE[w]) # It's cheaper to get people and peoplesort in one go keys["people"] = sorted(people.keys(), key=people.__getitem__)[:100] keys["peoplesort"] = sorted(peoplesort.keys(), key=peoplesort.__getitem__)[:100] ret = keys.pop(key) ret = (ret and "\n".join(ret)) or None other, values = keys.popitem() other = "~" + other if not values: self.__default.add(other) else: if other in self.__used: self.__used.remove(other) self.__used.append(other) self.__cache[other] = "\n".join(values) return ret elif numkey == "length": length = self.__get_value("~#" + key) return None if length is None else util.format_time(length) elif numkey == "long-length": length = self.__get_value("~#" + key[5:]) return (None if length is None else util.format_time_long(length)) elif numkey == "tracks": tracks = self.__get_value("~#" + key) return (None if tracks is None else ngettext("%d track", "%d tracks", tracks) % tracks) elif numkey == "discs": discs = self.__get_value("~#" + key) if discs > 1: return ngettext("%d disc", "%d discs", discs) % discs else: # TODO: check this is correct for discs == 1 return None elif numkey == "rating": rating = self.__get_value("~#" + key) if rating is None: return None return util.format_rating(rating) elif numkey == "filesize": size = self.__get_value("~#" + key) return None if size is None else util.format_size(size) key = "~" + key # Nothing special was found, so just take all values of the songs # and sort them by their number of appearance result = {} for song in self.songs: for value in song.list(key): result[value] = result.get(value, 0) - 1 values = listmap(lambda x: x[0], sorted(result.items(), key=lambda x: (x[1], x[0]))) return "\n".join(values) if values else None
def __init__(self, albums, parent, process_mode): super(RGDialog, self).__init__( title=_('ReplayGain Analyzer'), parent=parent) self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) self.add_icon_button(_("_Save"), Icons.DOCUMENT_SAVE, Gtk.ResponseType.OK) self.process_mode = process_mode self.set_default_size(600, 400) self.set_border_width(6) hbox = Gtk.HBox(spacing=6) info = Gtk.Label() hbox.pack_start(info, True, True, 0) self.vbox.pack_start(hbox, False, False, 6) swin = Gtk.ScrolledWindow() swin.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) swin.set_shadow_type(Gtk.ShadowType.IN) self.vbox.pack_start(swin, True, True, 0) view = HintedTreeView() swin.add(view) def icon_cdf(column, cell, model, iter_, *args): item = model[iter_][0] if item.error: cell.set_property('icon-name', Icons.DIALOG_ERROR) else: cell.set_property('icon-name', Icons.NONE) column = Gtk.TreeViewColumn() column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) icon_render = Gtk.CellRendererPixbuf() column.pack_start(icon_render, True) column.set_cell_data_func(icon_render, icon_cdf) view.append_column(column) def track_cdf(column, cell, model, iter_, *args): item = model[iter_][0] cell.set_property('text', item.title) cell.set_sensitive(model[iter_][1]) column = Gtk.TreeViewColumn(_("Track")) column.set_expand(True) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) track_render = Gtk.CellRendererText() track_render.set_property('ellipsize', Pango.EllipsizeMode.END) column.pack_start(track_render, True) column.set_cell_data_func(track_render, track_cdf) view.append_column(column) def progress_cdf(column, cell, model, iter_, *args): item = model[iter_][0] cell.set_property('value', int(item.progress * 100)) cell.set_sensitive(model[iter_][1]) column = Gtk.TreeViewColumn(_("Progress")) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) progress_render = Gtk.CellRendererProgress() column.pack_start(progress_render, True) column.set_cell_data_func(progress_render, progress_cdf) view.append_column(column) def gain_cdf(column, cell, model, iter_, *args): item = model[iter_][0] if item.gain is None or not item.done: cell.set_property('text', "-") else: cell.set_property('text', "%.2f db" % item.gain) cell.set_sensitive(model[iter_][1]) column = Gtk.TreeViewColumn(_("Gain")) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) gain_renderer = Gtk.CellRendererText() column.pack_start(gain_renderer, True) column.set_cell_data_func(gain_renderer, gain_cdf) view.append_column(column) def peak_cdf(column, cell, model, iter_, *args): item = model[iter_][0] if item.gain is None or not item.done: cell.set_property('text', "-") else: cell.set_property('text', "%.2f" % item.peak) cell.set_sensitive(model[iter_][1]) column = Gtk.TreeViewColumn(_("Peak")) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) peak_renderer = Gtk.CellRendererText() column.pack_start(peak_renderer, True) column.set_cell_data_func(peak_renderer, peak_cdf) view.append_column(column) self.create_pipelines() self._timeout = None self._sigs = {} self._done = [] self.__fill_view(view, albums) num_to_process = sum(int(rga.should_process) for rga in self._todo) template = ngettext( "There is <b>%(to-process)s</b> album to update (of %(all)s)", "There are <b>%(to-process)s</b> albums to update (of %(all)s)", num_to_process) info.set_markup(template % { "to-process": format_int_locale(num_to_process), "all": format_int_locale(len(self._todo)), }) self.connect("destroy", self.__destroy) self.connect('response', self.__response)