Example #1
0
def get_example_xml(song_path, rating, lastplayed):

    song_uri = fsn2uri(song_path)
    mount_uri = fsn2uri(find_mount_point(song_path))

    return (u"""\
<?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)).encode("utf-8")
Example #2
0
def get_example_xml(song_path, rating, lastplayed):

    song_uri = fsn2uri(song_path)
    mount_uri = fsn2uri(find_mount_point(song_path))

    return (u"""\
<?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)).encode("utf-8")
Example #3
0
 def test_fsn2uri(self):
     if os.name != "nt":
         uri = fsn2uri(fsnative(u"/öäü.txt"))
         self.assertEqual(uri, u"file:///%C3%B6%C3%A4%C3%BC.txt")
     else:
         uri = fsn2uri(fsnative(u"C:\\öäü.txt"))
         self.assertEqual(uri, "file:///C:/%C3%B6%C3%A4%C3%BC.txt")
         self.assertEqual(fsn2uri(u"C:\\SomeDir\xe4"),
                          "file:///C:/SomeDir%C3%A4")
Example #4
0
 def test_fsn2uri(self):
     if os.name != "nt":
         uri = fsn2uri(fsnative(u"/öäü.txt"))
         self.assertEqual(uri, u"file:///%C3%B6%C3%A4%C3%BC.txt")
     else:
         uri = fsn2uri(fsnative(u"C:\\öäü.txt"))
         self.assertEqual(
             uri, "file:///C:/%C3%B6%C3%A4%C3%BC.txt")
         self.assertEqual(
             fsn2uri(u"C:\\SomeDir\xe4"), "file:///C:/SomeDir%C3%A4")
Example #5
0
def test_uri_roundtrip():
    if os.name == "nt":
        for path in [
                u"C:\\foo-\u1234", u"C:\\bla\\quux ha",
                u"\\\\\u1234\\foo\\\u1423", u"\\\\foo;\\f"
        ]:
            path = fsnative(path)
            assert uri2fsn(fsn2uri(path)) == path
            assert isinstance(uri2fsn(fsn2uri(path)), fsnative)
    else:
        path = path2fsn(b"/foo-\xe1\x88\xb4")

        assert uri2fsn(fsn2uri(fsnative(u"/foo"))) == "/foo"
        assert uri2fsn(fsn2uri(path)) == path
        assert isinstance(uri2fsn(fsn2uri(path)), fsnative)
    def test_thumb(s):
        thumb = thumbnails.get_thumbnail(s.filename, (50, 60),
                                         ignore_temp=False)

        #check for right scaling
        s.failUnless(thumb)
        s.failUnlessEqual((thumb.get_width(), thumb.get_height()), (50, 25))

        #test the thumbnail filename
        uri = fsn2uri(s.filename)
        name = hash.md5(uri.encode("ascii")).hexdigest() + ".png"

        path = thumbnails.get_thumbnail_folder()
        path = os.path.join(path, "normal", name)

        s.failUnless(os.path.isfile(path))

        #check for metadata
        thumb_pb = GdkPixbuf.Pixbuf.new_from_file(path)
        meta_mtime = thumb_pb.get_option("tEXt::Thumb::MTime")
        meta_uri = thumb_pb.get_option("tEXt::Thumb::URI")

        s.failUnlessEqual(int(meta_mtime), int(mtime(s.filename)))
        s.failUnlessEqual(meta_uri, uri)

        #check rights
        if os.name != "nt":
            s.failUnlessEqual(os.stat(path).st_mode, 33152)
Example #7
0
def test_any_pathnames(path):
    fsn = path2fsn(path)
    abspath = os.path.abspath(fsn)
    if os.path.isabs(abspath):
        if is_wine:
            # FIXME: fails on native Windows
            assert uri2fsn(fsn2uri(abspath)) == abspath
Example #8
0
def get_example_db(song_path, rating, playcount, skipcount, lastplayed,
                   dateadded):
    # create a temporary database in memory
    db = sqlite3.connect(':memory:')

    # create a simplified version of a banshee track table
    csr = db.cursor()
    csr.execute('''CREATE TABLE CoreTracks(
                ArtistID INTEGER,
                AlbumID INTEGER,
                Uri TEXT,
                Title TEXT,
                Rating INTEGER,
                PlayCount INTEGER,
                SkipCount INTEGER,
                LastPlayedStamp INTEGER,
                DateAddedStamp INTEGER
                )
                ''')

    # insert song and save
    song_uri = fsn2uri(song_path)
    csr.execute('INSERT INTO CoreTracks VALUES (?,?,?,?,?,?,?,?,?)',
                (1, 1, song_uri, 'Music', rating, playcount, skipcount,
                lastplayed, dateadded))
    db.commit()

    # give the user the in-memory database
    return db
    def test_thumb(s):
        thumb = thumbnails.get_thumbnail(s.filename, (50, 60))

        #check for right scaling
        s.failUnless(thumb)
        s.failUnlessEqual((thumb.get_width(), thumb.get_height()), (50, 25))

        #test the thumbnail filename
        uri = fsn2uri(s.filename)
        name = hash.md5(uri.encode("ascii")).hexdigest() + ".png"

        path = thumbnails.get_thumbnail_folder()
        path = os.path.join(path, "normal", name)

        s.failUnless(os.path.isfile(path))

        #check for metadata
        thumb_pb = GdkPixbuf.Pixbuf.new_from_file(path)
        meta_mtime = thumb_pb.get_option("tEXt::Thumb::MTime")
        meta_uri = thumb_pb.get_option("tEXt::Thumb::URI")

        s.failUnlessEqual(int(meta_mtime), int(mtime(s.filename)))
        s.failUnlessEqual(meta_uri, uri)

        #check rights
        if os.name != "nt":
            s.failUnlessEqual(os.stat(path).st_mode, 33152)
Example #10
0
def get_cache_info(path, boundary):
    """For an image at `path` return (cache_path, thumb_size)

    cache_path points to a potential cache file
    thumb size is either 128 or 256
    """

    assert isinstance(path, fsnative)

    width, height = boundary

    if width <= ThumbSize.NORMAL and height <= ThumbSize.NORMAL:
        size_name = "normal"
        thumb_size = ThumbSize.NORMAL
    else:
        size_name = "large"
        thumb_size = ThumbSize.LARGE

    thumb_folder = get_thumbnail_folder()
    cache_dir = os.path.join(thumb_folder, size_name)

    uri = fsn2uri(path)
    thumb_name = hashlib.md5(uri).hexdigest() + ".png"
    thumb_path = os.path.join(cache_dir, thumb_name)

    return (thumb_path, thumb_size)
Example #11
0
    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:
            return fsn2uri(fileobj.name)
        return u""
Example #12
0
    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:
            return fsn2uri(fileobj.name)
        return u""
Example #13
0
def _show_files_thunar(dirname, entries):
    # https://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"

    try:
        dbus_proxy = _get_dbus_proxy(XFCE_NAME, XFCE_PATH, XFCE_IFACE)

        if not entries:
            dbus_proxy.DisplayFolder('(sss)',
                                     fsn2uri(dirname), "", _get_startup_id())
        else:
            dbus_proxy.DisplayFolderAndSelect(
                '(ssss)', fsn2uri(dirname), entries[0], "", _get_startup_id())
    except GLib.Error as e:
        raise BrowseError(e)
Example #14
0
def _show_files_fdo(dirname, entries):
    # https://www.freedesktop.org/wiki/Specifications/file-manager-interface/
    FDO_PATH = "/org/freedesktop/FileManager1"
    FDO_NAME = "org.freedesktop.FileManager1"
    FDO_IFACE = "org.freedesktop.FileManager1"

    try:
        dbus_proxy = _get_dbus_proxy(FDO_NAME, FDO_PATH, FDO_IFACE)

        if not entries:
            dbus_proxy.ShowFolders('(ass)',
                                   [fsn2uri(dirname)], _get_startup_id())
        else:
            item_uri = fsn2uri(os.path.join(dirname, entries[0]))
            dbus_proxy.ShowItems('(ass)', [item_uri], _get_startup_id())
    except GLib.Error as e:
        raise BrowseError(e)
Example #15
0
def _show_files_thunar(dirname, entries):
    # https://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"

    try:
        dbus_proxy = _get_dbus_proxy(XFCE_NAME, XFCE_PATH, XFCE_IFACE)

        if not entries:
            dbus_proxy.DisplayFolder('(sss)',
                                     fsn2uri(dirname), "", _get_startup_id())
        else:
            dbus_proxy.DisplayFolderAndSelect(
                '(ssss)', fsn2uri(dirname), entries[0], "", _get_startup_id())
    except GLib.Error as e:
        raise BrowseError(e)
Example #16
0
def _show_files_fdo(dirname, entries):
    # https://www.freedesktop.org/wiki/Specifications/file-manager-interface/
    FDO_PATH = "/org/freedesktop/FileManager1"
    FDO_NAME = "org.freedesktop.FileManager1"
    FDO_IFACE = "org.freedesktop.FileManager1"

    try:
        dbus_proxy = _get_dbus_proxy(FDO_NAME, FDO_PATH, FDO_IFACE)

        if not entries:
            dbus_proxy.ShowFolders('(ass)',
                                   [fsn2uri(dirname)], _get_startup_id())
        else:
            item_uri = fsn2uri(os.path.join(dirname, entries[0]))
            dbus_proxy.ShowItems('(ass)', [item_uri], _get_startup_id())
    except GLib.Error as e:
        raise BrowseError(e)
Example #17
0
        def label_path(path):
            l = Gtk.Label(label="<a href='%s'>%s</a>" % (
                            fsn2uri(path), escape(fsn2text(unexpand(path)))),
                          use_markup=True,
                          ellipsize=Pango.EllipsizeMode.MIDDLE,
                          xalign=0,
                          selectable=True)

            l.connect("activate-link", show_uri)
            return l
Example #18
0
        def label_path(path):
            l = Gtk.Label(label="<a href='%s'>%s</a>" % (
                            fsn2uri(path), escape(fsn2text(unexpand(path)))),
                          use_markup=True,
                          ellipsize=Pango.EllipsizeMode.MIDDLE,
                          xalign=0,
                          selectable=True)

            l.connect("activate-link", show_uri)
            return l
Example #19
0
    def test_roundtrip(self):
        if os.name == "nt":
            paths = [u"C:\\öäü.txt"]
        else:
            paths = [u"/öäü.txt", u"/a/foo/bar", u"/a/b/foo/bar"]

        for source in paths:
            path = uri2fsn(fsn2uri(fsnative(source)))
            self.assertTrue(isinstance(path, fsnative))
            self.assertEqual(path, fsnative(source))
Example #20
0
    def test_roundtrip(self):
        if os.name == "nt":
            paths = [u"C:\\öäü.txt"]
        else:
            paths = [u"/öäü.txt", u"//foo/bar", u"///foo/bar"]

        for source in paths:
            path = uri2fsn(fsn2uri(fsnative(source)))
            self.assertTrue(is_fsnative(path))
            self.assertEqual(path, fsnative(source))
Example #21
0
 def test_feed(self):
     fn = get_data_path('valid_feed.xml')
     feed = Feed(fsn2uri(fn))
     result = feed.parse()
     # Assume en_US / en_GB locale here in tests
     self.failIfEqual(feed.name, "Unknown", msg="Didn't find feed name")
     # Do this after the above, as many exceptions can be swallowed
     self.failUnless(result)
     self.failUnlessEqual(len(feed), 2)
     self.failUnlessEqual(feed[0]('title'),
                          'Full Episode: Tuesday, November 28, 2017')
Example #22
0
 def test_parse_onesong_uri(self):
     target = get_data_path("silence-44-s.ogg")
     target = fsn2uri(target).encode("ascii")
     target = self.prefix + target
     with temp_filename() as name:
         with open(name, "wb") as f:
             f.write(target)
         with open(name, "rb") as f:
             pl = self.Parse(f, name, pl_lib=self.pl_lib)
     self.failUnlessEqual(len(pl), 1)
     self.failUnlessEqual(pl[0]("title"), "Silence")
     pl.delete()
Example #23
0
def _show_files_fdo(dirname, entries):
    # http://www.freedesktop.org/wiki/Specifications/file-manager-interface
    FDO_PATH = "/org/freedesktop/FileManager1"
    FDO_NAME = "org.freedesktop.FileManager1"
    FDO_IFACE = "org.freedesktop.FileManager1"

    if not dbus:
        raise BrowseError("no dbus")

    try:
        bus = dbus.SessionBus()
        bus_object = bus.get_object(FDO_NAME, FDO_PATH)
        bus_iface = dbus.Interface(bus_object, dbus_interface=FDO_IFACE)

        if not entries:
            bus_iface.ShowFolders([fsn2uri(dirname)], _get_startup_id())
        else:
            item_uri = fsn2uri(os.path.join(dirname, entries[0]))
            bus_iface.ShowItems([item_uri], _get_startup_id())
    except dbus.DBusException as e:
        raise BrowseError(e)
 def test_parse_onesong_uri(self):
     target = get_data_path("silence-44-s.ogg")
     target = fsn2uri(target).encode("ascii")
     target = self.prefix + target
     with temp_filename() as name:
         with open(name, "wb") as f:
             f.write(target)
         with open(name, "rb") as f:
             pl = self.Parse(f, name)
     self.failUnlessEqual(len(pl), 1)
     self.failUnlessEqual(pl[0]("title"), "Silence")
     pl.delete()
Example #25
0
def _show_files_fdo(dirname, entries):
    # http://www.freedesktop.org/wiki/Specifications/file-manager-interface
    FDO_PATH = "/org/freedesktop/FileManager1"
    FDO_NAME = "org.freedesktop.FileManager1"
    FDO_IFACE = "org.freedesktop.FileManager1"

    if not dbus:
        raise BrowseError("no dbus")

    try:
        bus = dbus.SessionBus()
        bus_object = bus.get_object(FDO_NAME, FDO_PATH)
        bus_iface = dbus.Interface(bus_object, dbus_interface=FDO_IFACE)

        if not entries:
            bus_iface.ShowFolders([fsn2uri(dirname)], _get_startup_id())
        else:
            item_uri = fsn2uri(os.path.join(dirname, entries[0]))
            bus_iface.ShowItems([item_uri], _get_startup_id())
    except dbus.DBusException as e:
        raise BrowseError(e)
Example #26
0
def _show_files_thunar(dirname, entries):
    # 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)

        if not entries:
            bus_iface.DisplayFolder(fsn2uri(dirname), "", _get_startup_id())
        else:
            item_name = os.path.join(dirname, entries[0])
            bus_iface.DisplayFolderAndSelect(
                fsn2uri(dirname), item_name, "", _get_startup_id())
    except dbus.DBusException as e:
        raise BrowseError(e)
 def test_parse_onesong_uri(self):
     h, name = mkstemp()
     os.close(h)
     target = get_data_path("silence-44-s.ogg")
     target = fsn2uri(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()
Example #28
0
def _show_files_thunar(dirname, entries):
    # 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)

        if not entries:
            bus_iface.DisplayFolder(fsn2uri(dirname), "", _get_startup_id())
        else:
            item_name = os.path.join(dirname, entries[0])
            bus_iface.DisplayFolderAndSelect(fsn2uri(dirname), item_name, "",
                                             _get_startup_id())
    except dbus.DBusException as e:
        raise BrowseError(e)
Example #29
0
 def test_parse_onesong_uri(self):
     h, name = mkstemp()
     os.close(h)
     target = get_data_path("silence-44-s.ogg")
     target = fsn2uri(target).encode("ascii")
     target = self.prefix + target
     with open(name, "wb") 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()
Example #30
0
 def test_parse_onesong_uri(self):
     h, name = mkstemp()
     os.close(h)
     target = os.path.join(DATA_DIR, "silence-44-s.ogg")
     target = fsn2uri(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()
Example #31
0
def test_surrogates():
    if os.name == "nt":
        assert fsn2bytes(u"\ud83d", "utf-16-le") == b"=\xd8"
        assert bytes2fsn(b"\xd8=", "utf-16-be") == u"\ud83d"

        with pytest.raises(ValueError):
            bytes2fsn(b"\xd8=a", "utf-16-be")

        with pytest.raises(ValueError):
            bytes2fsn(b"=\xd8a", "utf-16-le")

        # for utf-16-le we have a workaround
        assert bytes2fsn(b"=\xd8", "utf-16-le") == u"\ud83d"
        assert bytes2fsn(b"=\xd8=\xd8", "utf-16-le") == u"\ud83d\ud83d"

        with pytest.raises(ValueError):
            bytes2fsn(b"=\xd8\x00\x00", "utf-16-le")

        # 4 byte code point
        assert fsn2bytes(u"\U0001f600", "utf-16-le") == b"=\xd8\x00\xde"
        assert bytes2fsn(b"=\xd8\x00\xde", "utf-16-le") == u"\U0001f600"

        # 4 byte codepoint + lone surrogate
        assert bytes2fsn(b"=\xd8\x00\xde=\xd8", "utf-16-le") == \
            u"\U0001f600\ud83d"

        with pytest.raises(UnicodeDecodeError):
            bytes2fsn(b"a", "utf-16-le")

        assert fsn2bytes(u"\ud83d", "utf-8") == b"\xed\xa0\xbd"
        assert bytes2fsn(b"\xed\xa0\xbd", "utf-8") == u"\ud83d"

        assert fsnative(u"\ud83d") == u"\ud83d"
        assert fsn2text(u"\ud83d") == u"\ufffd"

        # at least don't fail...
        assert fsn2uri(u"C:\\\ud83d") == u"file:///C:/%ED%A0%BD"
    else:
        # this shouldn't fail and produce the same result on py2/3 at least.
        assert fsn2bytes(fsnative(u"\ud83d"), None) == b"\xed\xa0\xbd"
        text2fsn(fsn2text(fsnative(u"\ud83d")))

        if PY2 and isunicodeencoding():
            # under Python 2 we get surrogates, but we can't do anything about
            # it since most codecs don't treat that as an error
            assert fsn2text(fsnative(u"\ud83d")) == u"\ud83d"
        else:
            # under Python 3 the decoder don't allow surrogates
            assert fsn2text(fsnative(u"\ud83d")) == u"\ufffd\ufffd\ufffd"
Example #32
0
 def write(self):
     track_list = Element("trackList")
     # TODO: ditch for proper indent, once we have Python 3.9
     track_list.text = "\n"
     for song in self._list:
         if isinstance(song, str):
             track = {"location": fsn2uri(song)}
         else:
             creator = self.CREATOR_PATTERN.format(song)
             mbid = song("musicbrainz_trackid")
             track = {
                 "location":
                 song("~uri"),
                 "identifier": (f"https://musicbrainz.org/recording/{mbid}"
                                if mbid else None),
                 "title":
                 song("title"),
                 "creator":
                 creator,
                 "album":
                 song("album"),
                 "trackNum":
                 song("~#track"),
                 "duration":
                 int(song("~#length") * 1000.)
             }
         track_list.append(self._element_from("track", track, True))
     playlist = Element("playlist",
                        attrib={
                            "version": "1",
                            "xmlns": XSPF_NS
                        })
     # Be kind to cat, git, editors etc. by leaving a final newline
     playlist.tail = "\n"
     playlist.append(self._version_tag())
     playlist.append(self._text_element("title", self.name))
     playlist.append(self._text_element("date", datetime.now().isoformat()))
     playlist.append(track_list)
     tree = ElementTree(playlist)
     ET.register_namespace("", XSPF_NS)
     path = self.path
     print_d(f"Writing {path !r}")
     tree.write(path, encoding="utf-8", xml_declaration=True)
     if self._last_fn != path:
         self._delete_file(self._last_fn)
         self._last_fn = path
Example #33
0
 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 [fsn2uri(self.__f.name)]
         elif name == "MIMEType":
             return "image/png"
         elif name == "Width" or name == "Height":
             return Icon.SIZE
         elif name == "ColorDepth":
             return self.__depth
Example #34
0
 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 [fsn2uri(self.__f.name)]
         elif name == "MIMEType":
             return "image/png"
         elif name == "Width" or name == "Height":
             return Icon.SIZE
         elif name == "ColorDepth":
             return self.__depth
Example #35
0
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(fsn2uri(dirname),
                                             sub_songs[0]("~basename"),
                                             display, get_startup_id())
    except dbus.DBusException as e:
        raise BrowseError(e)
Example #36
0
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(
                fsn2uri(dirname),
                sub_songs[0]("~basename"),
                display,
                get_startup_id())
    except dbus.DBusException as e:
        raise BrowseError(e)
Example #37
0
    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({fsn2uri(dir) for dir in dirs})
            print_d("Directories to drop: %s" % dirs)
            sel.set_uris(uris)
Example #38
0
    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({fsn2uri(dir) for dir in dirs})
            print_d("Directories to drop: %s" % dirs)
            sel.set_uris(uris)
Example #39
0
def test_fsn2uri():
    with pytest.raises(TypeError):
        fsn2uri(object())

    if os.name == "nt":
        with pytest.raises(TypeError):
            fsn2uri(u"\x00")
        assert fsn2uri(fsnative(u"C:\\foo")) == "file:///C:/foo"
        assert fsn2uri(u"C:\\ö ä%") == "file:///C:/%C3%B6%20%C3%A4%25"
        assert (fsn2uri(u"C:\\foo-\u1234") == "file:///C:/foo-%E1%88%B4")
        assert isinstance(fsn2uri(u"C:\\foo-\u1234"), text_type)
        assert fsn2uri(u"\\\serv\\share\\") == "file://serv/share/"
        assert fsn2uri(u"\\\serv\\\u1234\\") == "file://serv/%E1%88%B4/"
        assert \
            fsn2uri(fsnative(u"\\\\UNC\\foo\\bar")) == u"file://UNC/foo/bar"

        # winapi can't handle too large paths. make sure we raise properly
        with pytest.raises(ValueError):
            fsn2uri(u"C:\\" + 4000 * u"a")

        assert fsn2uri(u"C:\\\uD800\uDC01") == u"file:///C:/%F0%90%80%81"
    else:
        with pytest.raises(TypeError):
            fsn2uri(b"\x00")
        if PY2:
            path = "/foo-\xe1\x88\xb4"
        else:
            path = fsnative(u"/foo-\u1234")
        assert fsn2uri(path) == "file:///foo-%E1%88%B4"
        assert isinstance(fsn2uri(path), text_type)
Example #40
0
    def __get_metadata_real(self):
        """
        https://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata/
        """

        metadata = {}
        metadata["mpris:trackid"] = self.__get_current_track_id()

        def ignore_overflow(dbus_type, value):
            try:
                return dbus_type(value)
            except OverflowError:
                return 0

        song = app.player.info
        if not song:
            return metadata

        metadata["mpris:length"] = ignore_overflow(
            dbus.Int64, song("~#length") * 10 ** 6)

        if self.__cover is not None:
            self.__cover.close()
            self.__cover = None

        cover = app.cover_manager.get_cover(song)
        if cover:
            is_temp = cover.name.startswith(tempfile.gettempdir())
            if is_temp:
                self.__cover = cover
            metadata["mpris:artUrl"] = fsn2uri(cover.name)

        # All list values
        list_val = {"artist": "artist", "albumArtist": "albumartist",
            "comment": "comment", "composer": "composer", "genre": "genre",
            "lyricist": "lyricist"}
        for xesam, tag in iteritems(list_val):
            vals = song.list(tag)
            if vals:
                metadata["xesam:" + xesam] = listmap(unival, vals)

        # All single values
        sing_val = {"album": "album", "title": "title", "asText": "~lyrics"}
        for xesam, tag in iteritems(sing_val):
            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 iteritems(num_val):
            val = song("~#" + tag, None)
            if val is not None:
                metadata["xesam:" + xesam] = ignore_overflow(dbus.Int32, val)

        # Rating
        metadata["xesam:userRating"] = ignore_overflow(
            dbus.Double, 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
Example #41
0
 def test_win_unc_path(self):
     if os.name == "nt":
         self.assertEqual(fsn2uri(u"\\\\server\\share\\path"),
                          u"file://server/share/path")
Example #42
0
    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"] = fsn2uri(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 iteritems(list_val):
            vals = song.list(tag)
            if vals:
                metadata["xesam:" + xesam] = listmap(unival, vals)

        # All single values
        sing_val = {"album": "album", "title": "title", "asText": "~lyrics"}
        for xesam, tag in iteritems(sing_val):
            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 iteritems(num_val):
            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
Example #43
0
    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"] = fsn2uri(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 iteritems(list_val):
            vals = song.list(tag)
            if vals:
                metadata["xesam:" + xesam] = listmap(unival, vals)

        # All single values
        sing_val = {"album": "album", "title": "title", "asText": "~lyrics"}
        for xesam, tag in iteritems(sing_val):
            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 iteritems(num_val):
            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
Example #44
0
 def test_win_unc_path(self):
     if os.name == "nt":
         self.assertEqual(
             fsn2uri(u"\\\\server\\share\\path"),
             u"file://server/share/path")
Example #45
0
def get_thumbnail(path, boundary):
    """Get a thumbnail pixbuf of an image at `path`.

    Will create/use a thumbnail in the user's thumbnail directory if possible.
    Follows the Free Desktop specification:

    http://specifications.freedesktop.org/thumbnail-spec/

    Can raise GLib.GError. Thread-safe.
    """

    assert isinstance(path, fsnative)

    width, height = boundary
    new_from_file_at_size = GdkPixbuf.Pixbuf.new_from_file_at_size

    # larger than thumbnails, load directly
    if width > ThumbSize.LARGEST or height > ThumbSize.LARGEST:
        return new_from_file_at_size(path, width, height)

    path_mtime = mtime(path)
    if path_mtime == 0:
        return new_from_file_at_size(path, width, height)

    # embedded thumbnails come from /tmp/
    # FIXME: move this to another layer
    if path.startswith(tempfile.gettempdir()):
        return new_from_file_at_size(path, width, height)

    thumb_path, thumb_size = get_cache_info(path, boundary)
    cache_dir = os.path.dirname(thumb_path)
    try:
        mkdir(cache_dir, 0o700)
    except OSError:
        return new_from_file_at_size(path, width, height)

    try:
        pb = new_from_file_at_size(thumb_path, width, height)
    except GLib.GError:
        # in case it fails to load, we recreate it
        pass
    else:
        meta_mtime = pb.get_option("tEXt::Thumb::MTime")
        if meta_mtime is not None:
            try:
                meta_mtime = int(meta_mtime)
            except ValueError:
                pass
            else:
                if meta_mtime == int(path_mtime):
                    return pb

    info, pw, ph = GdkPixbuf.Pixbuf.get_file_info(path)

    # Too small picture, no thumbnail needed
    if pw < thumb_size and ph < thumb_size:
        return new_from_file_at_size(path, width, height)

    thumb_pb = new_from_file_at_size(path, thumb_size, thumb_size)

    uri = fsn2uri(path)
    mime = info.get_mime_types()[0]
    options = {
        "tEXt::Thumb::Image::Width": str(pw),
        "tEXt::Thumb::Image::Height": str(ph),
        "tEXt::Thumb::URI": uri,
        "tEXt::Thumb::MTime": str(int(path_mtime)),
        "tEXt::Thumb::Size": str(os.path.getsize(path)),
        "tEXt::Thumb::Mimetype": mime,
        "tEXt::Software": "QuodLibet"
    }

    thumb_pb.savev(thumb_path, "png", options.keys(), options.values())
    try:
        os.chmod(thumb_path, 0o600)
    except OSError:
        pass

    return scale(thumb_pb, boundary)
Example #46
0
    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)
Example #47
0
    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 text_type(fsn2uri(self["~filename"]))
            elif key == "format":
                return self.get("~format", 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)
Example #48
0
    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)