Download links
authorKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Fri, 1 Jan 2010 16:30:14 +0000 (17:30 +0100)
committerKristoffer Grönlund <kristoffer.gronlund@purplescout.se>
Fri, 1 Jan 2010 16:30:22 +0000 (17:30 +0100)
jamaendo/api.py
jamaui/albumlist.py [new file with mode: 0644]
jamaui/favorites.py
jamaui/player.py
jamaui/playerwindow.py
jamaui/postoffice.py
jamaui/search.py
jamaui/settings.py
jamaui/showalbum.py
jamaui/showartist.py
jamaui/ui.py

index d026770..74c96f7 100644 (file)
@@ -35,7 +35,7 @@ _COVERDIR = None
 _GET2 = '''http://api.jamendo.com/get2/'''
 _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'
 
 def set_cache_dir(cachedir):
     global _CACHEDIR
@@ -137,6 +137,10 @@ class Album(LazyQuery):
         if json:
             self.set_from_json(json)
 
+    def torrent_url(self):
+        return _TORRENTURL%(self.ID)
+
+
     def _needs_load(self):
         return self._needs_load_impl('name', 'image', 'artist_name', 'artist_id', 'license_url', 'tracks')
 
@@ -261,9 +265,12 @@ class CoverFetcher(threading.Thread):
                 self.cond.wait()
             self.cond.release()
 
+            multi = len(work) > 1
             for albumid, size, cb in work:
                 cover = self._fetch_cover(albumid, size)
                 cb(albumid, size, cover)
+                if multi:
+                    time.sleep(1.0)
 
 class CoverCache(object):
     """
@@ -303,16 +310,16 @@ class CoverCache(object):
     def get_async(self, albumid, size, cb):
         cover = self._covers.get((albumid, size), None)
         if cover:
-            cb(cover)
+            cb(albumid, size, cover)
         else:
             self._fetcher.request_cover(albumid, size, cb)
 
 _cover_cache = CoverCache()
 
-def get_album_cover(albumid, size=200):
+def get_album_cover(albumid, size=100):
     return _cover_cache.get_cover(albumid, size)
 
-def get_album_cover_async(cb, albumid, size=200):
+def get_album_cover_async(cb, albumid, size=100):
     _cover_cache.get_async(albumid, size, cb)
 
 class CustomQuery(Query):
diff --git a/jamaui/albumlist.py b/jamaui/albumlist.py
new file mode 100644 (file)
index 0000000..e851cdb
--- /dev/null
@@ -0,0 +1,70 @@
+import gtk
+import hildon
+import jamaendo
+from settings import settings
+from postoffice import postoffice
+import logging
+
+log = logging.getLogger(__name__)
+
+class AlbumList(gtk.TreeView):
+    def __init__(self):
+        gtk.TreeView.__init__(self)
+        self.__store = gtk.ListStore(str, int)
+        self.set_model(self.__store)
+
+        col = gtk.TreeViewColumn('Name')
+        self.append_column(col)
+        cell = gtk.CellRendererText()
+        col.pack_start(cell, True)
+        col.add_attribute(cell, 'text', 0)
+        self.set_search_column(0)
+        col.set_sort_column_id(0)
+
+        self.__show_artist = True
+
+    def add_album(self, album):
+        if self.__show_artist:
+            txt = "%s - %s" % (album.artist_name, album.name)
+        else:
+            txt = album.name
+        self.__store.append([txt, album.ID])
+
+    def get_album_id(self, path):
+        treeiter = self.__store.get_iter(path)
+        _, _id = self.__store.get(treeiter, 0, 1)
+        return _id
+
+    def show_artist(self, show):
+        self.__show_artist = show
+
+class TrackList(gtk.TreeView):
+    def __init__(self, numbers = True):
+        gtk.TreeView.__init__(self)
+        self.track_numbers = numbers
+        self.__store = gtk.ListStore(int, str, int)
+        self.set_model(self.__store)
+
+        if numbers:
+            col0 = gtk.TreeViewColumn('Num')
+            self.append_column(col0)
+            cell0 = gtk.CellRendererText()
+            col0.pack_start(cell0, True)
+            col0.add_attribute(cell0, 'text', 0)
+
+        col = gtk.TreeViewColumn('Name')
+        self.append_column(col)
+        cell = gtk.CellRendererText()
+        col.pack_start(cell, True)
+        col.add_attribute(cell, 'text', 1)
+
+        self.set_search_column(1 if numbers else 0)
+        col.set_sort_column_id(0)
+
+    def add_track(self, track):
+        self.__store.append([track.numalbum, track.name, track.ID])
+
+    def get_track_id(self, path):
+        treeiter = self.__store.get_iter(path)
+        _, _id = self.__store.get(treeiter, 0, 1)
+        return _id
index 9c37393..d607a8b 100644 (file)
@@ -30,6 +30,8 @@ from showalbum import ShowAlbum
 from settings import settings
 import logging
 
+from albumlist import AlbumList
+
 log = logging.getLogger(__name__)
 
 def _alist(l, match):
@@ -46,43 +48,33 @@ class FavoritesWindow(hildon.StackableWindow):
         if settings.user:
             # Results list
             self.panarea = hildon.PannableArea()
-            self.result_store = gtk.ListStore(str, int)
-            #self.result_store.append(['red'])
-            self.result_view = gtk.TreeView(self.result_store)
-            col = gtk.TreeViewColumn('Name')
-            self.result_view.append_column(col)
-            cell = gtk.CellRendererText()
-            col.pack_start(cell, True)
-            col.add_attribute(cell, 'text', 0)
-            self.result_view.set_search_column(0)
-            col.set_sort_column_id(0)
-            self.result_view.connect('row-activated', self.row_activated)
-
-            self.panarea.add(self.result_view)
+            self.results = AlbumList()
+            self.results.connect('row-activated', self.row_activated)
+            self.panarea.add(self.results)
 
             self.idmap = {}
+
+            def add_album(ID, album_factory):
+                if ID not in self.idmap:
+                    album = album_factory()
+                    self.idmap[ID] = album
+                    self.results.add_album(album)
+
             try:
                 for item in jamaendo.favorite_albums(settings.user):
-                    self.idmap[item.ID] = item
-                    self.result_store.append([self.get_item_text(item), item.ID])
+                    add_album(item.ID, lambda: item)
             except jamaendo.JamendoAPIException, e:
                 msg = "Query failed, is the user name '%s' correct?" % (settings.user)
                 banner = hildon.hildon_banner_show_information(self, '',
                                                                msg)
                 banner.set_timeout(3000)
 
-
-            def add_album(albumid):
-                album = jamaendo.get_album(albumid)
-                self.idmap[albumid] = album
-                self.result_store.append([self.get_item_text(album), albumid])
-
             for item in settings.favorites:
                 try:
                     if isinstance(item, tuple) and len(item) == 2:
                         ftype, fid = item
                         if ftype == 'album':
-                            add_album(fid)
+                            add_album(fid, lambda: jamaendo.get_album(fid))
 
                 except jamaendo.JamendoAPIException, e:
                     log.exception("jamaendo.get_album(%s)"%(fid))
@@ -123,8 +115,7 @@ enter your username</span>
         return button
 
     def row_activated(self, treeview, path, view_column):
-        treeiter = self.result_store.get_iter(path)
-        title, _id = self.result_store.get(treeiter, 0, 1)
+        _id = self.results.get_album_id(path)
         item = self.idmap[_id]
         print _id, item
         self.open_item(item)
index d860ccb..ec3c75b 100644 (file)
@@ -27,6 +27,7 @@ import dbus
 
 import jamaendo
 from settings import settings
+from postoffice import postoffice
 
 log = logging.getLogger(__name__)
 
@@ -65,6 +66,13 @@ class GStreamer(_Player):
         self.volume_multiplier = 1.
         self.volume_property = None
         self.eos_callback = lambda: self.stop()
+        postoffice.connect('settings-changed', self.on_settings_changed)
+
+    def on_settings_changed(self, key, value):
+        if key == 'volume':
+            self._set_volume_level(value)
+        #postoffice.disconnect(self.on_settings_changed)
+
 
     def play_url(self, filetype, uri):
         if None in (filetype, uri):
index 65fe075..57ea13f 100644 (file)
 #
 import gtk
 import hildon
-import jamaendo
 from settings import settings
+from postoffice import postoffice
 from player import Playlist, the_player
 
-_player_window = None
-
-def album_cover_receiver(albumid, size, cover):
-    if _player_window:
-        playing = _player_window.get_album_id()
-        if int(playing) == int(albumid):
-            _player_window.set_album_cover(cover)
-
 class PlayerWindow(hildon.StackableWindow):
     def __init__(self, playlist=None):
         hildon.StackableWindow.__init__(self)
         self.set_title("jamaendo")
 
-        global _player_window
-        _player_window = self
-
-        self.connect('hide', self.on_hide)
         self.connect('destroy', self.on_destroy)
 
         self.playlist = Playlist(playlist)
@@ -54,7 +42,6 @@ class PlayerWindow(hildon.StackableWindow):
         hbox = gtk.HBox()
 
         self.cover = gtk.Image()
-        self.cover.set_size_request(200, 200)
         self.cover.set_from_stock(gtk.STOCK_CDROM, gtk.ICON_SIZE_DIALOG)
 
         vbox2 = gtk.VBox()
@@ -103,6 +90,8 @@ class PlayerWindow(hildon.StackableWindow):
         hbox.pack_start(self.volume, False)
         self.add(vbox)
 
+        postoffice.connect('album-cover', self.set_album_cover)
+
         print "Created player window, playlist: %s" % (self.playlist)
 
     def get_album_id(self):
@@ -110,15 +99,8 @@ class PlayerWindow(hildon.StackableWindow):
             return self.playlist.current().album_id
         return None
 
-    def on_hide(self, wnd):
-        print "Hiding player window"
-
     def on_destroy(self, wnd):
-        print "Destroying player window"
-        global _player_window
-        _player_window = None
-        if self.player:
-            self.player.stop()
+        postoffice.disconnect('album-cover', self.set_album_cover)
 
     def add_stock_button(self, btns, stock, cb):
         btn = hildon.GtkButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
@@ -151,13 +133,17 @@ class PlayerWindow(hildon.StackableWindow):
             print "current:", item
             self.set_labels(item.name, item.artist_name, item.album_name,
                             self.playlist.current_index(), self.playlist.size())
-            jamaendo.get_album_cover_async(album_cover_receiver, int(item.album_id), size=200)
+            postoffice.notify('request-album-cover', int(item.album_id), 300)
+            #jamaendo.get_album_cover_async(album_cover_receiver, int(item.album_id), size=200)
             #coverfile = jamaendo.get_album_cover(int(item.album_id), size=200)
             #print "coverfile:", coverfile
             #self.cover.set_from_file(coverfile)
 
-    def set_album_cover(self, cover):
-        self.cover.set_from_file(cover)
+    def set_album_cover(self, albumid, size, cover):
+        if size == 300:
+            playing = self.get_album_id()
+            if int(playing) == int(albumid):
+                self.cover.set_from_file(cover)
 
     def play_tracks(self, tracks):
         self.playlist = Playlist(tracks)
index 009c884..675f9bf 100644 (file)
 #
 # message central
 
+import logging
+
+log = logging.getLogger(__name__)
+
 class PostOffice(object):
-    class Client(object):
-        def __init__(self):
-            self.tags = {}
-        def has_tag(self, tag):
-            return tag in self.tags
-        def notify(self, tags, data):
-            for tag in tags:
-                cb = self.tags.get(tag)
-                if cb:
-                    cb(data)
-        def register(self, tag, callback):
-            self.tags[tag] = callback
 
     def __init__(self):
-        self.clients = {}
-
-    def notify(self, tags, data):
-        if not isinstance(tags, list):
-            tags = [tags]
-        for client in clients:
-            client.notify(tags, data)
-
-    def register(self, client_id, tag, callback):
-        client = self.clients.get(client_id)
-        if not client:
-            client = Client()
-            self.clients[client_id] = client
-        client.register(tag, callback)
+        self.tags = {} # tag -> [callback]
+
+    def notify(self, tag, *data):
+        clients = self.tags.get(tag)
+        if clients:
+            log.debug("(%s %s) -> [%s]",
+                      tag,
+                      " ".join(str(x) for x in data),
+                      " ".join(str(x) for x in clients))
+            for client in clients:
+                client(*data)
+
+    def connect(self, tag, callback):
+        if tag not in self.tags:
+            self.tags[tag] = []
+        clients = self.tags[tag]
+        if callback not in clients:
+            clients.append(callback)
+
+    def disconnect(self, tag, callback):
+        if tag not in self.tags:
+            self.tags[tag] = []
+        clients = self.tags[tag]
+        if callback in clients:
+            clients.remove(callback)
 
 postoffice = PostOffice()
 
index 40779f7..f7e27fb 100644 (file)
@@ -88,8 +88,8 @@ class SearchWindow(hildon.StackableWindow):
         self.idmap = {}
 
     def mode_changed(self, selector, user_data):
-        current_selection = selector.get_current_text()
-        print current_selection
+        pass
+        #current_selection = selector.get_current_text()
 
     def on_search(self, w):
         mode = self.mode.get_active()
@@ -115,7 +115,7 @@ class SearchWindow(hildon.StackableWindow):
         treeiter = self.result_store.get_iter(path)
         title, _id = self.result_store.get(treeiter, 0, 1)
         item = self.idmap[_id]
-        print _id, item
+        #print _id, item
         self.open_item(item)
 
     def open_item(self, item):
index c1dcc61..f141c69 100644 (file)
@@ -24,6 +24,8 @@
 import cPickle, os
 import logging
 
+from postoffice import postoffice
+
 VERSION = 1
 log = logging.getLogger(__name__)
 
@@ -39,6 +41,11 @@ class Settings(object):
         for k,v in self.defaults.iteritems():
             setattr(self, k, v)
 
+    def __setattr__(self, key, value):
+        object.__setattr__(self, key, value)
+        if key in self.defaults.keys():
+            postoffice.notify('settings-changed', key, value)
+
     def set_filename(self, savename):
         self.__savename = savename
 
index c95974e..773db8c 100644 (file)
@@ -26,27 +26,22 @@ import hildon
 import jamaendo
 from playerwindow import open_playerwindow
 from settings import settings
+from postoffice import postoffice
 import util
+import logging
+from albumlist import TrackList
+import webbrowser
 
-_instance = None
-
-def album_cover_receiver(albumid, size, cover):
-    if _instance:
-        playing = _instance.get_album_id()
-        if int(playing) == int(albumid):
-            _instance.set_album_cover(cover)
+log = logging.getLogger(__name__)
 
 class ShowAlbum(hildon.StackableWindow):
     def __init__(self, album):
         hildon.StackableWindow.__init__(self)
-        self.set_title("Album")
+        self.set_title(album.artist_name)
         self.album = album
 
-        global _instance
-        _instance = self
         self.connect('destroy', self.on_destroy)
 
-        top_vbox = gtk.VBox()
         top_hbox = gtk.HBox()
         vbox1 = gtk.VBox()
         self.cover = gtk.Image()
@@ -62,33 +57,16 @@ class ShowAlbum(hildon.StackableWindow):
         self.playbtn.connect('clicked', self.on_play)
 
         vbox2 = gtk.VBox()
-        self.albumtitle = gtk.Label()
-        self.albumtitle.set_markup('<big>%s</big>' % (album.name))
-        self.artist = gtk.Label()
-        self.artist.set_markup('<span color="#cccccc">%s</span>'%(album.artist_name))
+        self.albumname = gtk.Label()
+        self.albumname.set_markup('<big>%s</big>'%(album.name))
         self.trackarea = hildon.PannableArea()
 
-        self.album_store = gtk.ListStore(int, str, int)
-        self.album_view = gtk.TreeView(self.album_store)
-        col0 = gtk.TreeViewColumn('Num')
-        col = gtk.TreeViewColumn('Name')
-        self.album_view.append_column(col0)
-        self.album_view.append_column(col)
-        cell0 = gtk.CellRendererText()
-        col0.pack_start(cell0, True)
-        col0.add_attribute(cell0, 'text', 0)
-        cell = gtk.CellRendererText()
-        col.pack_start(cell, True)
-        col.add_attribute(cell, 'text', 1)
-        self.album_view.set_search_column(1)
-        col.set_sort_column_id(0)
-        self.album_view.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.album_store.append([track.numalbum, track.name, track.ID])
+            self.tracks.add_track(track)
 
-        top_vbox.pack_start(self.albumtitle, False)
-        top_vbox.pack_start(top_hbox)
         top_hbox.pack_start(vbox1, False)
         top_hbox.pack_start(vbox2, True)
         vbox1.pack_start(self.cover, True)
@@ -98,21 +76,23 @@ class ShowAlbum(hildon.StackableWindow):
         self.bbox.add(self.download)
         self.bbox.add(self.favorite)
         self.bbox.add(self.license)
-        vbox2.pack_start(self.artist, False)
+        vbox2.pack_start(self.albumname, False)
         vbox2.pack_start(self.trackarea, True)
-        self.trackarea.add(self.album_view)
+        self.trackarea.add(self.tracks)
 
-        self.add(top_vbox)
+        self.add(top_hbox)
 
-        # here it's probably ok to get the cover synchronously..
-        coverfile = jamaendo.get_album_cover(self.album.ID, size=200)
-        self.cover.set_from_file(coverfile)
+        postoffice.connect('album-cover', self.on_album_cover)
+        postoffice.notify('request-album-cover', self.album.ID, 300)
 
         self.show_all()
 
     def on_destroy(self, wnd):
-        global _instance
-        _instance = None
+        postoffice.disconnect('album-cover', self.on_album_cover)
+
+    def on_album_cover(self, albumid, size, cover):
+        if albumid == self.album.ID and size == 300:
+            self.cover.set_from_file(cover)
 
     def make_imagebutton(self, name, cb):
         btn = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
@@ -131,8 +111,10 @@ class ShowAlbum(hildon.StackableWindow):
         self.open_item(artist)
 
     def on_download(self, btn):
-        banner = hildon.hildon_banner_show_information(self, '', "Downloads disabled in this version")
+        banner = hildon.hildon_banner_show_information(self, '', "Opening in web browser")
         banner.set_timeout(2000)
+        url = self.album.torrent_url()
+        webbrowser.open_new(url)
 
     def on_favorite(self, btn):
         settings.favorite(self.album)
@@ -141,15 +123,17 @@ class ShowAlbum(hildon.StackableWindow):
 
 
     def on_license(self, btn):
+        banner = hildon.hildon_banner_show_information(self, '', "Opening in web browser")
+        banner.set_timeout(2000)
         url = self.album.license_url
-        import webbrowser
         webbrowser.open_new(url)
 
     def on_play(self, btn):
         self.open_item(self.album)
 
     def row_activated(self, treeview, path, view_column):
-        pass
+        _id = self.tracks.get_track_id(path)
+        log.debug("clicked %s", _id)
 
     def open_item(self, item):
         if isinstance(item, jamaendo.Album):
index ecc857b..9cae975 100644 (file)
@@ -25,42 +25,30 @@ import gtk
 import hildon
 import jamaendo
 from playerwindow import open_playerwindow
+from albumlist import AlbumList
 
 class ShowArtist(hildon.StackableWindow):
     def __init__(self, artist):
         hildon.StackableWindow.__init__(self)
-        self.set_title("Artist")
+        self.set_title(artist.name)
         self.artist = artist
 
         self.panarea = hildon.PannableArea()
         vbox = gtk.VBox(False, 0)
 
-        name = gtk.Label()
-        name.set_markup('<big>%s</big>'%(artist.name))
-        vbox.pack_start(name, False)
+        self.albums = AlbumList()
+        self.albums.show_artist(False)
+        self.albums.connect('row-activated', self.row_activated)
 
-        self.album_store = gtk.ListStore(str, int)
-        self.album_view = gtk.TreeView(self.album_store)
-        col = gtk.TreeViewColumn('Name')
-        self.album_view.append_column(col)
-        cell = gtk.CellRendererText()
-        col.pack_start(cell, True)
-        col.add_attribute(cell, 'text', 0)
-        self.album_view.set_search_column(0)
-        col.set_sort_column_id(0)
-        self.album_view.connect('row-activated', self.row_activated)
-
-
-        self.panarea.add(self.album_view)
+        self.panarea.add(self.albums)
         vbox.pack_start(self.panarea, True, True, 0)
         self.add(vbox)
 
         for album in jamaendo.get_albums(artist.ID):
-            self.album_store.append([album.name, album.ID])
+            self.albums.add_album(album)
 
     def row_activated(self, treeview, path, view_column):
-        treeiter = self.album_store.get_iter(path)
-        title, _id = self.album_store.get(treeiter, 0, 1)
+        _id = self.albums.get_album_id(path)
         album = jamaendo.get_album(_id)
         if isinstance(album, list):
             album = album[0]
index 2ba4597..7a8fc6d 100644 (file)
@@ -56,6 +56,7 @@ DBusGMainLoop(set_as_default=True)
 
 import jamaendo
 
+from postoffice import postoffice
 from playerwindow import open_playerwindow
 from search import SearchWindow
 from featured import FeaturedWindow
@@ -91,6 +92,8 @@ class Jamaui(object):
         settings.set_filename(os.path.join(self.CONFDIR, 'ui_settings'))
         settings.load()
 
+        postoffice.connect('request-album-cover', self.on_request_cover)
+
     def save_settings(self):
         settings.save()
 
@@ -167,6 +170,12 @@ class Jamaui(object):
         btn.connect('clicked', callback)
         self.bbox.add(btn)
 
+    def on_request_cover(self, albumid, size):
+        jamaendo.get_album_cover_async(self.got_album_cover, int(albumid), size)
+
+    def got_album_cover(self, albumid, size, cover):
+        postoffice.notify('album-cover', albumid, size, cover)
+
     #def add_featured_button(self):
     #    self.featured_sel = hildon.TouchSelector(text=True)
     #    self.featured_sel.append_text("Albums of the week")
@@ -181,6 +190,7 @@ class Jamaui(object):
     #    self.bbox.add(btn)
 
     def destroy(self, widget):
+        postoffice.disconnect('request-album-cover', self.on_request_cover)
         gtk.main_quit()
 
     def show_about(self, w, win):