def __refresh_space(self, device): try: space, free = device.get_space() except NotImplementedError: self.__device_space.set_text("") self.__progress.hide() else: used = space - free fraction = float(used) / space self.__device_space.set_markup( _("<b>%s</b> used, <b>%s</b> available") % (util.format_size(used), util.format_size(free))) self.__progress.set_fraction(fraction) self.__progress.set_text("%.f%%" % round(fraction * 100)) self.__progress.show()
def update(img, content_type, size, props, item, frame): format = IMAGE_EXTENSIONS.get(content_type, content_type).upper() text = "{source} - {w}x{h}, {format}, <b>{size}</b>".format( source=escape(item.source), format=format, w=props.width, h=props.height, size=format_size(size)) frame.get_label_widget().set_markup(text) frame.get_child().set_reveal_child(True)
def _cdf(self, column, cell, model, iter_, user_data): value = model.get_value(iter_).get("~#filesize", 0) if not self._needs_update(value): return text = util.format_size(value) cell.set_property('text', text) self._recalc_width(model.get_path(iter_), text)
def get_size_of_url(url): request = urllib2.Request(url) request.add_header('Accept-Encoding', 'gzip') request.add_header('User-Agent', USER_AGENT) url_sock = urllib2.urlopen(request) size = url_sock.headers.get('content-length') url_sock.close() return format_size(int(size)) if size else ''
def __refresh_space(self, device): try: space, free = device.get_space() except NotImplementedError: self.__device_space.set_text("") self.__progress.hide() else: used = space - free fraction = float(used) / space self.__device_space.set_markup( _("%(used-size)s used, %(free-size)s available") % {"used-size": util.bold(util.format_size(used)), "free-size": util.bold(util.format_size(free))}) self.__progress.set_fraction(fraction) self.__progress.set_text("%.f%%" % round(fraction * 100)) self.__progress.show()
def get_size_of_url(url): request = urllib2.Request(url) request.add_header("Accept-Encoding", "gzip") request.add_header("User-Agent", USER_AGENT) url_sock = urllib2.urlopen(request) size = url_sock.headers.get("content-length") url_sock.close() return format_size(int(size)) if size else ""
def format(self): """Return a markup representation of information for this playlist""" total_size = float(self.get("~#filesize") or 0.0) songs_text = (ngettext("%d song", "%d songs", len(self.songs)) % len(self.songs)) # see Issue 504 return "<b>%s</b>\n<small>%s (%s%s)</small>" % ( util.escape(self.name), songs_text, self.get("~length", "0:00"), " / %s" % util.format_size(total_size) if total_size > 0 else "")
def format(self): """A markup representation of information for this playlist""" total_size = float(self.get("~#filesize") or 0.0) songs_text = (ngettext("%d song", "%d songs", len(self.songs)) % len(self.songs)) # see Issue 504 return "<b>%s</b>\n<small>%s (%s%s)</small>" % ( util.escape(self.name), songs_text, self.get("~length", "0:00"), " / %s" % util.format_size(total_size) if total_size>0 else "")
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 _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 = Table(2) table.attach(Label(_("Total length:")), 0, 1, 0, 1, xoptions=Gtk.AttachOptions.FILL) table.attach( Label(util.format_time_preferred(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 _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 = fsn2text(unexpand(song["~filename"])) length = util.format_time_preferred(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)) 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)) if not PY3: timestr = timestr.decode(util.get_locale_encoding()) return timestr fn = fsn2text(unexpand(song["~filename"])) length = util.format_time_preferred(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") table = [(_("path"), fn), (_("length"), length), (_("format"), format_), (_("codec"), codec), (_("encoding"), encoding), (_("bitrate"), bitrate), (_("file size"), size), (_("modified"), mtime)] t = Table(len(table)) for i, (tag_, text) in enumerate(table): tag_ = util.capitalize(util.escape(tag_) + ":") lab = Label(text) lab.set_ellipsize(Pango.EllipsizeMode.MIDDLE) t.attach(Label(tag_), 0, 1, i, i + 1, xoptions=Gtk.AttachOptions.FILL) t.attach(lab, 1, 2, i, i + 1) 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)
def _get_min_width(self): # e.g "2.22 MB" return self._cell_width(util.format_size(2.22 * (1024 ** 2)))
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)
def cell_data_size(column, cell, model, iter, data): if model[iter][2] == 0: size = _("Queued") else: size = util.format_size(model[iter][1].tell()) cell.set_property('text', size)
def __call__(self, key, default=u"", connector=" - ", joiner=', '): """Return the value(s) for a key, synthesizing if necessary. Multiple values for a key are delimited by newlines. A default value may be given (like `dict.get`); the default default is an empty unicode string (even if the tag is numeric). If a tied tag ('a~b') is requested, the `connector` keyword argument may be used to specify what it is tied with. In case the tied tag contains numeric and file path tags, the result will still be a unicode string. The `joiner` keyword specifies how multiple *values* will be joined within that tied tag output, e.g. ~people~title = "Kanye West, Jay Z - New Day" For details on tied tags, see the documentation for `util.tagsplit`. """ if key[:1] == "~": key = key[1:] if "~" in key: real_key = "~" + key values = [] sub_tags = util.tagsplit(real_key) # If it's genuinely a tied tag (not ~~people etc), we want # to delimit the multi-values separately from the tying j = joiner if len(sub_tags) > 1 else "\n" for t in sub_tags: vs = [decode_value(real_key, v) for v in (self.list(t))] v = j.join(vs) if v: values.append(v) return connector.join(values) or default elif key == "#track": try: return int(self["tracknumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "#disc": try: return int(self["discnumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "length": length = self.get("~#length") if length is None: return default else: return util.format_time_display(length) elif key == "#rating": return dict.get(self, "~" + key, config.RATINGS.default) elif key == "rating": return util.format_rating(self("~#rating")) elif key == "people": return "\n".join(self.list_unique(PEOPLE)) or default elif key == "people:real": # Issue 1034: Allow removal of V.A. if others exist. unique = self.list_unique(PEOPLE) # Order is important, for (unlikely case): multiple removals for val in VARIOUS_ARTISTS_VALUES: if len(unique) > 1 and val in unique: unique.remove(val) return "\n".join(unique) or default elif key == "people:roles": return (self._role_call("performer", PEOPLE) or default) elif key == "peoplesort": return ("\n".join(self.list_unique(PEOPLE_SORT)) or self("~people", default, connector)) elif key == "peoplesort:roles": # Ignores non-sort tags if there are any sort tags (e.g. just # returns "B" for {artist=A, performersort=B}). # TODO: figure out the "correct" behavior for mixed sort tags return (self._role_call("performersort", PEOPLE_SORT) or self("~peoplesort", default, connector)) elif key in ("performers", "performer"): return self._prefixvalue("performer") or default elif key in ("performerssort", "performersort"): return (self._prefixvalue("performersort") or self("~" + key[-4:], default, connector)) elif key in ("performers:roles", "performer:roles"): return (self._role_call("performer") or default) elif key in ("performerssort:roles", "performersort:roles"): return (self._role_call("performersort") or self("~" + key.replace("sort", ""), default, connector)) elif key == "basename": return os.path.basename(self["~filename"]) or self["~filename"] elif key == "dirname": return os.path.dirname(self["~filename"]) or self["~filename"] elif key == "uri": try: return self["~uri"] except KeyError: return fsn2uri(self["~filename"]) elif key == "format": return self.get("~format", text_type(self.format)) elif key == "codec": codec = self.get("~codec") if codec is None: return self("~format") return codec elif key == "encoding": parts = filter(None, [self.get("~encoding"), self.get("encodedby")]) encoding = u"\n".join(parts) return encoding or default elif key == "language": codes = self.list("language") if not codes: return default return u"\n".join(iso639.translate(c) or c for c in codes) elif key == "bitrate": return util.format_bitrate(self("~#bitrate")) elif key == "#date": date = self.get("date") if date is None: return default return util.date_key(date) elif key == "year": return self.get("date", default)[:4] elif key == "#year": try: return int(self.get("date", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "originalyear": return self.get("originaldate", default)[:4] elif key == "#originalyear": try: return int(self.get("originaldate", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "#tracks": try: return int(self["tracknumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "#discs": try: return int(self["discnumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "lyrics": # First, try the embedded lyrics. try: return self[key] except KeyError: pass # If there are no embedded lyrics, try to read them from # the external file. try: fileobj = open(self.lyric_filename, "rU") except EnvironmentError: return default else: return fileobj.read().decode("utf-8", "replace") elif key == "filesize": return util.format_size(self("~#filesize", 0)) elif key == "playlists": # See Issue 876 # Avoid circular references from formats/__init__.py from quodlibet.util.collection import Playlist playlists = Playlist.playlists_featuring(self) return "\n".join([s.name for s in playlists]) or default elif key.startswith("#replaygain_"): try: val = self.get(key[1:], default) return round(float(val.split(" ")[0]), 2) except (ValueError, TypeError, AttributeError): return default elif key[:1] == "#": key = "~" + key if key in self: return self[key] elif key in NUMERIC_ZERO_DEFAULT: return 0 else: try: val = self[key[2:]] except KeyError: return default try: return int(val) except ValueError: try: return float(val) except ValueError: return default else: return dict.get(self, "~" + key, default) elif key == "title": title = dict.get(self, "title") if title is None: basename = self("~basename") return "%s [%s]" % ( decode_value("~basename", basename), _("Unknown")) else: return title elif key in SORT_TO_TAG: try: return self[key] except KeyError: key = SORT_TO_TAG[key] return dict.get(self, key, default)
def __call__(self, key, default: Any = u"", connector=" - ", joiner=', '): """Return the value(s) for a key, synthesizing if necessary. Multiple values for a key are delimited by newlines. A default value may be given (like `dict.get`); the default default is an empty unicode string (even if the tag is numeric). If a tied tag ('a~b') is requested, the `connector` keyword argument may be used to specify what it is tied with. In case the tied tag contains numeric and file path tags, the result will still be a unicode string. The `joiner` keyword specifies how multiple *values* will be joined within that tied tag output, e.g. ~people~title = "Kanye West, Jay Z - New Day" For details on tied tags, see the documentation for `util.tagsplit`. """ if key[:1] == "~": key = key[1:] if "~" in key: real_key = "~" + key values = [] sub_tags = util.tagsplit(real_key) # If it's genuinely a tied tag (not ~~people etc), we want # to delimit the multi-values separately from the tying j = joiner if len(sub_tags) > 1 else "\n" for t in sub_tags: vs = [decode_value(real_key, v) for v in (self.list(t))] v = j.join(vs) if v: values.append(v) return connector.join(values) or default elif key == "#track": try: return int(self["tracknumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "#disc": try: return int(self["discnumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "length": length = self.get("~#length") if length is None: return default else: return util.format_time_display(length) elif key == "#rating": return dict.get(self, "~" + key, config.RATINGS.default) elif key == "rating": return util.format_rating(self("~#rating")) elif key == "people": return "\n".join(self.list_unique(PEOPLE)) or default elif key == "people:real": # Issue 1034: Allow removal of V.A. if others exist. unique = self.list_unique(PEOPLE) # Order is important, for (unlikely case): multiple removals for val in VARIOUS_ARTISTS_VALUES: if len(unique) > 1 and val in unique: unique.remove(val) return "\n".join(unique) or default elif key == "people:roles": return (self._role_call("performer", PEOPLE) or default) elif key == "peoplesort": return ("\n".join(self.list_unique(PEOPLE_SORT)) or self("~people", default, connector)) elif key == "peoplesort:roles": # Ignores non-sort tags if there are any sort tags (e.g. just # returns "B" for {artist=A, performersort=B}). # TODO: figure out the "correct" behavior for mixed sort tags return (self._role_call("performersort", PEOPLE_SORT) or self("~peoplesort", default, connector)) elif key in ("performers", "performer"): return self._prefixvalue("performer") or default elif key in ("performerssort", "performersort"): return (self._prefixvalue("performersort") or self("~" + key[-4:], default, connector)) elif key in ("performers:roles", "performer:roles"): return (self._role_call("performer") or default) elif key in ("performerssort:roles", "performersort:roles"): return (self._role_call("performersort") or self( "~" + key.replace("sort", ""), default, connector)) elif key == "basename": return os.path.basename(self["~filename"]) or self["~filename"] elif key == "dirname": return os.path.dirname(self["~filename"]) or self["~filename"] elif key == "uri": try: return self["~uri"] except KeyError: return fsn2uri(self["~filename"]) elif key == "format": return self.get("~format", str(self.format)) elif key == "codec": codec = self.get("~codec") if codec is None: return self("~format") return codec elif key == "encoding": encoding = "\n".join( part for part in [self.get("~encoding"), self.get("encodedby")] if part) return encoding or default elif key == "language": codes = self.list("language") if not codes: return default return u"\n".join(iso639.translate(c) or c for c in codes) elif key == "bitrate": return util.format_bitrate(self("~#bitrate")) elif key == "#date": date = self.get("date") if date is None: return default return util.date_key(date) elif key == "year": return self.get("date", default)[:4] elif key == "#year": try: return int(self.get("date", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "originalyear": return self.get("originaldate", default)[:4] elif key == "#originalyear": try: return int(self.get("originaldate", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "#tracks": try: return int(self["tracknumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "#discs": try: return int(self["discnumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "lyrics": # First, try the embedded lyrics. try: return self["lyrics"] except KeyError: pass try: return self["unsyncedlyrics"] except KeyError: pass # If there are no embedded lyrics, try to read them from # the external file. lyric_filename = self.lyric_filename if not lyric_filename: return default try: with open(lyric_filename, "rb") as fileobj: print_d(f"Reading lyrics from {lyric_filename!r}") text = fileobj.read().decode("utf-8", "replace") # try to skip binary files if "\0" in text: return default return text except (EnvironmentError, UnicodeDecodeError): return default elif key == "filesize": return util.format_size(self("~#filesize", 0)) elif key == "playlists": # TODO: avoid static dependency here... somehow from quodlibet import app lib = app.library if not lib: return "" playlists = lib.playlists.playlists_featuring(self) return "\n".join(s.name for s in playlists) or default elif key.startswith("#replaygain_"): try: val = self.get(key[1:], default) return round(float(val.split(" ")[0]), 2) except (ValueError, TypeError, AttributeError): return default elif key[:1] == "#": key = "~" + key if key in self: return self[key] elif key in NUMERIC_ZERO_DEFAULT: return 0 else: try: val = self[key[2:]] except KeyError: return default try: return int(val) except ValueError: try: return float(val) except ValueError: return default else: return dict.get(self, "~" + key, default) elif key == "title": title = dict.get(self, "title") if title is None: # build a title with missing_title_template option unknown_track_template = _( config.gettext("browsers", "missing_title_template")) from quodlibet.pattern import Pattern try: pattern = Pattern(unknown_track_template) except ValueError: title = decode_value("~basename", self("~basename")) else: title = pattern % self return title elif key in SORT_TO_TAG: try: return self[key] except KeyError: key = SORT_TO_TAG[key] return dict.get(self, key, default)
def t_dict(self, d): for key, value in d.items(): self.failUnlessEqual(util.format_size(key), value)
def _apply_value(self, model, iter_, cell, value): text = util.format_size(value) cell.set_property('text', text) self._recalc_width(model.get_path(iter_), text)
def __call__(self, key, default=u"", connector=" - "): """Return a key, synthesizing it if necessary. A default value may be given (like dict.get); the default default is an empty unicode string (even if the tag is numeric). If a tied tag ('a~b') is requested, the 'connector' keyword argument may be used to specify what it is tied with. For details on tied tags, see the documentation for util.tagsplit.""" if key[:1] == "~": key = key[1:] if "~" in key: # FIXME: decode ~filename etc. if not isinstance(default, basestring): return default return connector.join( filter( None, map( lambda x: isinstance(x, basestring) and x or str(x ), map( lambda x: (isinstance(x, float) and "%.2f" % x) or x, map(self.__call__, util.tagsplit("~" + key)))))) or default elif key == "#track": try: return int(self["tracknumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "#disc": try: return int(self["discnumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "length": length = self.get("~#length") if length is None: return default else: return util.format_time_display(length) elif key == "#rating": return dict.get(self, "~" + key, config.RATINGS.default) elif key == "rating": return util.format_rating(self("~#rating")) elif key == "people": return "\n".join(self.list_unique(PEOPLE)) or default elif key == "people:real": # Issue 1034: Allow removal of V.A. if others exist. unique = self.list_unique(PEOPLE) # Order is important, for (unlikely case): multiple removals for val in VARIOUS_ARTISTS_VALUES: if len(unique) > 1 and val in unique: unique.remove(val) return "\n".join(unique) or default elif key == "people:roles": return (self._role_call("performer", PEOPLE) or default) elif key == "peoplesort": return ("\n".join(self.list_unique(PEOPLE_SORT)) or self("~people", default, connector)) elif key == "peoplesort:roles": # Ignores non-sort tags if there are any sort tags (e.g. just # returns "B" for {artist=A, performersort=B}). # TODO: figure out the "correct" behavior for mixed sort tags return (self._role_call("performersort", PEOPLE_SORT) or self("~peoplesort", default, connector)) elif key in ("performers", "performer"): return self._prefixvalue("performer") or default elif key in ("performerssort", "performersort"): return (self._prefixvalue("performersort") or self("~" + key[-4:], default, connector)) elif key in ("performers:roles", "performer:roles"): return (self._role_call("performer") or default) elif key in ("performerssort:roles", "performersort:roles"): return (self._role_call("performersort") or self( "~" + key.replace("sort", ""), default, connector)) elif key == "basename": return os.path.basename(self["~filename"]) or self["~filename"] elif key == "dirname": return os.path.dirname(self["~filename"]) or self["~filename"] elif key == "uri": try: return self["~uri"] except KeyError: return URI.frompath(self["~filename"]) elif key == "format": return self.get("~format", self.format) elif key == "#date": date = self.get("date") if date is None: return default return util.date_key(date) elif key == "year": return self.get("date", default)[:4] elif key == "#year": try: return int(self.get("date", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "originalyear": return self.get("originaldate", default)[:4] elif key == "#originalyear": try: return int(self.get("originaldate", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "#tracks": try: return int(self["tracknumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "#discs": try: return int(self["discnumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "lyrics": try: fileobj = file(self.lyric_filename, "rU") except EnvironmentError: return default else: return fileobj.read().decode("utf-8", "replace") elif key == "filesize": return util.format_size(self("~#filesize", 0)) elif key == "playlists": # See Issue 876 # Avoid circular references from formats/__init__.py from quodlibet.util.collection import Playlist playlists = Playlist.playlists_featuring(self) return "\n".join([s.name for s in playlists]) or default elif key.startswith("#replaygain_"): try: val = self.get(key[1:], default) return round(float(val.split(" ")[0]), 2) except (ValueError, TypeError, AttributeError): return default elif key[:1] == "#": key = "~" + key if key in self: return self[key] elif key in INTERN_NUM_DEFAULT: return dict.get(self, key, 0) else: try: val = self[key[2:]] except KeyError: return default try: return int(val) except ValueError: try: return float(val) except ValueError: return default else: return dict.get(self, "~" + key, default) elif key == "title": title = dict.get(self, "title") if title is None: basename = self("~basename") basename = basename.decode(const.FSCODING, "replace") return "%s [%s]" % (basename, _("Unknown")) else: return title elif key in SORT_TO_TAG: try: return self[key] except KeyError: key = SORT_TO_TAG[key] return dict.get(self, key, default)
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 _cdf(self, column, cell, model, iter, tag): value = model[iter][0].get("~#filesize", 0) if not self._needs_update(value): return text = util.format_size(value) cell.set_property('text', text) self._update_layout(text, cell)
def __call__(self, key, default=u"", connector=" - "): """Return a key, synthesizing it if necessary. A default value may be given (like dict.get); the default default is an empty unicode string (even if the tag is numeric). If a tied tag ('a~b') is requested, the 'connector' keyword argument may be used to specify what it is tied with. In case the tied tag contains numeric and file path tags, the result will still be a unicode string. For details on tied tags, see the documentation for util.tagsplit. """ if key[:1] == "~": key = key[1:] if "~" in key: real_key = "~" + key values = [] for v in map(self.__call__, util.tagsplit(real_key)): v = decode_value(real_key, v) if v: values.append(v) return connector.join(values) or default elif key == "#track": try: return int(self["tracknumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "#disc": try: return int(self["discnumber"].split("/")[0]) except (ValueError, TypeError, KeyError): return default elif key == "length": length = self.get("~#length") if length is None: return default else: return util.format_time_display(length) elif key == "#rating": return dict.get(self, "~" + key, config.RATINGS.default) elif key == "rating": return util.format_rating(self("~#rating")) elif key == "people": return "\n".join(self.list_unique(PEOPLE)) or default elif key == "people:real": # Issue 1034: Allow removal of V.A. if others exist. unique = self.list_unique(PEOPLE) # Order is important, for (unlikely case): multiple removals for val in VARIOUS_ARTISTS_VALUES: if len(unique) > 1 and val in unique: unique.remove(val) return "\n".join(unique) or default elif key == "people:roles": return (self._role_call("performer", PEOPLE) or default) elif key == "peoplesort": return ("\n".join(self.list_unique(PEOPLE_SORT)) or self("~people", default, connector)) elif key == "peoplesort:roles": # Ignores non-sort tags if there are any sort tags (e.g. just # returns "B" for {artist=A, performersort=B}). # TODO: figure out the "correct" behavior for mixed sort tags return (self._role_call("performersort", PEOPLE_SORT) or self("~peoplesort", default, connector)) elif key in ("performers", "performer"): return self._prefixvalue("performer") or default elif key in ("performerssort", "performersort"): return (self._prefixvalue("performersort") or self("~" + key[-4:], default, connector)) elif key in ("performers:roles", "performer:roles"): return (self._role_call("performer") or default) elif key in ("performerssort:roles", "performersort:roles"): return (self._role_call("performersort") or self("~" + key.replace("sort", ""), default, connector)) elif key == "basename": return os.path.basename(self["~filename"]) or self["~filename"] elif key == "dirname": return os.path.dirname(self["~filename"]) or self["~filename"] elif key == "uri": try: return self["~uri"] except KeyError: return URI.frompath(self["~filename"]) elif key == "format": return self.get("~format", self.format) elif key == "#date": date = self.get("date") if date is None: return default return util.date_key(date) elif key == "year": return self.get("date", default)[:4] elif key == "#year": try: return int(self.get("date", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "originalyear": return self.get("originaldate", default)[:4] elif key == "#originalyear": try: return int(self.get("originaldate", default)[:4]) except (ValueError, TypeError, KeyError): return default elif key == "#tracks": try: return int(self["tracknumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "#discs": try: return int(self["discnumber"].split("/")[1]) except (ValueError, IndexError, TypeError, KeyError): return default elif key == "lyrics": try: fileobj = file(self.lyric_filename, "rU") except EnvironmentError: return default else: return fileobj.read().decode("utf-8", "replace") elif key == "filesize": return util.format_size(self("~#filesize", 0)) elif key == "playlists": # See Issue 876 # Avoid circular references from formats/__init__.py from quodlibet.util.collection import Playlist playlists = Playlist.playlists_featuring(self) return "\n".join([s.name for s in playlists]) or default elif key.startswith("#replaygain_"): try: val = self.get(key[1:], default) return round(float(val.split(" ")[0]), 2) except (ValueError, TypeError, AttributeError): return default elif key[:1] == "#": key = "~" + key if key in self: return self[key] elif key in INTERN_NUM_DEFAULT: return dict.get(self, key, 0) else: try: val = self[key[2:]] except KeyError: return default try: return int(val) except ValueError: try: return float(val) except ValueError: return default else: return dict.get(self, "~" + key, default) elif key == "title": title = dict.get(self, "title") if title is None: basename = self("~basename") return "%s [%s]" % ( decode_value("~basename", basename), _("Unknown")) else: return title elif key in SORT_TO_TAG: try: return self[key] except KeyError: key = SORT_TO_TAG[key] return dict.get(self, key, default)
def t_dict(self, d): for key, value in d.items(): formatted = util.format_size(key) self.failUnlessEqual(formatted, value) assert isinstance(formatted, text_type)