~ubuntu-branches/ubuntu/oneiric/rhythmbox/oneiric

« back to all changes in this revision

Viewing changes to plugins/context/context/AlbumTab.py

  • Committer: Bazaar Package Importer
  • Author(s): Rico Tzschichholz
  • Date: 2011-07-29 16:41:38 UTC
  • mto: This revision was merged to the branch mainline in revision 191.
  • Revision ID: james.westby@ubuntu.com-20110729164138-wwicy8nqalm18ck7
Tags: upstream-2.90.1~20110802
ImportĀ upstreamĀ versionĀ 2.90.1~20110802

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
2
 
#
3
 
# Copyright (C) 2009 John Iacona
4
 
#
5
 
# This program is free software; you can redistribute it and/or modify
6
 
# it under the terms of the GNU General Public License as published by
7
 
# the Free Software Foundation; either version 2, or (at your option)
8
 
# any later version.
9
 
#
10
 
# The Rhythmbox authors hereby grant permission for non-GPL compatible
11
 
# GStreamer plugins to be used and distributed together with GStreamer
12
 
# and Rhythmbox. This permission is above and beyond the permissions granted
13
 
# by the GPL license by which Rhythmbox is covered. If you modify this code
14
 
# you may extend this exception to your version of the code, but you are not
15
 
# obligated to do so. If you do not wish to do so, delete this exception
16
 
# statement from your version.
17
 
#
18
 
# This program is distributed in the hope that it will be useful,
19
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 
# GNU General Public License for more details.
22
 
#
23
 
# You should have received a copy of the GNU General Public License
24
 
# along with this program; if not, write to the Free Software
25
 
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
26
 
 
27
 
import gobject
28
 
import os
29
 
import cgi
30
 
import urllib
31
 
from mako.template import Template
32
 
import xml.dom.minidom as dom
33
 
 
34
 
import LastFM
35
 
 
36
 
import rb
37
 
from gi.repository import RB
38
 
from gi.repository import Gtk
39
 
from gi.repository import WebKit
40
 
 
41
 
class AlbumTab (gobject.GObject):
42
 
 
43
 
    __gsignals__ = {
44
 
        'switch-tab' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
45
 
                                (gobject.TYPE_STRING,))
46
 
    }
47
 
    
48
 
    def __init__ (self, shell, buttons, ds, view):
49
 
        gobject.GObject.__init__ (self)
50
 
        self.shell      = shell
51
 
        self.sp         = shell.get_player ()
52
 
        self.db         = shell.get_property ('db') 
53
 
        self.buttons    = buttons
54
 
 
55
 
        self.button     = Gtk.ToggleButton (label=_("Albums"))
56
 
        self.ds         = ds
57
 
        self.view       = view
58
 
        self.artist     = None
59
 
        self.active     = False
60
 
 
61
 
        self.button.show()
62
 
        self.button.set_relief (Gtk.ReliefStyle.NONE)
63
 
        self.button.set_focus_on_click(False)
64
 
        self.button.connect ('clicked', 
65
 
            lambda button: self.emit ('switch-tab', 'album'))
66
 
        buttons.pack_start (self.button, True, True, 0)
67
 
 
68
 
    def activate (self):
69
 
        self.button.set_active(True)
70
 
        self.active = True
71
 
        self.reload ()
72
 
 
73
 
    def deactivate (self):
74
 
        self.button.set_active(False)
75
 
        self.active = False
76
 
 
77
 
    def reload (self):
78
 
        entry = self.sp.get_playing_entry ()
79
 
        if entry is None:
80
 
            return None
81
 
 
82
 
        artist = entry.get_string (RB.RhythmDBPropType.ARTIST)
83
 
        album  = entry.get_string (RB.RhythmDBPropType.ALBUM)
84
 
        if self.active and artist != self.artist:
85
 
            self.view.loading(artist)
86
 
            self.ds.fetch_album_list (artist)
87
 
        else:
88
 
            self.view.load_view()
89
 
 
90
 
        self.artist = artist
91
 
 
92
 
class AlbumView (gobject.GObject):
93
 
 
94
 
    def __init__ (self, shell, plugin, webview, ds):
95
 
        gobject.GObject.__init__ (self)
96
 
        self.webview = webview
97
 
        self.ds      = ds
98
 
        self.shell   = shell
99
 
        self.plugin  = plugin
100
 
        self.file    = ""
101
 
 
102
 
        plugindir = os.path.split(plugin.find_file ('context.rb-plugin'))[0]
103
 
        self.basepath = "file://" + urllib.pathname2url (plugindir)
104
 
 
105
 
        self.load_tmpl ()
106
 
        self.connect_signals ()
107
 
 
108
 
    def load_view (self):
109
 
        self.webview.load_string(self.file, 'text/html', 'utf-8', self.basepath)
110
 
 
111
 
    def connect_signals (self):
112
 
        self.ds.connect('albums-ready', self.album_list_ready)
113
 
 
114
 
    def loading (self, current_artist):
115
 
        self.loading_file = self.loading_template.render (
116
 
            artist   = current_artist,
117
 
            # Translators: 'top' here means 'most popular'.  %s is replaced by the artist name.
118
 
            info     = _("Loading top albums for %s") % current_artist,
119
 
            song     = "",
120
 
            basepath = self.basepath)
121
 
        self.webview.load_string (self.loading_file, 'text/html', 'utf-8', self.basepath)
122
 
 
123
 
    def load_tmpl (self):
124
 
        self.path = self.plugin.find_file ('tmpl/album-tmpl.html')
125
 
        self.loading_path = self.plugin.find_file ('tmpl/loading.html')
126
 
        self.album_template = Template (filename = self.path,
127
 
                                        module_directory = '/tmp/context')
128
 
        self.loading_template = Template (filename = self.loading_path, 
129
 
                                          module_directory = '/tmp/context')
130
 
        self.styles = self.basepath + '/tmpl/main.css'
131
 
 
132
 
    def album_list_ready (self, ds):
133
 
        self.file = self.album_template.render (error = ds.get_error(), 
134
 
                                                list = ds.get_top_albums(), 
135
 
                                                artist = ds.get_artist(),
136
 
                                                datasource = LastFM.datasource_link (self.basepath),
137
 
                                                stylesheet = self.styles)
138
 
        self.load_view ()
139
 
 
140
 
 
141
 
class AlbumDataSource (gobject.GObject):
142
 
    
143
 
    __gsignals__ = {
144
 
        'albums-ready' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())
145
 
    }
146
 
 
147
 
    def __init__ (self, info_cache, ranking_cache):
148
 
        gobject.GObject.__init__ (self)
149
 
        self.albums = None
150
 
        self.error = None
151
 
        self.artist = None
152
 
        self.max_albums_fetched = 8
153
 
        self.info_cache = info_cache
154
 
        self.ranking_cache = ranking_cache
155
 
 
156
 
    def extract (self, data, position):
157
 
        """
158
 
        Safely extract the data from an xml node. Returns data
159
 
        at position or None if position does not exist
160
 
        """
161
 
        try:
162
 
            return data[position].firstChild.data
163
 
        except Exception, e:
164
 
            return None
165
 
 
166
 
    def get_artist (self):
167
 
        return self.artist
168
 
 
169
 
    def get_error (self):
170
 
        return self.error
171
 
 
172
 
    def fetch_album_list (self, artist):
173
 
        if LastFM.user_has_account() is False:
174
 
            self.error = LastFM.NO_ACCOUNT_ERROR
175
 
            self.emit ('albums-ready')
176
 
            return
177
 
 
178
 
        self.artist = artist
179
 
        qartist = urllib.quote_plus (artist)
180
 
        self.error  = None
181
 
        url = "%sartist.gettopalbums&artist=%s&api_key=%s" % (LastFM.URL_PREFIX,
182
 
                                                              qartist,
183
 
                                                              LastFM.API_KEY)
184
 
        cachekey = 'lastfm:artist:gettopalbums:%s' % qartist
185
 
        self.ranking_cache.fetch(cachekey, url, self.parse_album_list, artist)
186
 
 
187
 
    def parse_album_list (self, data, artist):
188
 
        if data is None:
189
 
            print "Nothing fetched for %s top albums" % artist
190
 
            return
191
 
 
192
 
        try:
193
 
            parsed = dom.parseString (data)
194
 
        except Exception, e:
195
 
            print "Error parsing album list: %s" % e
196
 
            return False
197
 
 
198
 
        lfm = parsed.getElementsByTagName ('lfm')[0]
199
 
        if lfm.attributes['status'].value == 'failed':
200
 
            self.error = lfm.childNodes[1].firstChild.data
201
 
            self.emit ('albums-ready')
202
 
            return
203
 
 
204
 
        self.albums = []
205
 
        album_nodes = parsed.getElementsByTagName ('album') 
206
 
        print "num albums: %d" % len(album_nodes)
207
 
        if len(album_nodes) == 0:
208
 
            self.error = "No albums found for %s" % artist
209
 
            self.emit('albums-ready')
210
 
            return
211
 
            
212
 
        self.album_info_fetched = min (len (album_nodes) - 1, self.max_albums_fetched)
213
 
 
214
 
        for i, album in enumerate (album_nodes): 
215
 
            if i >= self.album_info_fetched:
216
 
                break
217
 
 
218
 
            album_name = self.extract(album.getElementsByTagName ('name'), 0)
219
 
            imgs = album.getElementsByTagName ('image')
220
 
            images = (self.extract(imgs, 0), self.extract(imgs, 1), self.extract(imgs, 2))
221
 
            self.albums.append ({'title' : album_name, 'images' : images })
222
 
            self.fetch_album_info (artist, album_name, i)
223
 
 
224
 
    def get_top_albums (self):
225
 
        return self.albums
226
 
 
227
 
    def fetch_album_info (self, artist, album, index):
228
 
        qartist = urllib.quote_plus (artist)
229
 
        qalbum = urllib.quote_plus (album.encode('utf-8'))
230
 
        cachekey = "lastfm:album:getinfo:%s:%s" % (qartist, qalbum)
231
 
        url = "%salbum.getinfo&artist=%s&album=%s&api_key=%s" % (LastFM.URL_PREFIX,
232
 
                                                                 qartist,
233
 
                                                                 qalbum,
234
 
                                                                 LastFM.API_KEY)
235
 
        self.info_cache.fetch(cachekey, url, self.fetch_album_tracklist, album, index)
236
 
 
237
 
    def fetch_album_tracklist (self, data, album, index):
238
 
        if data is None:
239
 
            self.assemble_info(None, None, None)
240
 
 
241
 
        try:
242
 
            parsed = dom.parseString (data)
243
 
            self.albums[index]['id'] = parsed.getElementsByTagName ('id')[0].firstChild.data
244
 
        except Exception, e:
245
 
            print "Error parsing album tracklist: %s" % e
246
 
            return False
247
 
 
248
 
        self.albums[index]['releasedate'] = self.extract(parsed.getElementsByTagName ('releasedate'),0)
249
 
        self.albums[index]['summary'] = self.extract(parsed.getElementsByTagName ('summary'), 0)
250
 
 
251
 
        cachekey = "lastfm:album:tracks:%s" % self.albums[index]['id']
252
 
        url = "%splaylist.fetch&playlistURL=lastfm://playlist/album/%s&api_key=%s" % (
253
 
                     LastFM.URL_PREFIX, self.albums[index]['id'], LastFM.API_KEY)
254
 
 
255
 
        self.info_cache.fetch(cachekey, url, self.assemble_info, album, index)
256
 
 
257
 
    def assemble_info (self, data, album, index):
258
 
        rv = True
259
 
        if data is None:
260
 
            print "nothing fetched for %s tracklist" % album
261
 
        else:
262
 
            try:
263
 
                parsed = dom.parseString (data)
264
 
                list = parsed.getElementsByTagName ('track')
265
 
                tracklist = []
266
 
                album_length = 0
267
 
                for i, track in enumerate(list):
268
 
                    title = track.getElementsByTagName ('title')[0].firstChild.data
269
 
                    duration = int(track.getElementsByTagName ('duration')[0].firstChild.data) / 1000
270
 
                    album_length += duration
271
 
                    tracklist.append ((i, title, duration))
272
 
                self.albums[index]['tracklist'] = tracklist
273
 
                self.albums[index]['duration']  = album_length
274
 
            except Exception, e:
275
 
                print "Error parsing album playlist: %s" % e
276
 
                rv = False
277
 
 
278
 
        self.album_info_fetched -= 1
279
 
        print "%s albums left to process" % self.album_info_fetched
280
 
        if self.album_info_fetched == 0:
281
 
            self.emit('albums-ready')
282
 
 
283
 
        return rv