def _file(self, songs, box): length = 0 size = 0 for song in songs: length += song.get("~#length", 0) try: size += filesize(song["~filename"]) except EnvironmentError: pass table = Gtk.Table(n_rows=2, n_columns=2) table.set_col_spacings(6) table.attach(Label(_("Total length:")), 0, 1, 0, 1, xoptions=Gtk.AttachOptions.FILL) table.attach(Label(util.format_time_long(length)), 1, 2, 0, 1) table.attach(Label(_("Total size:")), 0, 1, 1, 2, xoptions=Gtk.AttachOptions.FILL) table.attach(Label(util.format_size(size)), 1, 2, 1, 2) box.pack_start(Frame(_("Files"), table), 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_long(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)
class Preferences(qltk.UniqueWindow, EditDisplayPatternMixin): _DEFAULT_PATTERN = DEFAULT_PATTERN_TEXT _PREVIEW_ITEM = FakeDisplayItem({ "date": "2010-10-31", "~length": util.format_time_display(6319), "~long-length": util.format_time_long(6319), "~tracks": numeric_phrase("%d track", "%d tracks", 5), "~discs": numeric_phrase("%d disc", "%d discs", 2), "~#rating": 0.75, "album": _("An Example Album"), "~people": _SOME_PEOPLE + "..."}) def __init__(self, browser): if self.is_not_unique(): return super(Preferences, self).__init__() self.set_border_width(12) self.set_title(_("Album List Preferences")) self.set_default_size(420, 380) self.set_transient_for(qltk.get_top_parent(browser)) # Do this config-driven setup at instance-time self._PREVIEW_ITEM["~rating"] = format_rating(0.75) box = Gtk.VBox(spacing=6) vbox = Gtk.VBox(spacing=6) cb = ConfigCheckButton( _("Show album _covers"), "browsers", "album_covers") cb.set_active(config.getboolean("browsers", "album_covers")) cb.connect('toggled', lambda s: browser.toggle_covers()) vbox.pack_start(cb, False, True, 0) cb = ConfigCheckButton( _("Inline _search includes people"), "browsers", "album_substrings") cb.set_active(config.getboolean("browsers", "album_substrings")) vbox.pack_start(cb, False, True, 0) f = qltk.Frame(_("Options"), child=vbox) box.pack_start(f, False, True, 12) display_frame = self.edit_display_pane(browser, _("Album Display")) box.pack_start(display_frame, True, True, 0) main_box = Gtk.VBox(spacing=12) close = Button(_("_Close"), Icons.WINDOW_CLOSE) close.connect('clicked', lambda *x: self.destroy()) b = Gtk.HButtonBox() b.set_layout(Gtk.ButtonBoxStyle.END) b.pack_start(close, True, True, 0) main_box.pack_start(box, True, True, 0) self.use_header_bar() if not self.has_close_button(): main_box.pack_start(b, False, True, 0) self.add(main_box) close.grab_focus() self.show_all()
def __set_time(self, info, songs): i = len(songs) length = sum(song.get("~#length", 0) for song in songs) t = self.browser.statusbar(i) % { 'count': i, 'time': util.format_time_long(length) } self.__statusbar.set_text(t)
class Preferences(qltk.UniqueWindow, EditDisplayPatternMixin): _A_SIZE = 127 * 1024 * 1024 _SOME_PEOPLE = "\n".join( tag(t) for t in ["artist", "performer", "composer", "arranger"]) _DEFAULT_PATTERN = DEFAULT_PATTERN_TEXT _PREVIEW_ITEM = FakeDisplayItem({ "date": "2015-11-31", "~length": format_time_display(6319), "~long-length": format_time_long(6319), "~tracks": numeric_phrase("%d track", "%d tracks", 27), "~#filesize": _A_SIZE, "~filesize": format_size(_A_SIZE), "~#rating": 0.75, "~name": _("Example Playlist"), "~people": _SOME_PEOPLE + "..." }) def __init__(self, browser): if self.is_not_unique(): return super(Preferences, self).__init__() self.set_border_width(12) self.set_title(_("Playlist Browser Preferences")) self.set_default_size(420, 240) self.set_transient_for(qltk.get_top_parent(browser)) box = Gtk.VBox(spacing=6) edit_frame = self.edit_display_pane(browser, _("Playlist display")) box.pack_start(edit_frame, False, True, 12) main_box = Gtk.VBox(spacing=12) close = Button(_("_Close"), Icons.WINDOW_CLOSE) close.connect('clicked', lambda *x: self.destroy()) b = Gtk.HButtonBox() b.set_layout(Gtk.ButtonBoxStyle.END) b.pack_start(close, True, True, 0) main_box.pack_start(box, True, True, 0) self.use_header_bar() if not self.has_close_button(): main_box.pack_start(b, False, True, 0) self.add(main_box) close.grab_focus() self.show_all()
def __set_time(self, *args, **kwargs): songs = kwargs.get("songs") or self.songlist.get_selected_songs() if "songs" not in kwargs and len(songs) <= 1: songs = self.songlist.get_songs() i = len(songs) length = sum([song("~#length", 0) for song in songs]) t = self.browser.statusbar(i) % { 'count': i, 'time': util.format_time_long(length)} self.statusbar.set_default_text(t)
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_long(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.ELLIPSIZE_END) w.set_markup("\n".join(text)) hb = gtk.HBox(spacing=12) cover = CoverImage(song=song) if cover: hb.pack_start(cover, expand=False) else: cover.destroy() hb.pack_start(w) box.pack_start(hb, expand=False, fill=False)
def _file(self, songs, box): length = 0 size = 0 for song in songs: length += song.get("~#length", 0) try: size += util.size(song["~filename"]) except EnvironmentError: pass table = gtk.Table(2, 2) table.set_col_spacings(6) table.attach(Label(_("Total length:")), 0, 1, 0, 1, xoptions=gtk.FILL) table.attach( Label(util.format_time_long(length)), 1, 2, 0, 1) table.attach(Label(_("Total size:")), 0, 1, 1, 2, xoptions=gtk.FILL) table.attach(Label(util.format_size(size)), 1, 2, 1, 2) box.pack_start(Frame(_("Files"), table), expand=False, fill=False)
def __preview_pattern(self, edit, label): people = "\n".join( [util.tag("artist"), util.tag("performer"), util.tag("arranger")]) album = FakeAlbum({"date": "2004-10-31", "~length": util.format_time(6319), "~long-length": util.format_time_long(6319), "~tracks": ngettext("%d track", "%d tracks", 5) % 5, "~discs": ngettext("%d disc", "%d discs", 2) % 2, "~people": people}) try: text = XMLFromPattern(edit.text) % album except: text = _("Invalid pattern") edit.apply.set_sensitive(False) try: pango.parse_markup(text, u"\u0000") except gobject.GError: text = _("Invalid pattern") edit.apply.set_sensitive(False) else: edit.apply.set_sensitive(True) label.set_markup(text)
def _file(self, song, box): def ftime(t): if t == 0: return _("Unknown") else: timestr = time.strftime("%c", time.localtime(t)) encoding = util.get_locale_encoding() return timestr.decode(encoding) fn = fsdecode(unexpand(song["~filename"])) length = util.format_time_long(song.get("~#length", 0)) size = util.format_size( song.get("~#filesize") or filesize(song["~filename"])) mtime = ftime(util.path.mtime(song["~filename"])) format_ = song("~format") codec = song("~codec") encoding = song.comma("~encoding") bitrate = song("~bitrate") t = Gtk.Table(n_rows=4, n_columns=2) t.set_col_spacings(6) t.set_homogeneous(False) table = [(_("length"), length), (_("format"), format_), (_("codec"), codec), (_("encoding"), encoding), (_("bitrate"), bitrate), (_("file size"), size), (_("modified"), mtime)] fnlab = Label(fn) fnlab.set_ellipsize(Pango.EllipsizeMode.MIDDLE) t.attach(fnlab, 0, 2, 0, 1, xoptions=Gtk.AttachOptions.FILL) for i, (l, r) in enumerate(table): l = "<b>%s</b>" % util.capitalize(util.escape(l) + ":") lab = Label() lab.set_markup(l) t.attach(lab, 0, 1, i + 1, i + 2, xoptions=Gtk.AttachOptions.FILL) t.attach(Label(r), 1, 2, i + 1, i + 2) box.pack_start(Frame(_("File"), t), False, False, 0)
def _file(self, song, box): def ftime(t): if t == 0: return _("Unknown") else: timestr = time.strftime("%c", time.localtime(t)) return timestr.decode(const.ENCODING) fn = fsdecode(unexpand(song["~filename"])) length = util.format_time_long(song.get("~#length", 0)) size = util.format_size( song.get("~#filesize") or filesize(song["~filename"])) mtime = ftime(util.path.mtime(song["~filename"])) bitrate = song.get("~#bitrate", 0) if bitrate != 0: bitrate = _("%d kbps") % int(bitrate) else: bitrate = False t = Gtk.Table(n_rows=4, n_columns=2) t.set_col_spacings(6) t.set_homogeneous(False) table = [(_("length"), length), (_("file size"), size), (_("modified"), mtime)] if bitrate: table.insert(1, (_("bitrate"), bitrate)) fnlab = Label(fn) fnlab.set_ellipsize(Pango.EllipsizeMode.MIDDLE) t.attach(fnlab, 0, 2, 0, 1, xoptions=Gtk.AttachOptions.FILL) for i, (l, r) in enumerate(table): l = "<b>%s</b>" % util.capitalize(util.escape(l) + ":") lab = Label() lab.set_markup(l) t.attach(lab, 0, 1, i + 1, i + 2, xoptions=Gtk.AttachOptions.FILL) t.attach(Label(r), 1, 2, i + 1, i + 2) box.pack_start(Frame(_("File"), t), False, False, 0)
class Preferences(qltk.UniqueWindow, EditDisplayPatternMixin): _DEFAULT_PATTERN = DEFAULT_PATTERN_TEXT _PREVIEW_ITEM = FakeDisplayItem({ "date": "2010-10-31", "~length": util.format_time_display(6319), "~long-length": util.format_time_long(6319), "~tracks": numeric_phrase("%d track", "%d tracks", 5), "~discs": numeric_phrase("%d disc", "%d discs", 2), "~#rating": 0.75, "album": _("An Example Album"), "~people": _SOME_PEOPLE + "..." }) def __init__(self, browser): if self.is_not_unique(): return super().__init__() self.set_border_width(12) self.set_title(_("Cover Grid Preferences")) self.set_default_size(420, 380) self.set_transient_for(qltk.get_top_parent(browser)) # Do this config-driven setup at instance-time self._PREVIEW_ITEM["~rating"] = format_rating(0.75) self.mag_lock = False box = Gtk.VBox(spacing=6) vbox = Gtk.VBox(spacing=6) cb = ConfigCheckButton(_("Show album _text"), "browsers", "album_text") cb.set_active(config.getboolean("browsers", "album_text")) cb.connect('toggled', lambda s: browser.toggle_text()) vbox.pack_start(cb, False, True, 0) cb2 = ConfigCheckButton(_("Show \"All Albums\" Item"), "browsers", "covergrid_all") cb2.set_active(config.getboolean("browsers", "covergrid_all", True)) def refilter(s): mod = browser.view.get_model() if mod: mod.refilter() cb2.connect('toggled', refilter) vbox.pack_start(cb2, False, True, 0) cb3 = ConfigCheckButton(_("Wide Mode"), "browsers", "covergrid_wide") cb3.set_active(config.getboolean("browsers", "covergrid_wide", False)) cb3.connect('toggled', lambda s: browser.toggle_wide()) vbox.pack_start(cb3, False, True, 0) # Redraws the covers only when the user releases the slider def mag_button_press(*_): self.mag_lock = True def mag_button_release(mag, _): self.mag_lock = False mag_changed(mag) def mag_changed(mag): if self.mag_lock: return newmag = mag.get_value() oldmag = config.getfloat("browsers", "covergrid_magnification", 3.) if newmag == oldmag: print_d("Covergrid magnification haven't changed: {0}".format( newmag)) return print_d('Covergrid magnification update from {0} to {1}'.format( oldmag, newmag)) config.set("browsers", "covergrid_magnification", mag.get_value()) browser.update_mag() mag_scale = Gtk.HScale(adjustment=Gtk.Adjustment.new( config.getfloat("browsers", "covergrid_magnification", 3), 0., 10., .5, .5, 0)) mag_scale.set_tooltip_text(_("Cover Magnification")) l = Gtk.Label(label=_("Cover Magnification")) mag_scale.set_value_pos(Gtk.PositionType.RIGHT) mag_scale.connect('button-press-event', mag_button_press) mag_scale.connect('button-release-event', mag_button_release) mag_scale.connect('value-changed', mag_changed) vbox.pack_start(l, False, True, 0) vbox.pack_start(mag_scale, False, True, 0) f = qltk.Frame(_("Options"), child=vbox) box.pack_start(f, False, True, 12) display_frame = self.edit_display_pane(browser, _("Album Display")) box.pack_start(display_frame, True, True, 0) main_box = Gtk.VBox(spacing=12) close = Button(_("_Close"), Icons.WINDOW_CLOSE) close.connect('clicked', lambda *x: self.destroy()) b = Gtk.HButtonBox() b.set_layout(Gtk.ButtonBoxStyle.END) b.pack_start(close, True, True, 0) main_box.pack_start(box, True, True, 0) self.use_header_bar() if not self.has_close_button(): main_box.pack_start(b, False, True, 0) self.add(main_box) close.grab_focus() self.show_all()
def __set_time(self, info, songs): i = len(songs) length = sum(song.get("~#length", 0) for song in songs) t = self.browser.status_text(count=i, time=util.format_time_long(length)) self.__statusbar.set_text(t)
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(set([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 INTERN_NUM_DEFAULT: return 0 return None elif key[:1] == "~": key = key[1:] 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 key == "length": length = self.__get_value("~#length") return None if length is None else util.format_time(length) elif key == "long-length": length = self.__get_value("~#length") return (None if length is None else util.format_time_long(length)) elif key == "tracks": tracks = self.__get_value("~#tracks") return (None if tracks is None else ngettext("%d track", "%d tracks", tracks) % tracks) elif key == "discs": discs = self.__get_value("~#discs") if discs > 1: return ngettext("%d disc", "%d discs", discs) % discs else: # TODO: check this is correct for discs == 1 return None elif key == "rating": rating = self.__get_value("~#rating") if rating is None: return None return util.format_rating(rating) elif key == "cover": return ((self.cover != type(self).cover) and "y") or None elif key == "filesize": size = self.__get_value("~#filesize") 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 = map(lambda x: x[0], sorted(result.items(), key=lambda x: x[1])) return "\n".join(values) if values else None
def __set_time(self, info, songs): i = len(songs) length = sum(song.get("~#length", 0) for song in songs) t = self.browser.statusbar(i) % { 'count': i, 'time': util.format_time_long(length)} self.statusbar.set_default_text(t)
class Preferences(qltk.UniqueWindow): _EXAMPLE_ALBUM = FakeAlbum({ "date": "2010-10-31", "~length": util.format_time_display(6319), "~long-length": util.format_time_long(6319), "~tracks": ngettext("%d track", "%d tracks", 5) % 5, "~discs": ngettext("%d disc", "%d discs", 2) % 2, "~#rating": 0.75, "album": _("An Example Album"), "~people": _SOME_PEOPLE + "..." }) def __init__(self, browser): if self.is_not_unique(): return super(Preferences, self).__init__() self.set_border_width(12) self.set_title(_("Album List Preferences") + " - Quod Libet") self.set_default_size(420, 380) self.set_transient_for(qltk.get_top_parent(browser)) # Do this config-driven setup at instance-time self._EXAMPLE_ALBUM["~rating"] = format_rating(0.75) box = Gtk.VBox(spacing=6) vbox = Gtk.VBox(spacing=6) cb = ConfigCheckButton(_("Show album _covers"), "browsers", "album_covers") cb.set_active(config.getboolean("browsers", "album_covers")) cb.connect('toggled', lambda s: browser.toggle_covers()) vbox.pack_start(cb, False, True, 0) cb = ConfigCheckButton(_("Inline _search includes people"), "browsers", "album_substrings") cb.set_active(config.getboolean("browsers", "album_substrings")) vbox.pack_start(cb, False, True, 0) f = qltk.Frame(_("Options"), child=vbox) box.pack_start(f, False, True, 12) vbox = Gtk.VBox(spacing=6) label = Gtk.Label() label.set_alignment(0.0, 0.5) label.set_padding(6, 6) eb = Gtk.EventBox() eb.get_style_context().add_class("entry") eb.add(label) edit = PatternEditBox(PATTERN) edit.text = browser._pattern_text edit.apply.connect('clicked', self.__set_pattern, edit, browser) connect_obj(edit.buffer, 'changed', self.__preview_pattern, edit, label) vbox.pack_start(eb, False, True, 3) vbox.pack_start(edit, True, True, 0) self.__preview_pattern(edit, label) f = qltk.Frame(_("Album Display"), child=vbox) box.pack_start(f, True, True, 0) main_box = Gtk.VBox(spacing=12) close = Gtk.Button(stock=Gtk.STOCK_CLOSE) close.connect('clicked', lambda *x: self.destroy()) b = Gtk.HButtonBox() b.set_layout(Gtk.ButtonBoxStyle.END) b.pack_start(close, True, True, 0) main_box.pack_start(box, True, True, 0) self.use_header_bar() if not self.has_close_button(): main_box.pack_start(b, False, True, 0) self.add(main_box) close.grab_focus() self.show_all() def __set_pattern(self, apply, edit, browser): browser.refresh_pattern(edit.text) def __preview_pattern(self, edit, label): try: text = XMLFromMarkupPattern(edit.text) % self._EXAMPLE_ALBUM except: text = _("Invalid pattern") edit.apply.set_sensitive(False) try: Pango.parse_markup(text, -1, u"\u0000") except GLib.GError: text = _("Invalid pattern") edit.apply.set_sensitive(False) else: edit.apply.set_sensitive(True) label.set_markup(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(set([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 elif key in NUM_DEFAULT_FUNCS: func = NUM_DEFAULT_FUNCS[key] else: #Unknown key. AudioFile will try to cast the values to int, #default to avg func = "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 != ""] if values: return func(values) else: return None elif key in INTERN_NUM_DEFAULT: return 0 return None elif key[:1] == "~": key = key[1:] 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 key == "length": length = self.__get_value("~#length") if length is None: return None return util.format_time(length) elif key == "long-length": length = self.__get_value("~#length") if length is None: return None return util.format_time_long(length) elif key == "tracks": tracks = self.__get_value("~#tracks") if tracks is None: return None return ngettext("%d track", "%d tracks", tracks) % tracks elif key == "discs": discs = self.__get_value("~#discs") if discs > 1: return ngettext("%d disc", "%d discs", discs) % discs else: return None elif key == "rating": rating = self.__get_value("~#rating") if rating is None: return None return util.format_rating(rating) elif key == "cover": return ((self.cover != type(self).cover) and "y") or None elif key == "filesize": size = self.__get_value("~#filesize") if size is None: return None return 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 = map(lambda x: x[0], sorted(result.items(), key=lambda x: x[1])) if not values: return None return "\n".join(values)