def get_example_xml(song_path, rating, lastplayed): song_uri = URI.frompath(song_path) mount_uri = URI.frompath(find_mount_point(song_path)) return """\ <?xml version="1.0" standalone="yes"?> <rhythmdb version="1.9"> <entry type="song"> <title>Music</title> <genre>Unknown</genre> <track-number>7</track-number> <duration>199</duration> <file-size>4799124</file-size> <location>%s</location> <mountpoint>%s</mountpoint> <mtime>1378717158</mtime> <first-seen>1339576187</first-seen> <last-seen>1409855394</last-seen> <last-played>%d</last-played> <play-count>1</play-count> <bitrate>191</bitrate> <rating>%d</rating> <date>731881</date> <media-type>audio/mpeg</media-type> <composer>Unknown</composer> </entry> </rhythmdb>\ """ % (song_uri, mount_uri, lastplayed, rating)
def test_unc_paths(self): if os.name != "nt": return self.assertEqual( URI.frompath(u"\\\\server\\share\\path"), r"file:////server/share/path")
def _get_image_uri(self, song): """A unicode file URI or an empty string""" fileobj = app.cover_manager.get_cover(song) self._set_image_fileobj(fileobj) if fileobj: image_path = fileobj.name return URI.frompath(image_path).decode("utf-8") return u""
def browse_files_thunar(songs, display=""): bus = dbus.SessionBus() bus_object = bus.get_object(XFCE_NAME, XFCE_PATH) bus_iface = dbus.Interface(bus_object, dbus_interface=XFCE_IFACE) for song in songs: dirname = song("~dirname") basename = song("~basename") bus_iface.DisplayFolderAndSelect(URI.frompath(dirname), basename, display, get_startup_id())
def test_windows_path(self): if os.name != "nt": return win_path = u"C:\\SomeDir\xe4" uri = URI.frompath(win_path) self.assertEqual(uri, "file:///C:/SomeDir%C3%A4") self.assertTrue(uri.is_filename) self.assertTrue(is_fsnative(uri.filename)) self.assertEqual(uri.filename, win_path)
def test_parse_onesong_uri(self): h, name = mkstemp() os.close(h) target = os.path.join(DATA_DIR, "silence-44-s.ogg") from quodlibet.util.uri import URI target = URI.frompath(target) target = self.prefix + target with open(name, "w") as f: f.write(target) list = self.Parse(name) os.unlink(name) self.failUnlessEqual(len(list), 1) self.failUnlessEqual(list[0]("title"), "Silence") list.delete()
def test_parse_onesong_uri(self): name = makename() target = os.path.join(os.getcwd(), "tests/data/silence-44-s.ogg") from quodlibet.util.uri import URI target = URI.frompath(target) target = self.prefix + target f = file(name, "w") f.write(target) f.close() list = self.Parse(name) os.unlink(name) self.failUnlessEqual(len(list), 1) self.failUnlessEqual(list[0]("title"), "Silence") list.delete()
def get_property(self, interface, name): if interface == MediaObject.IFACE: if name == "Parent": return EntryObject.PATH elif name == "Type": return "image" elif name == "Path": return Icon.PATH elif name == "DisplayName": return "I'm an icon \o/" elif interface == MediaItem.IFACE: if name == "URLs": return [URI.frompath(self.__f.name)] elif name == "MIMEType": return "image/png" elif name == "Width" or name == "Height": return Icon.SIZE elif name == "ColorDepth": return self.__depth
def __drag_data_get(self, view, ctx, sel, tid, etime): model, rows = view.get_selection().get_selected_rows() dirs = [model[row][0] for row in rows] for songs in self.__find_songs(view.get_selection()): pass if tid == self.TARGET_QL: cant_add = filter(lambda s: not s.can_add, songs) if cant_add: qltk.ErrorMessage( qltk.get_top_parent(self), _("Unable to copy songs"), _("The files selected cannot be copied to other " "song lists or the queue.")).run() ctx.drag_abort(etime) return to_add = filter(self.__library.__contains__, songs) self.__add_songs(view, to_add) qltk.selection_set_songs(sel, songs) else: # External target (app) is delivered a list of URIS of songs uris = list(set([URI.frompath(dir) for dir in dirs])) print_d("Directories to drop: %s" % [u.filename for u in uris]) sel.set_uris(uris)
def __drag_data_get(self, view, ctx, sel, tid, etime): model, rows = view.get_selection().get_selected_rows() dirs = [model[row][0] for row in rows] for songs in self.__find_songs(view.get_selection()): pass if tid == self.TARGET_QL: cant_add = filter(lambda s: not s.can_add, songs) if cant_add: qltk.ErrorMessage( qltk.get_top_parent(self), _("Unable to copy songs"), _("The files selected cannot be copied to other " "song lists or the queue.")).run() ctx.drag_abort(etime) return to_add = filter(self.__library.__contains__, songs) self.__add_songs(view, to_add) qltk.selection_set_songs(sel, songs) else: # External target (app) is delivered a list of URIS of songs uris = list({URI.frompath(dir) for dir in dirs}) print_d("Directories to drop: %s" % [u.filename for u in uris]) sel.set_uris(uris)
def browse_folders_thunar(songs, display=""): # http://git.xfce.org/xfce/thunar/tree/thunar/thunar-dbus-service-infos.xml XFCE_PATH = "/org/xfce/FileManager" XFCE_NAME = "org.xfce.FileManager" XFCE_IFACE = "org.xfce.FileManager" if not dbus: raise BrowseError("no dbus") try: bus = dbus.SessionBus() bus_object = bus.get_object(XFCE_NAME, XFCE_PATH) bus_iface = dbus.Interface(bus_object, dbus_interface=XFCE_IFACE) # open each folder and select the first file we have selected for dirname, sub_songs in group_songs(songs).items(): bus_iface.DisplayFolderAndSelect( URI.frompath(dirname), sub_songs[0]("~basename"), display, get_startup_id()) except dbus.DBusException as e: raise BrowseError(e)
def __get_metadata(self): """http://xmms2.org/wiki/MPRIS_Metadata""" metadata = {} metadata["mpris:trackid"] = self.__get_current_track_id() song = app.player.info if not song: return metadata metadata["mpris:length"] = dbus.Int64(song("~#length") * 10**6) self.__cover = cover = app.cover_manager.get_cover(song) is_temp = False if cover: name = cover.name is_temp = name.startswith(tempfile.gettempdir()) # This doesn't work for embedded images.. the file gets unlinked # after loosing the file handle metadata["mpris:artUrl"] = str(URI.frompath(name)) if not is_temp: self.__cover = None # All list values list_val = { "artist": "artist", "albumArtist": "albumartist", "comment": "comment", "composer": "composer", "genre": "genre", "lyricist": "lyricist" } for xesam, tag in list_val.iteritems(): vals = song.list(tag) if vals: metadata["xesam:" + xesam] = map(unival, vals) # All single values sing_val = {"album": "album", "title": "title", "asText": "~lyrics"} for xesam, tag in sing_val.iteritems(): vals = song.comma(tag) if vals: metadata["xesam:" + xesam] = unival(vals) # URI metadata["xesam:url"] = song("~uri") # Integers num_val = { "audioBPM": "bpm", "discNumber": "disc", "trackNumber": "track", "useCount": "playcount" } for xesam, tag in num_val.iteritems(): val = song("~#" + tag, None) if val is not None: metadata["xesam:" + xesam] = int(val) # Rating metadata["xesam:userRating"] = float(song("~#rating")) # Dates ISO_8601_format = "%Y-%m-%dT%H:%M:%S" tuple_time = time.gmtime(song("~#lastplayed")) iso_time = time.strftime(ISO_8601_format, tuple_time) metadata["xesam:lastUsed"] = iso_time year = song("~year") if year: try: tuple_time = time.strptime(year, "%Y") iso_time = time.strftime(ISO_8601_format, tuple_time) except ValueError: pass else: metadata["xesam:contentCreated"] = iso_time return metadata
def show_notification(self, song): """Returns True if showing the notification was successful""" if not song: return True try: if self.__enabled: # we are enabled try to work with the data we have and # keep it fresh if not self.__interface: iface, caps, spec = self.__get_interface() self.__interface = iface self.__caps = caps self.__spec_version = spec if "actions" in caps: self.__action_sig = iface.connect_to_signal( "ActionInvoked", self.on_dbus_action) else: iface = self.__interface caps = self.__caps spec = self.__spec_version else: # not enabled, just get everything temporary, # propably preview iface, caps, spec = self.__get_interface() except dbus.DBusException: print_w("[notify] %s" % _("Couldn't connect to notification daemon.")) self.__disconnect() return False strip_markup = lambda t: re.subn("\</?[iub]\>", "", t)[0] strip_links = lambda t: re.subn("\</?a.*?\>", "", t)[0] strip_images = lambda t: re.subn("\<img.*?\>", "", t)[0] title = XMLFromPattern(get_conf_value("titlepattern")) % song title = unescape(strip_markup(strip_links(strip_images(title)))) body = "" if "body" in caps: body = XMLFromPattern(get_conf_value("bodypattern")) % song if "body-markup" not in caps: body = strip_markup(body) if "body-hyperlinks" not in caps: body = strip_links(body) if "body-images" not in caps: body = strip_images(body) image_path = "" if "icon-static" in caps: self.__image_fp = song.find_cover() if self.__image_fp: image_path = self.__image_fp.name is_temp = image_path.startswith(tempfile.gettempdir()) # If it is not an embeded cover, drop the file handle if not is_temp: self.__image_fp = None # spec recommends it, and it seems to work if image_path and spec >= (1, 1): image_path = URI.frompath(image_path) actions = [] if "actions" in caps: actions = ["next", _("Next")] hints = { "desktop-entry": "quodlibet", } try: self.__last_id = iface.Notify("Quod Libet", self.__last_id, image_path, title, body, actions, hints, get_conf_int("timeout")) except dbus.DBusException: print_w("[notify] %s" % _("Couldn't connect to notification daemon.")) self.__disconnect() return False # preview done, remove all references again if not self.__enabled: self.__disconnect() return True
def setUp(s): s.http_uri = URI("http://www.example.com/~piman;woo?bar=quux#whee") s.rfile_uri = URI("file://example.com/home/piman/crazy") s.file_uri = URI.frompath("/home/piman/cr!azy")
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 __get_metadata(self): """http://xmms2.org/wiki/MPRIS_Metadata""" metadata = {} metadata["mpris:trackid"] = self.__get_current_track_id() song = app.player.info if not song: return metadata metadata["mpris:length"] = dbus.Int64(song("~#length") * 10 ** 6) self.__cover = cover = app.cover_manager.get_cover(song) is_temp = False if cover: name = cover.name is_temp = name.startswith(tempfile.gettempdir()) # This doesn't work for embedded images.. the file gets unlinked # after loosing the file handle metadata["mpris:artUrl"] = str(URI.frompath(name)) if not is_temp: self.__cover = None # All list values list_val = { "artist": "artist", "albumArtist": "albumartist", "comment": "comment", "composer": "composer", "genre": "genre", "lyricist": "lyricist", } for xesam, tag in list_val.iteritems(): vals = song.list(tag) if vals: metadata["xesam:" + xesam] = map(unival, vals) # All single values sing_val = {"album": "album", "title": "title", "asText": "~lyrics"} for xesam, tag in sing_val.iteritems(): vals = song.comma(tag) if vals: metadata["xesam:" + xesam] = unival(vals) # URI metadata["xesam:url"] = song("~uri") # Integers num_val = {"audioBPM": "bpm", "discNumber": "disc", "trackNumber": "track", "useCount": "playcount"} for xesam, tag in num_val.iteritems(): val = song("~#" + tag, None) if val is not None: metadata["xesam:" + xesam] = int(val) # Rating metadata["xesam:userRating"] = float(song("~#rating")) # Dates ISO_8601_format = "%Y-%m-%dT%H:%M:%S" tuple_time = time.gmtime(song("~#lastplayed")) iso_time = time.strftime(ISO_8601_format, tuple_time) metadata["xesam:lastUsed"] = iso_time year = song("~year") if year: try: tuple_time = time.strptime(year, "%Y") iso_time = time.strftime(ISO_8601_format, tuple_time) except ValueError: pass else: metadata["xesam:contentCreated"] = iso_time return metadata
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 setUp(s): s.http_uri = URI("http://www.example.com/~piman;woo?bar=quux#whee") s.rfile_uri = URI("file://example.com/home/piman/crazy") s.file_uri = URI.frompath("/home/piman/cr!azy") s.extra_uri = URI("file:///////////home/piman")
def test_unc_paths(self): if os.name != "nt": return self.assertEqual(URI.frompath(u"\\\\server\\share\\path"), r"file:////server/share/path")
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(length) elif key == "#rating": return dict.get(self, "~" + key, const.DEFAULT_RATING) elif key == "rating": return util.format_rating(self("~#rating")) elif key == "people": join = "\n".join people = filter(None, map(self.__call__, PEOPLE)) if not people: return default people = join(people).split("\n") index = people.index return join([person for (i, person) in enumerate(people) if index(person) == i]) elif key == "peoplesort": join = "\n".join people = filter(None, map(self.__call__, PEOPLE_SORT)) people = join(people).split("\n") index = people.index return (join([person for (i, person) in enumerate(people) if index(person) == i]) or self("~people", default, connector)) elif key == "performers" or key == "performer": performers = {} for key in self.keys(): if key.startswith("performer:"): role = key.split(":", 1)[1] for value in self.list(key): try: performers[str(value)] except: performers[str(value)] = [] performers[str(value)].append(util.title(role)) values = [] if len(performers) > 0: for performer in performers: roles = '' i = 0 for role in performers[performer]: if i > 0: roles += ', ' roles += role i += 1 values.append("%s (%s)" % (performer, roles)) values.extend(self.list("performer")) if not values: return default return "\n".join(values) elif key == "performerssort" or key == "performersort": values = [] for key in self.keys(): if key.startswith("performersort:"): role = key.split(":", 1)[1] for value in self.list(key): values.append("%s (%s)" % (value, role)) values.extend(self.list("performersort")) return ("\n".join(values) or self("~performers", 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 == "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 == "playlists": # See Issue 876 # Avoid circular references from formats/__init__.py from quodlibet.util.collection import Playlist try: start = time.time() playlists = Playlist.playlists_featuring(self) import random if not random.randint(0, 1000): print_d("A sample song('~playlists') call: took %d μs " % (1E6 * (time.time() - start))) return "\n".join([s.name for s in playlists]) except KeyError: return 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: 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)