Again the hack is too great to mention.. but the amount of covers are now limited.
authorKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Sat, 2 Jan 2010 04:16:03 +0000 (05:16 +0100)
committerKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Sat, 2 Jan 2010 23:37:45 +0000 (00:37 +0100)
jamaendo/api.py
jamaui/__init__.py
jamaui/albumlist.py
jamaui/player.py
jamaui/playerwindow.py
jamaui/radios.py
jamaui/showalbum.py

index 4c94cac..cd3d454 100644 (file)
@@ -28,7 +28,8 @@
 
 # An improved, structured jamendo API wrapper for the N900 with cacheing
 # Image / cover downloads.. and more?
 
 # An improved, structured jamendo API wrapper for the N900 with cacheing
 # Image / cover downloads.. and more?
-import urllib, threading, os, gzip, time, simplejson, re
+import urllib, threading, os, time, simplejson, re
+import logging
 
 _CACHEDIR = None
 _COVERDIR = None
 
 _CACHEDIR = None
 _COVERDIR = None
@@ -37,21 +38,15 @@ _MP3URL = _GET2+'stream/track/redirect/?id=%d&streamencoding=mp31'
 _OGGURL = _GET2+'stream/track/redirect/?id=%d&streamencoding=ogg2'
 _TORRENTURL = _GET2+'bittorrent/file/redirect/?album_id=%d&type=archive&class=mp32'
 
 _OGGURL = _GET2+'stream/track/redirect/?id=%d&streamencoding=ogg2'
 _TORRENTURL = _GET2+'bittorrent/file/redirect/?album_id=%d&type=archive&class=mp32'
 
-def set_cache_dir(cachedir):
-    global _CACHEDIR
-    global _COVERDIR
-    _CACHEDIR = cachedir
-    _COVERDIR = os.path.join(_CACHEDIR, 'covers')
-
-    try:
-        os.makedirs(_CACHEDIR)
-    except OSError:
-        pass
-
-    try:
-        os.makedirs(_COVERDIR)
-    except OSError:
-        pass
+try:
+    log = logging.getLogger(__name__)
+except:
+    class StdoutLogger(object):
+        def info(self, s, *args):
+            print s % (args)
+        def debug(self, s, *args):
+            pass#print s % (args)
+    log = StdoutLogger()
 
 # These classes can be partially constructed,
 # and if asked for a property they don't know,
 
 # These classes can be partially constructed,
 # and if asked for a property they don't know,
@@ -104,7 +99,7 @@ class LazyQuery(object):
     def __repr__(self):
         try:
             return u"%s(%s)"%(self.__class__.__name__,
     def __repr__(self):
         try:
             return u"%s(%s)"%(self.__class__.__name__,
-                              u", ".join(repr(v) for k,v in self.__dict__.iteritems() if not k.startswith('_')))
+                              u", ".join(("%s:%s"%(k,repr(v))) for k,v in self.__dict__.iteritems() if not k.startswith('_')))
         except UnicodeEncodeError:
             #import traceback
             #traceback.print_exc()
         except UnicodeEncodeError:
             #import traceback
             #traceback.print_exc()
@@ -200,6 +195,8 @@ _CACHED_ARTISTS = 100
 _CACHED_ALBUMS = 200
 _CACHED_TRACKS = 500
 _CACHED_RADIOS = 10
 _CACHED_ALBUMS = 200
 _CACHED_TRACKS = 500
 _CACHED_RADIOS = 10
+# cache sizes, persistant
+_CACHED_COVERS = 2048
 
 # TODO: cache queries?
 
 
 # TODO: cache queries?
 
@@ -218,7 +215,7 @@ class Query(object):
         pass
 
     def _geturl(self, url):
         pass
 
     def _geturl(self, url):
-        print "*** %s" % (url)
+        log.info("%s", url)
         Query._ratelimit()
         try:
             f = urllib.urlopen(url)
         Query._ratelimit()
         try:
             f = urllib.urlopen(url)
@@ -234,8 +231,6 @@ class Query(object):
     def execute(self):
         raise NotImplemented
 
     def execute(self):
         raise NotImplemented
 
-import threading
-
 class CoverFetcher(threading.Thread):
     def __init__(self):
         threading.Thread.__init__(self)
 class CoverFetcher(threading.Thread):
     def __init__(self):
         threading.Thread.__init__(self)
@@ -289,25 +284,46 @@ class CoverCache(object):
     """
     def __init__(self):
         self._covers = {} # (albumid, size) -> file
     """
     def __init__(self):
         self._covers = {} # (albumid, size) -> file
-        coverdir = _COVERDIR if _COVERDIR else '/tmp'
-        if os.path.isdir(coverdir):
-            covermatch = re.compile(r'(\d+)\-(\d+)\.jpg')
-            for fil in os.listdir(coverdir):
-                fl = os.path.join(coverdir, fil)
-                m = covermatch.match(fil)
-                if m and os.path.isfile(fl):
-                    self._covers[(int(m.group(1)), int(m.group(2)))] = fl
         self._fetcher = CoverFetcher()
         self._fetcher.start()
         self._fetcher = CoverFetcher()
         self._fetcher.start()
+        if _COVERDIR and os.path.isdir(_COVERDIR):
+            self.prime_cache()
+
+    def prime_cache(self):
+        coverdir = _COVERDIR
+        covermatch = re.compile(r'(\d+)\-(\d+)\.jpg')
+
+        prev_covers = os.listdir(coverdir)
+
+        if len(prev_covers) > _CACHED_COVERS:
+            import random
+            dropn = len(prev_covers) - _CACHED_COVERS
+            todrop = random.sample(prev_covers, dropn)
+            log.warning("Deleting from cache: %s", todrop)
+            for d in todrop:
+                m = covermatch.match(d)
+                if m:
+                    try:
+                        os.unlink(os.path.join(coverdir, d))
+                    except OSError, e:
+                        log.exception('unlinking failed')
+
+        for fil in os.listdir(coverdir):
+            fl = os.path.join(coverdir, fil)
+            m = covermatch.match(fil)
+            if m and os.path.isfile(fl):
+                self._covers[(int(m.group(1)), int(m.group(2)))] = fl
 
     def fetch_cover(self, albumid, size):
 
     def fetch_cover(self, albumid, size):
-        coverdir = _COVERDIR if _COVERDIR else '/tmp'
-        to = os.path.join(coverdir, '%d-%d.jpg'%(albumid, size))
-        if not os.path.isfile(to):
-            url = _GET2+'image/album/redirect/?id=%d&imagesize=%d'%(albumid, size)
-            urllib.urlretrieve(url, to)
-            self._covers[(albumid, size)] = to
-        return to
+        coverdir = _COVERDIR
+        if coverdir:
+            to = os.path.join(coverdir, '%d-%d.jpg'%(albumid, size))
+            if not os.path.isfile(to):
+                url = _GET2+'image/album/redirect/?id=%d&imagesize=%d'%(albumid, size)
+                urllib.urlretrieve(url, to)
+                self._covers[(albumid, size)] = to
+            return to
+        return None
 
     def get_cover(self, albumid, size):
         cover = self._covers.get((albumid, size), None)
 
     def get_cover(self, albumid, size):
         cover = self._covers.get((albumid, size), None)
@@ -324,6 +340,24 @@ class CoverCache(object):
 
 _cover_cache = CoverCache()
 
 
 _cover_cache = CoverCache()
 
+def set_cache_dir(cachedir):
+    global _CACHEDIR
+    global _COVERDIR
+    _CACHEDIR = cachedir
+    _COVERDIR = os.path.join(_CACHEDIR, 'covers')
+
+    try:
+        os.makedirs(_CACHEDIR)
+    except OSError:
+        pass
+
+    try:
+        os.makedirs(_COVERDIR)
+    except OSError:
+        pass
+
+    _cover_cache.prime_cache()
+
 def get_album_cover(albumid, size=100):
     return _cover_cache.get_cover(albumid, size)
 
 def get_album_cover(albumid, size=100):
     return _cover_cache.get_cover(albumid, size)
 
@@ -393,11 +427,7 @@ class GetQuery(Query):
             'params' : 'user_idstr=%s',
             'constructor' : [Album]
             },
             'params' : 'user_idstr=%s',
             'constructor' : [Album]
             },
-    #http://api.jamendo.com/get2/id+name+url+image+artist_name/album/jsonpretty/album_user_starred/?user_idstr=sylvinus&n=all
-    #q = SearchQuery('album', user_idstr=user)
-
         }
         }
-#http://api.jamendo.com/get2/id+name+image+artist_name+album_name+album_id+numalbum+duration/track/json/radio_track_inradioplaylist+track_album+album_artist/?order=numradio_asc&radio_id=283
 
     def __init__(self, what, ID):
         Query.__init__(self)
 
     def __init__(self, what, ID):
         Query.__init__(self)
@@ -471,6 +501,14 @@ def _update_cache(cache, new_items):
         elif isinstance(item, Album) and item.tracks:
             for track in item.tracks:
                 _update_cache(_tracks, track)
         elif isinstance(item, Album) and item.tracks:
             for track in item.tracks:
                 _update_cache(_tracks, track)
+    # enforce cache limits here!
+    # also, TODO: save/load cache between sessions
+    # that will require storing a timestamp with
+    # each item, though..
+    # perhaps,
+    # artists: 1 day - changes often
+    # albums: 2-5 days - changes less often (?)
+    # tracks: 1 week - changes rarely, queried often
 
 def get_artist(artist_id):
     """Returns: Artist"""
 
 def get_artist(artist_id):
     """Returns: Artist"""
index 505bc11..fd73b72 100644 (file)
@@ -26,8 +26,8 @@ import logging
 import sys
 
 LOG_FILENAME = '/tmp/jamaendo.log'
 import sys
 
 LOG_FILENAME = '/tmp/jamaendo.log'
-LOG_LEVEL = logging.DEBUG
-
-# 
-logging.basicConfig(filename=LOG_FILENAME, level=LOG_LEVEL, format="%(name)-15s: [%(lineno)4d] %(levelname)-8s %(message)s")
+LOG_LEVEL = logging.INFO
+#LOG_FORMAT = "%(asctime)s %(name)-19s %(levelname)-5s - %(message)s"
+LOG_FORMAT = "%(asctime)s %(name)-10s: [%(lineno)4d] %(levelname)-5s %(message)s"
+logging.basicConfig(filename=LOG_FILENAME, level=LOG_LEVEL, format=LOG_FORMAT)
 
 
index ce1671a..5c82637 100644 (file)
@@ -7,6 +7,12 @@ import logging
 
 log = logging.getLogger(__name__)
 
 
 log = logging.getLogger(__name__)
 
+class ImageDownloader(object):
+    """
+    TODO: background downloader of images
+    for album lists, track lists, etc
+    """
+
 class AlbumList(gtk.TreeView):
     def __init__(self):
         gtk.TreeView.__init__(self)
 class AlbumList(gtk.TreeView):
     def __init__(self):
         gtk.TreeView.__init__(self)
@@ -89,8 +95,8 @@ class RadioList(gtk.TreeView):
 
     def get_radio_id(self, path):
         treeiter = self.__store.get_iter(path)
 
     def get_radio_id(self, path):
         treeiter = self.__store.get_iter(path)
-        _, _id = self.__store.get(treeiter, 0, 1)
-        return _id
+        name, _id = self.__store.get(treeiter, 0, 1)
+        return name, _id
 
     def radio_name(self, radio):
         if radio.idstr:
 
     def radio_name(self, radio):
         if radio.idstr:
index 65cc549..dd51a92 100644 (file)
@@ -122,7 +122,6 @@ class GStreamer(_Player):
 
     def play(self):
         if self.player:
 
     def play(self):
         if self.player:
-            log.debug("playing")
             self.player.set_state(gst.STATE_PLAYING)
 
     def pause(self):
             self.player.set_state(gst.STATE_PLAYING)
 
     def pause(self):
@@ -183,17 +182,18 @@ class GStreamer(_Player):
         # Sets the right property depending on the platform of self.filesrc
         if self.player is not None:
             self.filesrc.set_property(self.filesrc_property, uri)
         # Sets the right property depending on the platform of self.filesrc
         if self.player is not None:
             self.filesrc.set_property(self.filesrc_property, uri)
+            log.info("%s", uri)
 
     def _on_message(self, bus, message):
         t = message.type
 
         if t == gst.MESSAGE_EOS:
 
     def _on_message(self, bus, message):
         t = message.type
 
         if t == gst.MESSAGE_EOS:
-            log.info("End of stream")
+            log.debug("Gstreamer: End of stream")
             self.eos_callback()
         elif t == gst.MESSAGE_STATE_CHANGED:
             if (message.src == self.player and
                 message.structure['new-state'] == gst.STATE_PLAYING):
             self.eos_callback()
         elif t == gst.MESSAGE_STATE_CHANGED:
             if (message.src == self.player and
                 message.structure['new-state'] == gst.STATE_PLAYING):
-                log.info("State changed to playing")
+                log.debug("gstreamer: state -> playing")
         elif t == gst.MESSAGE_ERROR:
             err, debug = message.parse_error()
             log.critical( 'Error: %s %s', err, debug )
         elif t == gst.MESSAGE_ERROR:
             err, debug = message.parse_error()
             log.critical( 'Error: %s %s', err, debug )
@@ -289,6 +289,9 @@ PlayerBackend = GStreamer
 
 class Playlist(object):
     def __init__(self, items = []):
 
 class Playlist(object):
     def __init__(self, items = []):
+        self.radio_mode = False
+        self.radio_id = None
+        self.radio_name = None
         if items is None:
             items = []
         for item in items:
         if items is None:
             items = []
         for item in items:
@@ -327,6 +330,11 @@ class Playlist(object):
             return self.items[self._current]
         return None
 
             return self.items[self._current]
         return None
 
+    def jump_to(self, item_id):
+        for c, i in enumerate(self.items):
+            if i.ID == item_id:
+                self._current = c
+
     def current_index(self):
         return self._current
 
     def current_index(self):
         return self._current
 
@@ -334,13 +342,14 @@ class Playlist(object):
         return len(self.items)
 
     def __repr__(self):
         return len(self.items)
 
     def __repr__(self):
-        return "Playlist(%s)"%(", ".join([str(item.ID) for item in self.items]))
+        return "Playlist(%d of %s)"%(self._current, ", ".join([str(item.ID) for item in self.items]))
 
 class Player(object):
     def __init__(self):
         self.backend = PlayerBackend()
         self.backend.set_eos_callback(self._on_eos)
         self.playlist = Playlist()
 
 class Player(object):
     def __init__(self):
         self.backend = PlayerBackend()
         self.backend.set_eos_callback(self._on_eos)
         self.playlist = Playlist()
+        self.__in_end_notify = False # ugly...
 
     def get_position_duration(self):
         return self.backend.get_position_duration()
 
     def get_position_duration(self):
         return self.backend.get_position_duration()
@@ -379,8 +388,15 @@ class Player(object):
             self.backend.play_url('mp3', entry.mp3_url())
             log.debug("playing %s", entry)
             postoffice.notify('next', entry)
             self.backend.play_url('mp3', entry.mp3_url())
             log.debug("playing %s", entry)
             postoffice.notify('next', entry)
-        else:
-            self.stop()
+        elif not self.__in_end_notify:
+            self.__in_end_notify = True
+            postoffice.notify('playlist-end', self.playlist)
+            self.__in_end_notify = False
+            # if the notification refills the playlist,
+            # we do nothing after this point so we don't
+            # mess things up
+            if not self.playlist.has_next():
+                self.stop()
 
     def prev(self):
         if self.playlist.has_prev():
 
     def prev(self):
         if self.playlist.has_prev():
@@ -391,7 +407,6 @@ class Player(object):
             postoffice.notify('prev', entry)
 
     def _on_eos(self):
             postoffice.notify('prev', entry)
 
     def _on_eos(self):
-        log.debug("EOS!")
         self.next()
 
 the_player = Player() # the player instance
         self.next()
 
 the_player = Player() # the player instance
index ce13494..2c441ec 100644 (file)
@@ -26,6 +26,7 @@ import gobject
 import hildon
 import util
 import pango
 import hildon
 import util
 import pango
+import jamaendo
 from settings import settings
 from postoffice import postoffice
 from player import Playlist, the_player
 from settings import settings
 from postoffice import postoffice
 from player import Playlist, the_player
@@ -60,10 +61,10 @@ class PlayerWindow(hildon.StackableWindow):
         self.playlist_pos = gtk.Label()
         self.playlist_pos.set_alignment(1.0,0)
         self.track = gtk.Label()
         self.playlist_pos = gtk.Label()
         self.playlist_pos.set_alignment(1.0,0)
         self.track = gtk.Label()
-        self.track.set_alignment(0,0)
+        self.track.set_alignment(0,1)
         self.track.set_ellipsize(pango.ELLIPSIZE_END)
         self.artist = gtk.Label()
         self.track.set_ellipsize(pango.ELLIPSIZE_END)
         self.artist = gtk.Label()
-        self.artist.set_alignment(0,0)
+        self.artist.set_alignment(0,0.5)
         self.artist.set_ellipsize(pango.ELLIPSIZE_END)
         self.album = gtk.Label()
         self.album.set_alignment(0,0)
         self.artist.set_ellipsize(pango.ELLIPSIZE_END)
         self.album = gtk.Label()
         self.album.set_alignment(0,0)
@@ -104,6 +105,7 @@ class PlayerWindow(hildon.StackableWindow):
         self.add(vbox)
 
         postoffice.connect('album-cover', self, self.set_album_cover)
         self.add(vbox)
 
         postoffice.connect('album-cover', self, self.set_album_cover)
+        postoffice.connect('playlist-end', self, self.on_playlist_end)
         postoffice.connect(['next', 'prev', 'play', 'pause', 'stop'], self, self.on_state_changed)
 
         #print "Created player window, playlist: %s" % (self.playlist)
         postoffice.connect(['next', 'prev', 'play', 'pause', 'stop'], self, self.on_state_changed)
 
         #print "Created player window, playlist: %s" % (self.playlist)
@@ -164,7 +166,7 @@ class PlayerWindow(hildon.StackableWindow):
 
     def on_destroy(self, wnd):
         self.stop_position_timer()
 
     def on_destroy(self, wnd):
         self.stop_position_timer()
-        postoffice.disconnect(['album-cover', 'next', 'prev', 'play', 'stop'], self)
+        postoffice.disconnect(['album-cover', 'playlist-end', 'next', 'prev', 'play', 'stop'], self)
 
     def add_stock_button(self, btns, stock, cb):
         btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
 
     def add_stock_button(self, btns, stock, cb):
         btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
@@ -199,7 +201,11 @@ class PlayerWindow(hildon.StackableWindow):
             self.playbtn.set_data('state', 'play')
 
     def set_labels(self, track, artist, album, playlist_pos, playlist_size):
             self.playbtn.set_data('state', 'play')
 
     def set_labels(self, track, artist, album, playlist_pos, playlist_size):
-        self.playlist_pos.set_markup('<span size="small">Track %s of %s</span>'%(int(playlist_pos)+1, playlist_size))
+        if self.playlist.radio_mode:
+            ppstr = '<span size="small">Radio: %s</span>'%(cgi.escape(self.playlist.radio_name))
+        else:
+            ppstr = '<span size="small">Track %s of %s</span>'%(int(playlist_pos)+1, playlist_size)
+        self.playlist_pos.set_markup(ppstr)
         self.track.set_markup('<span size="x-large">%s</span>'%(cgi.escape(track)))
         self.artist.set_markup('<span size="large">%s</span>'%(cgi.escape(artist)))
         self.album.set_markup('<span foreground="#aaaaaa">%s</span>'%(cgi.escape(album)))
         self.track.set_markup('<span size="x-large">%s</span>'%(cgi.escape(track)))
         self.artist.set_markup('<span size="large">%s</span>'%(cgi.escape(artist)))
         self.album.set_markup('<span foreground="#aaaaaa">%s</span>'%(cgi.escape(album)))
@@ -258,9 +264,37 @@ class PlayerWindow(hildon.StackableWindow):
             if playing and albumid and (int(playing) == int(albumid)):
                 self.cover.set_from_file(cover)
 
             if playing and albumid and (int(playing) == int(albumid)):
                 self.cover.set_from_file(cover)
 
+    def play_radio(self, radio_name, radio_id):
+        playlist = Playlist()
+        playlist.radio_mode = True
+        playlist.radio_name = radio_name
+        playlist.radio_id = radio_id
+        log.debug("Playing radio: %s", playlist)
+        self.refill_radio(playlist)
+
+    def refill_radio(self, playlist):
+        if playlist.radio_mode:
+            playlist.add(jamaendo.get_radio_tracks(playlist.radio_id))
+            log.debug("Refilling radio %s", playlist)
+            self.player.playlist = playlist
+            self.playlist = playlist
+            self.player.next()
+            log.debug("Playlist current: %s, playing? %s", playlist.current_index(),
+                      self.player.playing())
+
+    def on_playlist_end(self, playlist):
+        if playlist.radio_mode:
+            self.refill_radio(playlist)
+
     def play_tracks(self, tracks):
     def play_tracks(self, tracks):
+        self.__play_tracks(tracks)
+
+    def __play_tracks(self, tracks):
         self.clear_position()
         self.clear_position()
-        self.playlist = Playlist(tracks)
+        if isinstance(tracks, Playlist):
+            self.playlist = tracks
+        else:
+            self.playlist = Playlist(tracks)
         self.player.stop()
         self.player.play(self.playlist)
 
         self.player.stop()
         self.player.play(self.playlist)
 
index 2068b82..dc3109c 100644 (file)
@@ -59,14 +59,6 @@ class RadiosWindow(hildon.StackableWindow):
         return button
 
     def row_activated(self, treeview, path, view_column):
         return button
 
     def row_activated(self, treeview, path, view_column):
-        _id = self.radiolist.get_radio_id(path)
-        item = self.radios[_id]
-        self.open_item(item)
-
-    def open_item(self, item):
-        hildon.hildon_gtk_window_set_progress_indicator(self, 1)
-        tracks = jamaendo.get_radio_tracks(item.ID)
-        hildon.hildon_gtk_window_set_progress_indicator(self, 0)
-        if tracks:
-            wnd = open_playerwindow()
-            wnd.play_tracks(tracks)
+        name, _id = self.radiolist.get_radio_id(path)
+        wnd = open_playerwindow()
+        wnd.play_radio(name, _id)
index 4325622..688022d 100644 (file)
@@ -25,6 +25,7 @@ import gtk
 import cgi
 import hildon
 import jamaendo
 import cgi
 import hildon
 import jamaendo
+from player import Playlist
 from playerwindow import open_playerwindow
 from settings import settings
 from postoffice import postoffice
 from playerwindow import open_playerwindow
 from settings import settings
 from postoffice import postoffice
@@ -55,10 +56,6 @@ class ShowAlbum(hildon.StackableWindow):
         self.download = self.make_imagebutton('download', self.on_download)
         self.favorite = self.make_imagebutton('favorite', self.on_favorite)
         self.license = self.make_imagebutton('license', self.on_license)
         self.download = self.make_imagebutton('download', self.on_download)
         self.favorite = self.make_imagebutton('favorite', self.on_favorite)
         self.license = self.make_imagebutton('license', self.on_license)
-        self.playbtn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
-        self.playbtn.set_label("Play album")
-        self.playbtn.set_border_width(0)
-        self.playbtn.connect('clicked', self.on_play)
 
         vbox2 = gtk.VBox()
         self.albumname = gtk.Label()
 
         vbox2 = gtk.VBox()
         self.albumname = gtk.Label()
@@ -68,13 +65,13 @@ class ShowAlbum(hildon.StackableWindow):
         self.tracks = TrackList(numbers=True)
         self.tracks.connect('row-activated', self.row_activated)
 
         self.tracks = TrackList(numbers=True)
         self.tracks.connect('row-activated', self.row_activated)
 
-        for track in jamaendo.get_tracks(album.ID):
+        self.tracklist = jamaendo.get_tracks(album.ID)
+        for track in self.tracklist:
             self.tracks.add_track(track)
 
         top_hbox.pack_start(vbox1, False)
         top_hbox.pack_start(vbox2, True)
         vbox1.pack_start(self.cover, True)
             self.tracks.add_track(track)
 
         top_hbox.pack_start(vbox1, False)
         top_hbox.pack_start(vbox2, True)
         vbox1.pack_start(self.cover, True)
-        vbox1.pack_start(self.playbtn, False)
         vbox1.pack_start(self.bbox, False)
         self.bbox.add(self.goto_artist)
         self.bbox.add(self.download)
         vbox1.pack_start(self.bbox, False)
         self.bbox.add(self.goto_artist)
         self.bbox.add(self.download)
@@ -149,8 +146,10 @@ class ShowAlbum(hildon.StackableWindow):
 
     def row_activated(self, treeview, path, view_column):
         _id = self.tracks.get_track_id(path)
 
     def row_activated(self, treeview, path, view_column):
         _id = self.tracks.get_track_id(path)
-        track = jamaendo.get_track(_id)
-        self.open_item(track)
+        playlist = Playlist(self.tracklist)
+        playlist.jump_to(_id)
+        wnd = open_playerwindow()
+        wnd.play_tracks(playlist)
 
     def open_item(self, item):
         if isinstance(item, jamaendo.Album):
 
     def open_item(self, item):
         if isinstance(item, jamaendo.Album):