Beispiel #1
0
def MenuItems(marks, player, seekable):
    sizes = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
    items = []
    if not marks or marks[0][0] != 0:
        # Translators: Refers to the beginning of the playing song.
        marks.insert(0, (0, _("Beginning")))
    for time, mark in marks:
        i = Gtk.MenuItem()
        # older pygobject (~3.2) added a child on creation
        if i.get_child():
            i.remove(i.get_child())
        connect_obj(i, 'activate', player.seek, time * 1000)
        i.set_sensitive(time >= 0 and seekable)
        hbox = Gtk.HBox(spacing=12)
        i.add(hbox)
        if time < 0:
            l = Gtk.Label(label=_("N/A"))
        else:
            l = Gtk.Label(label=util.format_time(time))
        l.set_alignment(0.0, 0.5)
        sizes.add_widget(l)
        hbox.pack_start(l, False, True, 0)
        text = Gtk.Label(mark)
        text.set_max_width_chars(80)
        text.set_ellipsize(Pango.EllipsizeMode.END)
        text.set_alignment(0.0, 0.5)
        hbox.pack_start(text, True, True, 0)
        i.show_all()
        items.append(i)
    return items
Beispiel #2
0
    def step(self, **values):
        """Advance the counter by one. Arguments are applied to the
        originally-supplied text as a format string.

        This function doesn't return if the dialog is paused (though
        the GTK main loop will still run), and returns True if stop
        was pressed.
        """

        if self.count:
            self.current += 1
            self._progress.set_fraction(max(0, min(1, self.current / float(self.count))))
        else:
            self._progress.pulse()
        values.setdefault("total", self.count)
        values.setdefault("current", self.current)
        if self.count:
            t = (time.time() - self._start_time) / self.current
            remaining = math.ceil((self.count - self.current) * t)
            values.setdefault("remaining", util.format_time(remaining))
        self._label.set_markup(self._text % values)

        while not self.quit and (self.paused or gtk.events_pending()):
            gtk.main_iteration()
        return self.quit
Beispiel #3
0
 def __set_bookmarks(self, marks):
     result = []
     for time, mark in marks:
         if time < 0: raise ValueError("mark times must be positive")
         result.append(u"%s %s" % (util.format_time(time), mark))
     result = u"\n".join(result)
     if result: self["~bookmark"] = result
     elif "~bookmark" in self: del(self["~bookmark"])
Beispiel #4
0
 def __update_count(self, model, path, lab):
     if len(model) == 0: text = ""
     else:
         time = sum([row[0].get("~#length", 0) for row in model])
         text = ngettext("%(count)d song (%(time)s)",
                         "%(count)d songs (%(time)s)",
                         len(model)) % {
             "count": len(model), "time": util.format_time(time) }
     lab.set_text(text)
Beispiel #5
0
    def __init__(self, parent, library, player):
        super(EditBookmarks, self).__init__()
        self.set_transient_for(qltk.get_top_parent(parent))
        self.set_border_width(12)
        self.set_default_size(350, 250)
        self.set_title(_("Bookmarks") + " - %s" % player.song.comma("title"))

        self.add(EditBookmarksPane(library, player.song, close=True))

        s = library.connect("removed", self.__check_lock, player.song)
        self.connect_object("destroy", library.disconnect, s)

        position = player.get_position() // 1000
        self.child.time.set_text(util.format_time(position))
        self.child.markname.grab_focus()

        self.child.close.connect_object("clicked", qltk.Window.destroy, self)

        self.show_all()
Beispiel #6
0
    def __init__(self, parent, library, player):
        super(EditBookmarks, self).__init__()
        self.set_transient_for(qltk.get_top_parent(parent))
        self.set_border_width(12)
        self.set_default_size(350, 250)
        self.set_title(_("Bookmarks") + " - %s" % player.song.comma("title"))

        pane = EditBookmarksPane(library, player.song, close=True)
        self.add(pane)

        s = library.connect('removed', self.__check_lock, player.song)
        connect_obj(self, 'destroy', library.disconnect, s)

        position = player.get_position() // 1000
        pane.time.set_text(util.format_time(position))
        pane.markname.grab_focus()
        pane.close.connect('clicked', lambda *x: self.destroy())

        self.get_child().show_all()
Beispiel #7
0
    def __preview_pattern(self, edit, label):
        people = "\n".join(
            [util.tag("artist"), util.tag("performer"), util.tag("arranger")])
        album = FakeAlbum({"date": "2004-10-31",
            "~length": util.format_time(6319),
            "~long-length": util.format_time_long(6319),
            "~tracks": ngettext("%d track", "%d tracks", 5) % 5,
            "~discs": ngettext("%d disc", "%d discs", 2) % 2,
            "~people": people})

        try: text = XMLFromPattern(edit.text) % album
        except:
            text = _("Invalid pattern")
            edit.apply.set_sensitive(False)
        try: pango.parse_markup(text, u"\u0000")
        except gobject.GError:
            text = _("Invalid pattern")
            edit.apply.set_sensitive(False)
        else: edit.apply.set_sensitive(True)
        label.set_markup(text)
Beispiel #8
0
def MenuItems(marks, player, seekable):
    sizes = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
    items = []
    if not marks or marks[0][0] != 0:
        # Translators: Refers to the beginning of the playing song.
        marks.insert(0, (0, _("Beginning")))
    for time, mark in marks:
        i = gtk.MenuItem()
        i.connect_object("activate", player.seek, time * 1000)
        i.set_sensitive(time >= 0 and seekable)
        i.add(gtk.HBox(spacing=12))
        if time < 0:
            l = gtk.Label(_("N/A"))
        else:
            l = gtk.Label(util.format_time(time))
        l.set_alignment(0.0, 0.5)
        sizes.add_widget(l)
        i.child.pack_start(l, expand=False)
        m = gtk.Label(mark)
        m.set_alignment(0.0, 0.5)
        i.child.pack_start(m)
        i.show_all()
        items.append(i)
    return items
Beispiel #9
0
 def test_seconds(self):
     self.failUnlessEqual(util.format_time(0), "0:00")
     self.failUnlessEqual(util.format_time(59), "0:59")
Beispiel #10
0
 def _cdf(self, column, cell, model, iter, tag):
     value = model[iter][0].get("~#length", 0)
     if not self._needs_update(value): return
     text = util.format_time(value)
     cell.set_property('text', text)
     self._update_layout(text, cell)
Beispiel #11
0
 def __update_time(self, scale, timer):
     value = scale.get_value()
     max = scale.get_adjustment().upper
     value -= self.__remaining.get_active() * max
     timer.set_text(util.format_time(value))
Beispiel #12
0
 def cdf(column, cell, model, iter, data):
     if model[iter][0] < 0:
         cell.set_property('text', _("N/A"))
     else:
         cell.set_property('text', util.format_time(model[iter][0]))
Beispiel #13
0
    def set_time(self, time_):
        """Set the time in seconds"""

        self.set_text(util.format_time(time_))
Beispiel #14
0
 def test_negative(self):
     self.failUnlessEqual(util.format_time(-124), "-2:04")
Beispiel #15
0
class DuplicatesTreeModel(Gtk.TreeStore):
    """A tree store to model duplicated song information"""

    # Define columns to display (and how, in lieu of using qltk.browsers)
    def i(x):
        return x
    TAG_MAP = [
        ("artist", i), ("title", i), ("album", i),
        ("~#length", lambda s: util.format_time(int(s))),
        ("~#filesize", lambda s: util.format_size(int(s))), ("~#bitrate", i),
        ("~filename", i)]
    # Now make a dict. This seems clunky.
    tag_functions = {}
    for t, f in TAG_MAP:
        tag_functions[t] = f

    @classmethod
    def group_value(cls, group, tag):
        """Gets a formatted aggregated value/dummy for a set of tag values"""
        try:
            group_val = group[tag].safenicestr()
        except KeyError:
            return ""
        else:
            try:
                group_val = cls.tag_functions[tag](group_val)
            except (ValueError, TypeError):
                pass
        return group_val.replace("\n", ", ")

    def find_row(self, song):
        """Returns the row in the model from song, or None"""
        for parent in self:
            for row in parent.iterchildren():
                if row[0] == song:
                    self.__iter = row.iter
                    self.sourced = True
                    return row
        return None

    def add_to_existing_group(self, key, song):
        """Tries to add a song to an existing group. Returns None if not able
        """
        #print_d("Trying to add %s to group \"%s\"" % (song("~filename"), key))
        for parent in self:
            if key == parent[0]:
                print_d("Found group", self)
                return self.append(parent.iter, self.__make_row(song))
            # TODO: update group
        return None

    @classmethod
    def __make_row(cls, song):
        """Construct GTK row for a song, with all columns"""
        return [song] + [util.escape(str(f(song.comma(tag)))) for
                         (tag, f) in cls.TAG_MAP]

    def add_group(self, key, songs):
        """Adds a new group, returning the row created"""
        group = AudioFileGroup(songs)
        # Add the group first.
        parent = self.append(None,
            [key] +
            [self.group_value(group, tag) for tag, f in self.TAG_MAP])

        for s in songs:
            self.append(parent, self.__make_row(s))

    def go_to(self, song, explicit=False):
        #print_d("Duplicates: told to go to %r" % song, context=self)
        self.__iter = None
        if isinstance(song, Gtk.TreeIter):
            self.__iter = song
            self.sourced = True
        elif not self.find_row(song):
            print_d("Failed to find song", context=self)
        return self.__iter

    def remove(self, itr):
        if self.__iter and self[itr].path == self[self.__iter].path:
            self.__iter = None
        super(DuplicatesTreeModel, self).remove(itr)

    def get(self):
        return [row[0] for row in self]

    @property
    def get_current(self):
        if self.__iter is None:
            return None
        elif self.is_empty():
            return None
        else:
            return self[self.__iter][0]

    @property
    def get_current_path(self):
        if self.__iter is None:
            return None
        elif self.is_empty():
            return None
        else:
            return self[self.__iter].path

    @property
    def get_current_iter(self):
        if self.__iter is None:
            return None
        elif self.is_empty():
            return None
        else:
            return self.__iter

    def is_empty(self):
        return not len(self)

    def __init__(self):
        super(DuplicatesTreeModel, self).__init__(
            object, str, str, str, str, str, str, str)
Beispiel #16
0
    def __get_value(self, key):
        """This is similar to __call__ in the AudioFile class.
        All internal tags are changed to represent a collection of songs.
        """

        # Using key:<func> runs the resulting list of values
        # through the function before returning it.
        # Numeric keys without a func will default to a reasonable function
        if key.startswith("~#"):
            key = key[2:]

            if key[-4:-3] == ":":
                func = key[-3:]
                key = key[:-4]
            elif key == "tracks":
                return len(self.songs)
            elif key == "discs":
                return len(set([song("~#disc", 1) for song in self.songs]))
            elif key == "bitrate":
                length = self.__get_value("~#length")
                if not length: return 0
                w = lambda s: s("~#bitrate", 0) * s("~#length", 0)
                return sum(w(song) for song in self.songs) / length
            elif key in NUM_DEFAULT_FUNCS:
                func = NUM_DEFAULT_FUNCS[key]
            else:
                #Unknown key. AudioFile will try to cast the values to int,
                #default to avg
                func = "avg"

            key = "~#" + key

            func = NUM_FUNCS.get(func)
            if func:
                #if none of the songs can return a numeric key
                #the album returns default
                values = (song(key) for song in self.songs)
                values = [v for v in values if v != ""]
                if values: return func(values)
                else: return None
            elif key in INTERN_NUM_DEFAULT:
                return 0
            return None
        elif key[:1] == "~":
            key = key[1:]
            keys = {"people": {}, "peoplesort": {}}
            if key in keys:
                people = keys["people"]
                peoplesort = keys["peoplesort"]
                for song in self.songs:
                    # Rank people by "relevance" -- artists before composers
                    # before performers, then by number of appearances.
                    for w, k in enumerate(ELPOEP):
                        persons = song.list(k)
                        for person in persons:
                            people[person] = (people.get(person, 0) -
                                              PEOPLE_SCORE[w])
                        if k in TAG_TO_SORT:
                            persons = song.list(TAG_TO_SORT[k]) or persons
                        for person in persons:
                            peoplesort[person] = (peoplesort.get(person, 0) -
                                                  PEOPLE_SCORE[w])
                #It's cheaper to get people and peoplesort in one go
                keys["people"] = sorted(people.keys(),
                    key=people.__getitem__)[:100]
                keys["peoplesort"] = sorted(peoplesort.keys(),
                    key=peoplesort.__getitem__)[:100]

                ret = keys.pop(key)
                ret = (ret and "\n".join(ret)) or None

                other, values = keys.popitem()
                other = "~" + other
                if not values:
                    self.__default.add(other)
                else:
                    if other in self.__used:
                        self.__used.remove(other)
                    self.__used.append(other)
                    self.__cache[other] = "\n".join(values)

                return ret
            elif key == "length":
                length = self.__get_value("~#length")
                if length is None: return None
                return util.format_time(length)
            elif key == "long-length":
                length = self.__get_value("~#length")
                if length is None: return None
                return util.format_time_long(length)
            elif key == "tracks":
                tracks = self.__get_value("~#tracks")
                if tracks is None: return None
                return ngettext("%d track", "%d tracks", tracks) % tracks
            elif key == "discs":
                discs = self.__get_value("~#discs")
                if discs > 1:
                    return ngettext("%d disc", "%d discs", discs) % discs
                else: return None
            elif key == "rating":
                rating = self.__get_value("~#rating")
                if rating is None: return None
                return util.format_rating(rating)
            elif key == "cover":
                return ((self.cover != type(self).cover) and "y") or None
            elif key == "filesize":
                size = self.__get_value("~#filesize")
                if size is None: return None
                return util.format_size(size)
            key = "~" + key

        #Nothing special was found, so just take all values of the songs
        #and sort them by their number of appearance
        result = {}
        for song in self.songs:
            for value in song.list(key):
                result[value] = result.get(value, 0) - 1

        values = map(lambda x: x[0],
            sorted(result.items(), key=lambda x: x[1]))
        if not values: return None
        return "\n".join(values)
Beispiel #17
0
 def test_roundtrip(self):
     # The values are the ones tested for Tformat_time, so we know they
     # will be formatted correctly. They're also representative of
     # all the major patterns.
     for i in [0, 59, 60, 60 * 59 + 59, 60 * 60, 60 * 60 + 60 * 59 + 59]:
         self.failUnlessEqual(util.parse_time(util.format_time(i)), i)
Beispiel #18
0
 def test_negative(self):
     self.failUnlessEqual(util.format_time(-124), "-2:04")
Beispiel #19
0
 def test_hourss(self):
     self.failUnlessEqual(util.format_time(60 * 60), "1:00:00")
     self.failUnlessEqual(util.format_time(60 * 60 + 60 * 59 + 59),
                          "1:59:59")
Beispiel #20
0
 def test_minutes(self):
     self.failUnlessEqual(util.format_time(60), "1:00")
     self.failUnlessEqual(util.format_time(60 * 59 + 59), "59:59")
Beispiel #21
0
class Preferences(qltk.UniqueWindow):

    _EXAMPLE_ALBUM = FakeAlbum({
        "date":
        "2010-10-31",
        "~length":
        util.format_time(6319),
        "~long-length":
        util.format_time_long(6319),
        "~tracks":
        ngettext("%d track", "%d tracks", 5) % 5,
        "~discs":
        ngettext("%d disc", "%d discs", 2) % 2,
        "~#rating":
        0.75,
        "album":
        _("An Example Album"),
        "~people":
        _SOME_PEOPLE + "..."
    })

    def __init__(self, browser):
        if self.is_not_unique():
            return
        super(Preferences, self).__init__()
        self.set_border_width(12)
        self.set_title(_("Album List Preferences") + " - Quod Libet")
        self.set_default_size(420, 380)
        self.set_transient_for(qltk.get_top_parent(browser))
        # Do this config-driven setup at instance-time
        self._EXAMPLE_ALBUM["~rating"] = format_rating(0.75)

        box = Gtk.VBox(spacing=6)
        vbox = Gtk.VBox(spacing=6)
        cb = ConfigCheckButton(_("Show album _covers"), "browsers",
                               "album_covers")
        cb.set_active(config.getboolean("browsers", "album_covers"))
        gobject_weak(cb.connect, 'toggled', lambda s: browser.toggle_covers())
        vbox.pack_start(cb, False, True, 0)

        cb = ConfigCheckButton(_("Inline _search includes people"), "browsers",
                               "album_substrings")
        cb.set_active(config.getboolean("browsers", "album_substrings"))
        vbox.pack_start(cb, False, True, 0)
        f = qltk.Frame(_("Options"), child=vbox)
        box.pack_start(f, False, True, 12)

        vbox = Gtk.VBox(spacing=6)
        label = Gtk.Label()
        label.set_alignment(0.0, 0.5)
        label.set_padding(6, 6)
        eb = Gtk.EventBox()
        eb.get_style_context().add_class("entry")
        eb.add(label)

        edit = PatternEditBox(PATTERN)
        edit.text = browser._pattern_text
        gobject_weak(edit.apply.connect, 'clicked', self.__set_pattern, edit,
                     browser)
        gobject_weak(edit.buffer.connect_object,
                     'changed',
                     self.__preview_pattern,
                     edit,
                     label,
                     parent=edit)

        vbox.pack_start(eb, False, True, 3)
        vbox.pack_start(edit, True, True, 0)
        self.__preview_pattern(edit, label)
        f = qltk.Frame(_("Album Display"), child=vbox)
        box.pack_start(f, True, True, 0)

        main_box = Gtk.VBox(spacing=12)
        close = Gtk.Button(stock=Gtk.STOCK_CLOSE)
        close.connect('clicked', lambda *x: self.destroy())
        b = Gtk.HButtonBox()
        b.set_layout(Gtk.ButtonBoxStyle.END)
        b.pack_start(close, True, True, 0)

        main_box.pack_start(box, True, True, 0)
        main_box.pack_start(b, False, True, 0)
        self.add(main_box)

        close.grab_focus()
        self.show_all()

    def __set_pattern(self, apply, edit, browser):
        browser.refresh_pattern(edit.text)

    def __preview_pattern(self, edit, label):
        try:
            text = XMLFromMarkupPattern(edit.text) % self._EXAMPLE_ALBUM
        except:
            text = _("Invalid pattern")
            edit.apply.set_sensitive(False)
        try:
            Pango.parse_markup(text, -1, u"\u0000")
        except GLib.GError:
            text = _("Invalid pattern")
            edit.apply.set_sensitive(False)
        else:
            edit.apply.set_sensitive(True)
        label.set_markup(text)
Beispiel #22
0
 def test_hourss(self):
     self.failUnlessEqual(util.format_time(60 * 60), "1:00:00")
     self.failUnlessEqual(
         util.format_time(60 * 60 + 60 * 59 + 59), "1:59:59")
Beispiel #23
0
 def test_minutes(self):
     self.failUnlessEqual(util.format_time(60), "1:00")
     self.failUnlessEqual(util.format_time(60 * 59 + 59), "59:59")
Beispiel #24
0
 def test_roundtrip(self):
     # The values are the ones tested for Tformat_time, so we know they
     # will be formatted correctly. They're also representative of
     # all the major patterns.
     for i in [0, 59, 60, 60 * 59 + 59, 60 * 60, 60 * 60 + 60 * 59 + 59]:
         self.failUnlessEqual(util.parse_time(util.format_time(i)), i)
Beispiel #25
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.

        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)
Beispiel #26
0
    def __get_value(self, key):
        """This is similar to __call__ in the AudioFile class.
        All internal tags are changed to represent a collection of songs.
        """

        # Using key:<func> runs the resulting list of values
        # through the function before returning it.
        # Numeric keys without a func will default to a reasonable function
        if key.startswith("~#"):
            key = key[2:]

            if key[-4:-3] == ":":
                func = key[-3:]
                key = key[:-4]
            elif key == "tracks":
                return len(self.songs)
            elif key == "discs":
                return len({song("~#disc", 1) for song in self.songs})
            elif key == "bitrate":
                length = self.__get_value("~#length")
                if not length:
                    return 0
                w = lambda s: s("~#bitrate", 0) * s("~#length", 0)
                return sum(w(song) for song in self.songs) / length
            else:
                # Standard or unknown numeric key.
                # AudioFile will try to cast the values to int,
                # default to avg
                func = NUM_DEFAULT_FUNCS.get(key, "avg")

            key = "~#" + key
            func = NUM_FUNCS.get(func)
            if func:
                # If none of the songs can return a numeric key,
                # the album returns default
                values = (song(key) for song in self.songs)
                values = [v for v in values if v != ""]
                return func(values) if values else None
            elif key in INTERN_NUM_DEFAULT:
                return 0
            return None
        elif key[:1] == "~":
            key = key[1:]
            numkey = key.split(":")[0]
            keys = {"people": {}, "peoplesort": {}}
            if key in keys:
                people = keys["people"]
                peoplesort = keys["peoplesort"]
                for song in self.songs:
                    # Rank people by "relevance" -- artists before composers
                    # before performers, then by number of appearances.
                    for w, k in enumerate(ELPOEP):
                        persons = song.list(k)
                        for person in persons:
                            people[person] = (people.get(person, 0) -
                                              PEOPLE_SCORE[w])
                        if k in TAG_TO_SORT:
                            persons = song.list(TAG_TO_SORT[k]) or persons
                        for person in persons:
                            peoplesort[person] = (peoplesort.get(person, 0) -
                                                  PEOPLE_SCORE[w])
                # It's cheaper to get people and peoplesort in one go
                keys["people"] = sorted(people.keys(),
                                        key=people.__getitem__)[:100]
                keys["peoplesort"] = sorted(peoplesort.keys(),
                                            key=peoplesort.__getitem__)[:100]

                ret = keys.pop(key)
                ret = (ret and "\n".join(ret)) or None

                other, values = keys.popitem()
                other = "~" + other
                if not values:
                    self.__default.add(other)
                else:
                    if other in self.__used:
                        self.__used.remove(other)
                    self.__used.append(other)
                    self.__cache[other] = "\n".join(values)
                return ret
            elif numkey == "length":
                length = self.__get_value("~#" + key)
                return None if length is None else util.format_time(length)
            elif numkey == "long-length":
                length = self.__get_value("~#" + key[5:])
                return (None
                        if length is None else util.format_time_long(length))
            elif numkey == "tracks":
                tracks = self.__get_value("~#" + key)
                return (None if tracks is None else
                        ngettext("%d track", "%d tracks", tracks) % tracks)
            elif numkey == "discs":
                discs = self.__get_value("~#" + key)
                if discs > 1:
                    return ngettext("%d disc", "%d discs", discs) % discs
                else:
                    # TODO: check this is correct for discs == 1
                    return None
            elif numkey == "rating":
                rating = self.__get_value("~#" + key)
                if rating is None:
                    return None
                return util.format_rating(rating)
            elif key == "cover":
                return ((self.cover != type(self).cover) and "y") or None
            elif numkey == "filesize":
                size = self.__get_value("~#" + key)
                return None if size is None else util.format_size(size)
            key = "~" + key

        # Nothing special was found, so just take all values of the songs
        # and sort them by their number of appearance
        result = {}
        for song in self.songs:
            for value in song.list(key):
                result[value] = result.get(value, 0) - 1

        values = map(lambda x: x[0], sorted(result.items(),
                                            key=lambda x: x[1]))
        return "\n".join(values) if values else None
Beispiel #27
0
 def test_seconds(self):
     self.failUnlessEqual(util.format_time(0), "0:00")
     self.failUnlessEqual(util.format_time(59), "0:59")
Beispiel #28
0
 def cdf(column, cell, model, iter, data):
     if model[iter][0] < 0:
         cell.set_property('text', _("N/A"))
     else:
         cell.set_property('text', util.format_time(model[iter][0]))
 def _get_min_width(self):
     # 1:22:22, allows entire albums as files (< 75mins)
     return self._cell_width(util.format_time(60 * 82 + 22))