예제 #1
0
    def _album(self, song, box):
        if "album" not in song: return
        w = Label("")
        text = []
        text.append("<i>%s</i>" % util.escape(song.comma("album")))
        if "date" in song:
            text[-1] += " (%s)" % util.escape(song.comma("date"))
        secondary = []
        if "discnumber" in song:
            secondary.append(_("Disc %s") % song["discnumber"])
        if "discsubtitle" in song:
            secondary.append("<i>%s</i>" %
                             util.escape(song.comma("discsubtitle")))
        if "tracknumber" in song:
            secondary.append(_("Track %s") % song["tracknumber"])
        if secondary: text.append(" - ".join(secondary))

        if "organization" in song or "labelid" in song:
            t = util.escape(song.comma("~organization~labelid"))
            text.append(t)

        if "producer" in song:
            text.append("Produced by %s" %(
                util.escape(song.comma("producer"))))

        w.set_markup("\n".join(text))
        w.set_ellipsize(pango.ELLIPSIZE_END)
        hb = gtk.HBox(spacing=12)

        cover = CoverImage(song=song)
        if cover: hb.pack_start(cover, expand=False)
        else: cover.destroy()

        hb.pack_start(w)
        box.pack_start(Frame(tag("album"), hb), expand=False, fill=False)
예제 #2
0
class TopBar(gtk.HBox):
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__(spacing=3)

        # play controls
        t = PlayControls(player, library.librarian)
        self.volume = t.volume
        self.pack_start(t, expand=False, fill=False)

        # song text
        text = SongInfo(library.librarian, player)
        self.pack_start(Alignment(text, border=3))

        # cover image
        self.image = CoverImage(resize=True)
        player.connect('song-started', lambda x, s: self.image.set_song(s))
        parent.connect('artwork-changed', self.__song_art_changed, library)
        self.pack_start(self.image, expand=False)

    def __song_art_changed(self, player, songs, library):
        self.image.refresh()
        refresh_albums = []
        for song in songs:
            # Album browser only (currently):
            album = library.albums.get(song.album_key, None)
            if album:
                album.scan_cover(force=True)
                refresh_albums.append(album)
        if refresh_albums:
            library.albums.refresh(refresh_albums)
예제 #3
0
class TopBar(Gtk.Toolbar):
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__()

        # play controls
        control_item = Gtk.ToolItem()
        self.insert(control_item, 0)
        t = PlayControls(player, library.librarian)
        self.volume = t.volume
        control_item.add(t)

        self.insert(Gtk.SeparatorToolItem(), 1)

        info_item = Gtk.ToolItem()
        self.insert(info_item, 2)
        info_item.set_expand(True)

        box = Gtk.Box(spacing=6)
        info_item.add(box)
        qltk.add_css(self, "GtkToolbar {padding: 3px;}")

        # song text
        info_pattern_path = os.path.join(const.USERDIR, "songinfo")
        text = SongInfo(library.librarian, player, info_pattern_path)
        box.pack_start(Align(text, border=3), True, True, 0)

        # cover image
        self.image = CoverImage(resize=True)
        connect_destroy(player, 'song-started', self.__new_song)

        # FIXME: makes testing easier
        if app.cover_manager:
            connect_destroy(
                app.cover_manager, 'cover-changed',
                self.__song_art_changed, library)

        self.image.props.margin = 2
        box.pack_start(self.image, False, True, 0)

        for child in self.get_children():
            child.show_all()

        context = self.get_style_context()
        context.add_class("primary-toolbar")

    def __new_song(self, player, song):
        self.image.set_song(song)

    def __song_art_changed(self, player, songs, library):
        self.image.refresh()
        refresh_albums = []
        for song in songs:
            # Album browser only (currently):
            album = library.albums.get(song.album_key, None)
            if album:
                album.scan_cover(force=True)
                refresh_albums.append(album)
        if refresh_albums:
            library.albums.refresh(refresh_albums)
예제 #4
0
class TopBar(Gtk.Toolbar):
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__()

        # play controls
        control_item = Gtk.ToolItem()
        self.insert(control_item, 0)
        t = PlayControls(player, library.librarian)
        self.volume = t.volume
        control_item.add(t)

        self.insert(Gtk.SeparatorToolItem(), 1)

        info_item = Gtk.ToolItem()
        self.insert(info_item, 2)
        info_item.set_expand(True)

        box = Gtk.Box(spacing=6)
        info_item.add(box)
        qltk.add_css(self, "GtkToolbar {padding: 3px;}")

        # song text
        text = SongInfo(library.librarian, player)
        box.pack_start(Alignment(text, border=3), True, True, 0)

        # cover image
        self.image = CoverImage(resize=True)
        gobject_weak(player.connect, 'song-started', self.__new_song,
                     parent=self)
        gobject_weak(parent.connect, 'artwork-changed',
                     self.__song_art_changed, library, parent=self)

        # CoverImage doesn't behave in a Alignment, so wrap it
        coverbox = Gtk.Box()
        coverbox.pack_start(self.image, True, True, 0)
        box.pack_start(Alignment(coverbox, border=2), False, True, 0)

        for child in self.get_children():
            child.show_all()

        context = self.get_style_context()
        context.add_class("primary-toolbar")

    def __new_song(self, player, song):
        self.image.set_song(song)

    def __song_art_changed(self, player, songs, library):
        self.image.refresh()
        refresh_albums = []
        for song in songs:
            # Album browser only (currently):
            album = library.albums.get(song.album_key, None)
            if album:
                album.scan_cover(force=True)
                refresh_albums.append(album)
        if refresh_albums:
            library.albums.refresh(refresh_albums)
예제 #5
0
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__()

        # play controls
        control_item = Gtk.ToolItem()
        self.insert(control_item, 0)
        t = PlayControls(player, library.librarian)
        self.volume = t.volume

        # only restore the volume in case it is managed locally, otherwise
        # this could affect the system volume
        if not player.has_external_volume:
            player.volume = config.getfloat("memory", "volume")

        connect_destroy(player, "notify::volume", self._on_volume_changed)
        control_item.add(t)

        self.insert(Gtk.SeparatorToolItem(), 1)

        info_item = Gtk.ToolItem()
        self.insert(info_item, 2)
        info_item.set_expand(True)

        box = Gtk.Box(spacing=6)
        info_item.add(box)
        qltk.add_css(self, "GtkToolbar {padding: 3px;}")

        self._pattern_box = Gtk.VBox()

        # song text
        info_pattern_path = os.path.join(quodlibet.get_user_dir(), "songinfo")
        text = SongInfo(library.librarian, player, info_pattern_path)
        self._pattern_box.pack_start(Align(text, border=3), True, True, 0)
        box.pack_start(self._pattern_box, True, True, 0)

        # cover image
        self.image = CoverImage(resize=True)
        connect_destroy(player, 'song-started', self.__new_song)

        # FIXME: makes testing easier
        if app.cover_manager:
            connect_destroy(
                app.cover_manager, 'cover-changed',
                self.__song_art_changed, library)

        box.pack_start(Align(self.image, border=2), False, True, 0)

        # On older Gtk+ (3.4, at least)
        # setting a margin on CoverImage leads to errors and result in the
        # QL window not being visible for some reason.
        assert self.image.props.margin == 0

        for child in self.get_children():
            child.show_all()

        context = self.get_style_context()
        context.add_class("primary-toolbar")
예제 #6
0
    def _album(self, songs, box):
        text = []

        discs = {}
        for song in songs:
            try:
                discs[song("~#disc")] = int(
                    song["tracknumber"].split("/")[1])
            except (AttributeError, ValueError, IndexError, KeyError):
                discs[song("~#disc")] = max([
                    song("~#track", discs.get(song("~#disc"), 0))])
        tracks = sum(discs.values())
        discs = len(discs)
        length = sum([song.get("~#length", 0) for song in songs])

        if tracks == 0 or tracks < len(songs): tracks = len(songs)

        parts = []
        if discs > 1:
            parts.append(
                ngettext("%d disc", "%d discs", discs) % discs)
        parts.append(
                ngettext("%d track", "%d tracks", tracks) % tracks)
        if tracks != len(songs):
            parts.append(ngettext("%d selected", "%d selected",
                len(songs)) % len(songs))

        text.append(", ".join(parts))
        text.append(util.format_time_long(length))

        if "location" in song:
            text.append(util.escape(song["location"]))
        if "organization" in song or "labelid" in song:
            t = util.escape(song.comma("~organization~labelid"))
            text.append(t)

        if "producer" in song:
            text.append(_("Produced by %s") %(
                util.escape(song.comma("producer"))))

        w = Label("")
        w.set_ellipsize(pango.ELLIPSIZE_END)
        w.set_markup("\n".join(text))
        hb = gtk.HBox(spacing=12)

        cover = CoverImage(song=song)
        if cover: hb.pack_start(cover, expand=False)
        else: cover.destroy()

        hb.pack_start(w)
        box.pack_start(hb, expand=False, fill=False)
예제 #7
0
    def _album(self, songs, box):
        noalbum = 0
        albums = {}
        for song in songs:
            if "album" in song:
                albums[song.list("album")[0]] = song
            else:
                noalbum += 1
        albums = [(song.get("date"), song, album)
                  for album, song in albums.items()]
        albums.sort()

        def format(args):
            date, song, album = args
            if date:
                return "%s (%s)" % (album, date[:4])
            else:
                return album

        get_cover = app.cover_manager.get_cover
        covers = [(a, get_cover(s), s) for d, s, a in albums]
        albums = map(format, albums)
        if noalbum:
            albums.append(
                ngettext("%d song with no album", "%d songs with no album",
                         noalbum) % noalbum)
        l = Label("\n".join(albums))
        l.set_ellipsize(Pango.EllipsizeMode.END)
        box.pack_start(Frame(_("Selected Discography"), l), False, False, 0)

        covers = [ac for ac in covers if bool(ac[1])]
        t = Gtk.Table(n_rows=4, n_columns=(len(covers) // 4) + 1)
        t.set_col_spacings(12)
        t.set_row_spacings(12)
        added = set()
        for i, (album, cover, song) in enumerate(covers):
            if cover.name in added:
                continue
            cov = CoverImage(song=song)
            cov.get_child().set_tooltip_text(album)
            c = i % 4
            r = i // 4
            t.attach(cov,
                     c,
                     c + 1,
                     r,
                     r + 1,
                     xoptions=Gtk.AttachOptions.EXPAND,
                     yoptions=0)
            added.add(cover.name)
        box.pack_start(t, True, True, 0)
예제 #8
0
 def test_set_song(self):
     c = CoverImage()
     c.set_song(AudioFile({"~filename": "woo"}))
     event = Gdk.Event.new(Gdk.EventType.BUTTON_PRESS)
     event.type.button = 1
     c.emit("button-press-event", event)
     c.destroy()
예제 #9
0
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__()

        # play controls
        control_item = Gtk.ToolItem()
        self.insert(control_item, 0)
        t = PlayControls(player, library.librarian)
        self.volume = t.volume

        # only restore the volume in case it is managed locally, otherwise
        # this could affect the system volume
        if not player.has_external_volume:
            player.volume = config.getfloat("memory", "volume")

        self.volume.connect("value-changed", self._on_volume_changed)
        control_item.add(t)

        self.insert(Gtk.SeparatorToolItem(), 1)

        info_item = Gtk.ToolItem()
        self.insert(info_item, 2)
        info_item.set_expand(True)

        box = Gtk.Box(spacing=6)
        info_item.add(box)
        qltk.add_css(self, "GtkToolbar {padding: 3px;}")

        # song text
        info_pattern_path = os.path.join(const.USERDIR, "songinfo")
        text = SongInfo(library.librarian, player, info_pattern_path)
        box.pack_start(Align(text, border=3), True, True, 0)

        # cover image
        self.image = CoverImage(resize=True)
        connect_destroy(player, 'song-started', self.__new_song)

        # FIXME: makes testing easier
        if app.cover_manager:
            connect_destroy(app.cover_manager, 'cover-changed',
                            self.__song_art_changed, library)

        self.image.props.margin = 2
        box.pack_start(self.image, False, True, 0)

        for child in self.get_children():
            child.show_all()

        context = self.get_style_context()
        context.add_class("primary-toolbar")
예제 #10
0
    def _album(self, songs, box):
        noalbum = 0
        albums = {}
        for song in songs:
            if "album" in song:
                albums[song.list("album")[0]] = song
            else:
                noalbum += 1
        albums = [(song.get("date"), song, album) for
                  album, song in albums.items()]
        albums.sort()

        def format(args):
            date, song, album = args
            if date:
                return "%s (%s)" % (album, date[:4])
            else:
                return album

        get_cover = app.cover_manager.get_cover
        covers = [(a, get_cover(s), s) for d, s, a in albums]
        albums = map(format, albums)
        if noalbum:
            albums.append(ngettext("%d song with no album",
                "%d songs with no album", noalbum) % noalbum)
        l = Label("\n".join(albums))
        l.set_ellipsize(Pango.EllipsizeMode.END)
        box.pack_start(Frame(_("Selected Discography"), l), False, False, 0)

        covers = [ac for ac in covers if bool(ac[1])]
        t = Gtk.Table(n_rows=4, n_columns=(len(covers) // 4) + 1)
        t.set_col_spacings(12)
        t.set_row_spacings(12)
        added = set()
        for i, (album, cover, song) in enumerate(covers):
            if cover.name in added:
                continue
            cov = CoverImage(song=song)
            cov.get_child().set_tooltip_text(album)
            c = i % 4
            r = i // 4
            t.attach(cov, c, c + 1, r, r + 1,
                     xoptions=Gtk.AttachOptions.EXPAND, yoptions=0)
            added.add(cover.name)
        box.pack_start(t, True, True, 0)
예제 #11
0
 def test_set_song(self):
     c = CoverImage()
     c.set_song(AudioFile({"~filename": "woo"}))
     event = Gdk.Event.new(Gdk.EventType.BUTTON_PRESS)
     event.type.button = 1
     c.emit("button-press-event", event)
     c.destroy()
예제 #12
0
    def _album(self, songs, box):
        text = []

        discs = {}
        for song in songs:
            try:
                discs[song("~#disc")] = int(
                    song["tracknumber"].split("/")[1])
            except (AttributeError, ValueError, IndexError, KeyError):
                discs[song("~#disc")] = max([
                    song("~#track", discs.get(song("~#disc"), 0))])
        tracks = sum(discs.values())
        discs = len(discs)
        length = sum([song.get("~#length", 0) for song in songs])

        if tracks == 0 or tracks < len(songs):
            tracks = len(songs)

        parts = []
        if discs > 1:
            parts.append(
                ngettext("%d disc", "%d discs", discs) % discs)
        parts.append(
                ngettext("%d track", "%d tracks", tracks) % tracks)
        if tracks != len(songs):
            parts.append(ngettext("%d selected", "%d selected",
                len(songs)) % len(songs))

        text.append(", ".join(parts))
        text.append(util.format_time_long(length))

        if "location" in song:
            text.append(util.escape(song["location"]))
        if "organization" in song or "labelid" in song:
            t = util.escape(song.comma("~organization~labelid"))
            text.append(t)

        if "producer" in song:
            text.append(_("Produced by %s") % (
                util.escape(song.comma("producer"))))

        w = Label("")
        w.set_ellipsize(Pango.EllipsizeMode.END)
        w.set_markup("\n".join(text))
        hb = Gtk.HBox(spacing=12)

        cover = CoverImage()
        cover.set_property('no-show-all', True)
        hb.pack_start(cover, False, True, 0)

        def show_cover(cover, success):
            if success:
                cover.show()
            cover.disconnect(signal_id)
        signal_id = cover.connect('cover-visible', show_cover)
        cover.set_song(song)

        hb.pack_start(w, True, True, 0)
        box.pack_start(hb, False, False, 0)
예제 #13
0
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__()

        # play controls
        control_item = Gtk.ToolItem()
        self.insert(control_item, 0)
        t = PlayControls(player, library.librarian)
        self.volume = t.volume
        control_item.add(t)

        self.insert(Gtk.SeparatorToolItem(), 1)

        info_item = Gtk.ToolItem()
        self.insert(info_item, 2)
        info_item.set_expand(True)

        box = Gtk.Box(spacing=6)
        info_item.add(box)
        qltk.add_css(self, "GtkToolbar {padding: 3px;}")

        # song text
        text = SongInfo(library.librarian, player)
        box.pack_start(Alignment(text, border=3), True, True, 0)

        # cover image
        self.image = CoverImage(resize=True)
        gobject_weak(player.connect, 'song-started', self.__new_song,
                     parent=self)
        gobject_weak(parent.connect, 'artwork-changed',
                     self.__song_art_changed, library, parent=self)

        # CoverImage doesn't behave in a Alignment, so wrap it
        coverbox = Gtk.Box()
        coverbox.pack_start(self.image, True, True, 0)
        box.pack_start(Alignment(coverbox, border=2), False, True, 0)

        for child in self.get_children():
            child.show_all()

        context = self.get_style_context()
        context.add_class("primary-toolbar")
예제 #14
0
    def _album(self, songs, box):
        text = []

        discs = {}
        for song in songs:
            try:
                discs[song("~#disc")] = int(
                    song["tracknumber"].split("/")[1])
            except (AttributeError, ValueError, IndexError, KeyError):
                discs[song("~#disc")] = max([
                    song("~#track", discs.get(song("~#disc"), 0))])
        tracks = sum(discs.values())
        discs = len(discs)
        length = sum([song.get("~#length", 0) for song in songs])

        if tracks == 0 or tracks < len(songs):
            tracks = len(songs)

        parts = []
        if discs > 1:
            parts.append(
                ngettext("%d disc", "%d discs", discs) % discs)
        parts.append(
                ngettext("%d track", "%d tracks", tracks) % tracks)
        if tracks != len(songs):
            parts.append(ngettext("%d selected", "%d selected",
                len(songs)) % len(songs))

        text.append(", ".join(parts))
        text.append(util.format_time_preferred(length))

        if "location" in song:
            text.append(util.escape(song["location"]))
        if "organization" in song or "labelid" in song:
            t = util.escape(song.comma("~organization~labelid"))
            text.append(t)

        if "producer" in song:
            text.append(_("Produced by %s") % (
                util.escape(song.comma("producer"))))

        w = Label("")
        w.set_ellipsize(Pango.EllipsizeMode.END)
        w.set_markup("\n".join(text))
        hb = Gtk.HBox(spacing=12)

        cover = CoverImage()
        cover.set_property('no-show-all', True)
        hb.pack_start(cover, False, True, 0)

        def show_cover(cover, success):
            if success:
                cover.show()
            cover.disconnect(signal_id)
        signal_id = cover.connect('cover-visible', show_cover)
        cover.set_song(song)

        hb.pack_start(w, True, True, 0)
        box.pack_start(hb, False, False, 0)
예제 #15
0
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__()

        # play controls
        control_item = Gtk.ToolItem()
        self.insert(control_item, 0)
        t = PlayControls(player, library.librarian)
        self.volume = t.volume

        # only restore the volume in case it is managed locally, otherwise
        # this could affect the system volume
        if not player.has_external_volume:
            player.volume = config.getfloat("memory", "volume")

        self.volume.connect("value-changed", self._on_volume_changed)
        control_item.add(t)

        self.insert(Gtk.SeparatorToolItem(), 1)

        info_item = Gtk.ToolItem()
        self.insert(info_item, 2)
        info_item.set_expand(True)

        box = Gtk.Box(spacing=6)
        info_item.add(box)
        qltk.add_css(self, "GtkToolbar {padding: 3px;}")

        # song text
        info_pattern_path = os.path.join(const.USERDIR, "songinfo")
        text = SongInfo(library.librarian, player, info_pattern_path)
        box.pack_start(Align(text, border=3), True, True, 0)

        # cover image
        self.image = CoverImage(resize=True)
        connect_destroy(player, 'song-started', self.__new_song)

        # FIXME: makes testing easier
        if app.cover_manager:
            connect_destroy(
                app.cover_manager, 'cover-changed',
                self.__song_art_changed, library)

        self.image.props.margin = 2
        box.pack_start(self.image, False, True, 0)

        for child in self.get_children():
            child.show_all()

        context = self.get_style_context()
        context.add_class("primary-toolbar")
예제 #16
0
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__(spacing=3)

        # play controls
        t = PlayControls(player, library.librarian)
        self.volume = t.volume
        self.pack_start(t, expand=False, fill=False)

        # song text
        text = SongInfo(library.librarian, player)
        self.pack_start(Alignment(text, border=3))

        # cover image
        self.image = CoverImage(resize=True)
        player.connect('song-started', lambda x, s: self.image.set_song(s))
        parent.connect('artwork-changed', self.__song_art_changed, library)
        self.pack_start(self.image, expand=False)
예제 #17
0
    def __init__(self, parent, player, library):
        super(TopBar, self).__init__()

        # play controls
        control_item = Gtk.ToolItem()
        self.insert(control_item, 0)
        t = PlayControls(player, library.librarian)
        self.volume = t.volume
        control_item.add(t)

        self.insert(Gtk.SeparatorToolItem(), 1)

        info_item = Gtk.ToolItem()
        self.insert(info_item, 2)
        info_item.set_expand(True)

        box = Gtk.Box(spacing=6)
        info_item.add(box)
        qltk.add_css(self, "GtkToolbar {padding: 3px;}")

        # song text
        info_pattern_path = os.path.join(const.USERDIR, "songinfo")
        text = SongInfo(library.librarian, player, info_pattern_path)
        box.pack_start(Alignment(text, border=3), True, True, 0)

        # cover image
        self.image = CoverImage(resize=True)
        connect_destroy(player, 'song-started', self.__new_song)

        # FIXME: makes testing easier
        if app.cover_manager:
            connect_destroy(
                app.cover_manager, 'cover-changed',
                self.__song_art_changed, library)

        # CoverImage doesn't behave in a Alignment, so wrap it
        coverbox = Gtk.Box()
        coverbox.pack_start(self.image, True, True, 0)
        box.pack_start(Alignment(coverbox, border=2), False, True, 0)

        for child in self.get_children():
            child.show_all()

        context = self.get_style_context()
        context.add_class("primary-toolbar")
예제 #18
0
    def _album(self, song, box):
        if "album" not in song:
            return
        w = Label("")
        text = []
        text.append("<i>%s</i>" % util.escape(song.comma("album")))
        if "date" in song:
            text[-1] += " (%s)" % util.escape(song.comma("date"))
        secondary = []
        if "discnumber" in song:
            secondary.append(_("Disc %s") % song["discnumber"])
        if "discsubtitle" in song:
            secondary.append("<i>%s</i>" %
                             util.escape(song.comma("discsubtitle")))
        if "tracknumber" in song:
            secondary.append(_("Track %s") % song["tracknumber"])
        if secondary:
            text.append(" - ".join(secondary))

        if "organization" in song or "labelid" in song:
            t = util.escape(song.comma("~organization~labelid"))
            text.append(t)

        if "producer" in song:
            text.append("Produced by %s" %
                        (util.escape(song.comma("producer"))))

        w.set_markup("\n".join(text))
        w.set_ellipsize(Pango.EllipsizeMode.END)
        hb = Gtk.HBox(spacing=12)

        cover = CoverImage()
        cover.set_property('no-show-all', True)
        hb.pack_start(cover, False, True, 0)

        def show_cover(cover, success):
            if success:
                cover.show()
            cover.disconnect(signal_id)

        signal_id = cover.connect('cover-visible', show_cover)
        cover.set_song(song)

        hb.pack_start(w, True, True, 0)
        box.pack_start(Frame(tag("album"), hb), False, False, 0)
예제 #19
0
    def _album(self, song, box):
        if "album" not in song:
            return
        w = Label("")
        text = []
        text.append("<i>%s</i>" % util.escape(song.comma("album")))
        if "date" in song:
            text[-1] += " (%s)" % util.escape(song.comma("date"))
        secondary = []
        if "discnumber" in song:
            secondary.append(_("Disc %s") % song["discnumber"])
        if "discsubtitle" in song:
            secondary.append("<i>%s</i>" %
                             util.escape(song.comma("discsubtitle")))
        if "tracknumber" in song:
            secondary.append(_("Track %s") % song["tracknumber"])
        if secondary:
            text.append(" - ".join(secondary))

        if "organization" in song or "labelid" in song:
            t = util.escape(song.comma("~organization~labelid"))
            text.append(t)

        if "producer" in song:
            text.append("Produced by %s" % (
                util.escape(song.comma("producer"))))

        w.set_markup("\n".join(text))
        w.set_ellipsize(Pango.EllipsizeMode.END)
        hb = Gtk.HBox(spacing=12)

        cover = CoverImage()
        cover.set_property('no-show-all', True)
        hb.pack_start(cover, False, True, 0)

        def show_cover(cover, success):
            if success:
                cover.show()
            cover.disconnect(signal_id)
        signal_id = cover.connect('cover-visible', show_cover)
        cover.set_song(song)

        hb.pack_start(w, True, True, 0)
        box.pack_start(Frame(tag("album"), hb), False, False, 0)