예제 #1
0
class DummySongObject(MediaItem, MediaObject, DBusPropertyFilter,
                      DBusIntrospectable):
    """ A dummy song object that is not exported on the bus, but supports
    the usual interfaces.

    You need to assign a real song before using it, and have to pass
    a path prefix.

    The path of the song is /org/gnome/UPnP/MediaServer2/Song/<PREFIX>/SongID
    This lets us reconstruct the original parent path:
    /org/gnome/UPnP/MediaServer2/<PREFIX>

    atm. a prefix can look like "Albums/123456"
    """

    SUPPORTS_MULTIPLE_OBJECT_PATHS = False
    __pattern = Pattern("<discnumber|<discnumber>.><tracknumber>. <title>")

    def __init__(self, parent):
        DBusIntrospectable.__init__(self)
        DBusPropertyFilter.__init__(self)
        MediaObject.__init__(self, parent)
        MediaItem.__init__(self, optional=SUPPORTED_SONG_PROPERTIES)

    def set_song(self, song, prefix):
        self.__song = song
        self.__prefix = prefix

    def get_property(self, interface, name):
        if interface == MediaObject.IFACE:
            if name == "Parent":
                return BASE_PATH + "/" + self.__prefix
            elif name == "Type":
                return "music"
            elif name == "Path":
                path = SongObject.PATH
                path += "/" + self.__prefix + "/" + str(id(self.__song))
                return path
            elif name == "DisplayName":
                return unival(self.__song.comma("title"))
        elif interface == MediaItem.IFACE:
            if name == "URLs":
                return [self.__song("~uri")]
            elif name == "MIMEType":
                mimes = self.__song.mimes
                return mimes and mimes[0]
            elif name == "Size":
                return self.__song("~#filesize")
            elif name == "Artist":
                return unival(self.__song.comma("artist"))
            elif name == "Album":
                return unival(self.__song.comma("album"))
            elif name == "Date":
                return unival(self.__song.comma("date"))
            elif name == "Genre":
                return unival(self.__song.comma("genre"))
            elif name == "Duration":
                return self.__song("~#length")
            elif name == "TrackNumber":
                return self.__song("~#track", 0)
예제 #2
0
    def __save(self, *data):
        """Save the cover and spawn the program to edit it if selected"""

        filename = self.name_combo.get_active_text()
        # Allow support for filename patterns
        pattern = Pattern(filename)
        filename = fsencode(pattern.format(self.song))
        file_path = os.path.join(self.dirname, filename)

        msg = (_('The file <b>%s</b> already exists.\n\nOverwrite?')
                % util.escape(filename))
        if (os.path.exists(file_path)
                and not qltk.ConfirmAction(None, _('File exists'), msg).run()):
            return

        try:
            f = open(file_path, 'wb')
            f.write(self.current_data)
            f.close()
        except IOError:
            qltk.ErrorMessage(None, _('Saving failed'),
                _('Unable to save "%s".') % file_path).run()
        else:
            if self.open_check.get_active():
                try:
                    util.spawn([self.cmd.get_text(), file_path])
                except:
                    pass

            app.window.emit("artwork-changed", [self.song])

        self.main_win.destroy()
    def __changed_entry(self, entry, cbs, label):
        text = entry.get_text()
        if text[0:1] == "<" and text[-1:] == ">":
            parts = text[1:-1].split("~")
            for cb in cbs:
                if parts and parts[0] == cb.tag:
                    parts.pop(0)
            if parts:
                for cb in cbs:
                    cb.set_inconsistent(True)
            else:
                parts = text[1:-1].split("~")
                for cb in cbs:
                    cb.set_inconsistent(False)
                    cb.set_active(cb.tag in parts)
        else:
            for cb in cbs:
                cb.set_inconsistent(True)

        if app.player.info is None:
            text = _("Not playing")
        else:
            text = Pattern(entry.get_text()) % app.player.info
        label.set_text(text)
        label.get_parent().set_tooltip_text(text)
        config.set("plugins", "icon_tooltip", entry.get_text())
예제 #4
0
def pattern(pat, cap=True, esc=False):
    """Return a 'natural' version of the pattern string for human-readable
    bits. Assumes all tags in the pattern are present."""
    from quodlibet.parse import Pattern, XMLFromPattern

    class Fakesong(dict):
        cap = False

        def comma(self, key):
            return " - ".join(self.list(key))

        def list(self, key):
            return [tag(k, self.cap) for k in tagsplit(key)]

        list_seperate = list
        __call__ = comma

    fakesong = Fakesong({'filename': tag('filename', cap)})
    fakesong.cap = cap
    try:
        p = (esc and XMLFromPattern(pat)) or Pattern(pat)
    except ValueError:
        return _("Invalid pattern")

    return p.format(fakesong)
예제 #5
0
    def __init__(self, *args, **kwargs):
        super(PatternColumn, self).__init__(*args, **kwargs)

        try:
            self._pattern = Pattern(self.header_name)
        except ValueError:
            self._pattern = None
    def __get_sort_tag(self, tag):
        replace_order = {
            "~#track": "",
            "~#disc": "",
            "~length": "~#length"
        }

        if tag == "~title~version":
            tag = "title"
        elif tag == "~album~discsubtitle":
            tag = "album"

        if tag.startswith("<"):
            for key, value in replace_order.iteritems():
                tag = tag.replace("<%s>" % key, "<%s>" % value)
            tag = Pattern(tag).format
        else:
            tags = util.tagsplit(tag)
            sort_tags = []
            for tag in tags:
                tag = replace_order.get(tag, tag)
                tag = TAG_TO_SORT.get(tag, tag)
                if tag not in sort_tags:
                    sort_tags.append(tag)
            if len(sort_tags) > 1:
                tag = "~" + "~".join(sort_tags)

        return tag
예제 #7
0
def get_sort_tag(tag):
    """Returns a tag that can be used for sorting for the given column tag.

    Returns '' if the default sort key should be used.
    """

    replace_order = {"~#track": "", "~#disc": "", "~length": "~#length"}

    if tag == "~title~version":
        tag = "title"
    elif tag == "~album~discsubtitle":
        tag = "album"

    if tag.startswith("<"):
        for key, value in replace_order.iteritems():
            tag = tag.replace("<%s>" % key, "<%s>" % value)
        tag = Pattern(tag).format
    else:
        tags = util.tagsplit(tag)
        sort_tags = []
        for tag in tags:
            tag = replace_order.get(tag, tag)
            tag = TAG_TO_SORT.get(tag, tag)
            if tag not in sort_tags:
                sort_tags.append(tag)
        if len(sort_tags) > 1:
            tag = "~" + "~".join(sort_tags)

    return tag
예제 #8
0
class DummyAlbumObject(MediaContainer, MediaObject, DBusPropertyFilter,
                       DBusIntrospectable):

    SUPPORTS_MULTIPLE_OBJECT_PATHS = False
    __pattern = Pattern("<albumartist|<~albumartist~album>|<~artist~album>>")

    def __init__(self, parent):
        DBusIntrospectable.__init__(self)
        DBusPropertyFilter.__init__(self)
        MediaObject.__init__(self, parent)
        MediaContainer.__init__(self)
        self.__song = DummySongObject(self)

    def get_dummy(self, song):
        self.__song.set_song(song, "Albums/" + str(id(self.__album)))
        return self.__song

    def set_album(self, album):
        self.__album = album
        self.PATH = self.parent.PATH + "/" + str(id(album))

    def get_property(self, interface, name):
        if interface == MediaContainer.IFACE:
            if name == "ChildCount" or name == "ItemCount":
                return len(self.__album.songs)
            elif name == "ContainerCount":
                return 0
            elif name == "Searchable":
                return False
        elif interface == MediaObject.IFACE:
            if name == "Parent":
                return self.parent.PATH
            elif name == "Type":
                return "container"
            elif name == "Path":
                return self.PATH
            elif name == "DisplayName":
                return unival(self.__pattern % self.__album)

    def list_containers(self, offset, max_, filter_):
        return []

    def list_items(self, offset, max_, filter_):
        songs = sorted(self.__album.songs, key=lambda s: s.sort_key)
        dummy = self.get_dummy(None)
        props = dummy.get_properties_for_filter(MediaItem.IFACE, filter_)
        end = (max_ and offset + max_) or None

        result = []
        for song in songs[offset:end]:
            result.append(self.get_dummy(song).get_values(props))
        return result

    list_children = list_items
예제 #9
0
 def test_escape_slash(s):
     fpat = FileFromPattern('<~filename>')
     pat = Pattern('<~filename>')
     wpat = FileFromPattern(r'\\<artist>\\ "<title>')
     s.assertEquals(fpat.format(s.a), "_path_to_a.mp3")
     s.assertEquals(pat.format(s.a), "/path/to/a.mp3")
     if os.name != "nt":
         s.assertEquals(wpat.format(s.a), "\\Artist\\ \"Title5.mp3")
     else:
         # FIXME..
         pass
예제 #10
0
    def plugin_on_song_started(self, song):
        if not self._icon:
            return

        if song:
            try:
                pattern = Pattern(config.get("plugins", "icon_tooltip"))
            except (ValueError, config.Error):
                pattern = self.__pattern

            tooltip = pattern % song
        else:
            tooltip = _("Not playing")

        self._icon.set_tooltip_markup(util.escape(tooltip))
예제 #11
0
def print_playing(fstring="<artist~album~tracknumber~title>"):
    import quodlibet
    from quodlibet.formats._audio import AudioFile
    from quodlibet.parse import Pattern
    from quodlibet import const

    try:
        text = open(const.CURRENT, "rb").read()
        song = AudioFile()
        song.from_dump(text)
        print_(Pattern(fstring).format(song))
        quodlibet.exit()
    except (OSError, IOError):
        print_(_("No song is currently playing."))
        quodlibet.exit(True)
예제 #12
0
 def test_tied(s):
     pat = Pattern('<genre>')
     s.failUnlessEqual(pat.format_list(s.c), set(['/', '/']))
     pat = Pattern('<performer>')
     s.failUnlessEqual(pat.format_list(s.d), set(['a', 'b']))
     pat = Pattern('<performer><performer>')
     s.failUnlessEqual(set(pat.format_list(s.d)),
         set(['aa', 'ab', 'ba', 'bb']))
     pat = Pattern('<~performer~artist>')
     s.failUnlessEqual(pat.format_list(s.d),
         set(['a - foo', 'b - foo', 'a - bar', 'b - bar']))
     pat = Pattern('<performer~artist>')
     s.failUnlessEqual(pat.format_list(s.d),
         set(['a - foo', 'b - foo', 'a - bar', 'b - bar']))
     pat = Pattern('<artist|<artist>.|<performer>>')
     s.failUnlessEqual(pat.format_list(s.d), set(['foo.', 'bar.']))
     pat = Pattern('<artist|<artist|<artist>.|<performer>>>')
     s.failUnlessEqual(pat.format_list(s.d), set(['foo.', 'bar.']))
예제 #13
0
    def set_all_column_headers(cls, headers):
        config.set_columns(headers)
        try:
            headers.remove("~current")
        except ValueError:
            pass
        cls.headers = headers
        for listview in cls.instances():
            listview.set_column_headers(headers)

        star = list(Query.STAR)
        for header in headers:
            if "<" in header:
                try:
                    tags = Pattern(header).tags
                except ValueError:
                    continue
            else:
                tags = util.tagsplit(header)
            for tag in tags:
                if not tag.startswith("~#") and tag not in star:
                    star.append(tag)
        SongList.star = star
예제 #14
0
 def test_conditional_unknown(s):
     pat = Pattern('<album|foo|bar>')
     s.assertEquals(pat.format(s.a), 'bar')
예제 #15
0
 def test_conditional_genre(s):
     pat = Pattern('<genre|<genre>|music>')
     s.assertEquals(pat.format(s.a), 'music')
     s.assertEquals(pat.format(s.b), 'music')
     s.assertEquals(pat.format(s.c), '/, /')
예제 #16
0
 def test_conditional_other_other(s):
     # FIXME: was <tracknumber|a|b|c>.. but we can't put <>| in the format
     # string since it would break the XML pattern formater.
     s.assertEqual(Pattern('<tracknumber|a|b|c>').format(s.a), "")
예제 #17
0
 def test_conditional_other_number_dot_title(s):
     pat = Pattern('<tracknumber|<tracknumber>|00>. <title>')
     s.assertEquals(pat.format(s.a), '5/6. Title5')
     s.assertEquals(pat.format(s.b), '6. Title6')
     s.assertEquals(pat.format(s.c), '00. test/subdir')
예제 #18
0
    def test_missing_value(self):
        pat = Pattern('<genre> - <artist>')
        self.assertEqual(pat.format_list(self.a), set([" - Artist"]))

        pat = Pattern('')
        self.assertEqual(pat.format_list(self.a), set([""]))
예제 #19
0
 def test_same(s):
     pat = Pattern('<~basename> <title>')
     s.failUnlessEqual(pat.format_list(s.a), set([pat.format(s.a)]))
     pat = Pattern('/a<genre|/<genre>>/<title>')
     s.failUnlessEqual(pat.format_list(s.a), set([pat.format(s.a)]))
예제 #20
0
 def test_generated(s):
     pat = Pattern('<~basename>')
     s.assertEquals(pat.format(s.a), os.path.basename(s.a["~filename"]))
예제 #21
0
 def test_number_dot_title(s):
     pat = Pattern('<tracknumber>. <title>')
     s.assertEquals(pat.format(s.a), '5/6. Title5')
     s.assertEquals(pat.format(s.b), '6. Title6')
     s.assertEquals(pat.format(s.c), '. test/subdir')
예제 #22
0
 def test_unicode_with_int(s):
     song = s.AudioFile({"tracknumber": "5/6",
         "title": "\xe3\x81\x99\xe3\x81\xbf\xe3\x82\x8c".decode('utf-8')})
     pat = Pattern('<~#track>. <title>')
     s.assertEquals(pat.format(song),
         "5. \xe3\x81\x99\xe3\x81\xbf\xe3\x82\x8c".decode('utf-8'))
예제 #23
0
 def test_number_dot_genre(s):
     pat = Pattern('<tracknumber>. <genre>')
     s.assertEquals(pat.format(s.a), '5/6. ')
     s.assertEquals(pat.format(s.b), '6. ')
     s.assertEquals(pat.format(s.c), '. /, /')
예제 #24
0
 def test_conditional_notfile(s):
     pat = Pattern('<tracknumber|<tracknumber>|00>')
     s.assertEquals(pat.format(s.a), '5/6')
     s.assertEquals(pat.format(s.b), '6')
     s.assertEquals(pat.format(s.c), '00')
예제 #25
0
 def test_conditional_subdir(s):
     pat = Pattern('/a<genre|/<genre>>/<title>')
     s.assertEquals(pat.format(s.a), '/a/Title5')
     s.assertEquals(pat.format(s.b), '/a/Title6')
     s.assertEquals(pat.format(s.c), '/a//, //test/subdir')
예제 #26
0
 def test_same2(s):
     fpat = FileFromPattern('<~filename>')
     pat = Pattern('<~filename>')
     s.assertEquals(fpat.format_list(s.a), set([fpat.format(s.a)]))
     s.assertEquals(pat.format_list(s.a), set([pat.format(s.a)]))
예제 #27
0
 def test_recnumber_dot_title(s):
     pat = Pattern('\<<tracknumber>\>. <title>')
     s.assertEquals(pat.format(s.a), '<5/6>. Title5')
     s.assertEquals(pat.format(s.b), '<6>. Title6')
     s.assertEquals(pat.format(s.c), '<>. test/subdir')
예제 #28
0
 def test_empty(self):
     self.failUnlessEqual(Pattern("").tags, set([]))
예제 #29
0
 def test_generated_and_not_generated(s):
     pat = Pattern('<~basename> <title>')
     res = pat.format(s.a)
     s.assertEquals(
         res, os.path.basename(s.a["~filename"]) + " " + s.a["title"])
예제 #30
0
 def test_both(self):
     pat = "<foo|<~bar~fuu> - <fa>|<bar>>"
     self.failUnlessEqual(Pattern(pat).tags, set(["bar", "fuu", "fa"]))