1
# -*- coding: utf-8 -*-
5
# Copyright (C) 2007 - Guillaume Desmottes
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 2, or (at your option)
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program; if not, write to the Free Software
19
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
# Parts from "Magnatune Rhythmbox plugin" (stolen from rhythmbox's MagnatuneSource.py)
22
# Copyright (C), 2006 Adam Zimmerman <adam_zimmerman@sfu.ca>
25
from JamendoSaxHandler import JamendoSaxHandler
26
import JamendoConfigureDialog
38
jamendo_song_info_uri = "http://img.jamendo.com/data/dbdump_artistalbumtrack.xml.gz"
40
mp32_uri = "http://api.jamendo.com/get2/bittorrent/file/plain/?type=archive&class=mp32&album_id="
41
ogg3_uri = "http://api.jamendo.com/get2/bittorrent/file/plain/?type=archive&class=ogg3&album_id="
44
# MP3s for streaming : http://api.jamendo.com/get2/stream/track/redirect/?id={TRACKID}&streamencoding=mp31
45
# OGGs for streaming : http://api.jamendo.com/get2/stream/track/redirect/?id={TRACKID}&streamencoding=ogg2
47
# .torrent file for download (MP3 archive) : http://api.jamendo.com/get2/bittorrent/file/plain/?album_id={ALBUMID}&type=archive&class=mp32
48
# .torrent file for download (OGG archive) : http://api.jamendo.com/get2/bittorrent/file/plain/?album_id={ALBUMID}&type=archive&class=ogg3
50
# Album Covers are available here: http://api.jamendo.com/get2/image/album/redirect/?id={ALBUMID}&imagesize={100-600}
52
artwork_url = "http://api.jamendo.com/get2/image/album/redirect/?id=%s&imagesize=200"
53
artist_url = "http://www.jamendo.com/get/artist/id/album/page/plain/"
55
class JamendoSource(rb.BrowserSource):
57
'plugin': (rb.Plugin, 'plugin', 'plugin', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
62
rb.BrowserSource.__init__(self, name=_("Jamendo"))
66
self.__saxHandler = None
67
self.__activated = False
70
self.__info_screen = None
71
self.__updating = True
72
self.__load_current_size = 0
73
self.__load_total_size = 0
74
self.__db_load_finished = False
76
self.__catalogue_loader = None
77
self.__catalogue_check = None
79
self.__jamendo_dir = rb.find_user_cache_file("jamendo")
80
if os.path.exists(self.__jamendo_dir) is False:
81
os.makedirs(self.__jamendo_dir, 0700)
83
self.__local_catalogue_path = os.path.join(self.__jamendo_dir, "dbdump.xml")
84
self.__local_catalogue_temp = os.path.join(self.__jamendo_dir, "dbdump.xml.tmp")
86
def do_set_property(self, property, value):
87
if property.name == 'plugin':
90
raise AttributeError, 'unknown property %s' % property.name
92
def do_impl_get_browser_key (self):
93
return "/apps/rhythmbox/plugins/jamendo/show_browser"
95
def do_impl_get_paned_key (self):
96
return "/apps/rhythmbox/plugins/jamendo/paned_position"
98
def do_impl_can_delete (self):
101
def do_impl_pack_paned (self, paned):
102
self.__paned_box = gtk.VBox(False, 5)
103
self.pack_start(self.__paned_box)
104
self.__paned_box.pack_start(paned)
110
def do_impl_show_entry_popup(self):
111
self.show_source_popup ("/JamendoSourceViewPopup")
113
def do_impl_get_ui_actions(self):
114
return ["JamendoDownloadAlbum","JamendoDonateArtist"]
117
def do_impl_get_status(self):
119
if self.__load_total_size > 0:
120
progress = min (float(self.__load_current_size) / self.__load_total_size, 1.0)
123
return (_("Loading Jamendo catalog"), None, progress)
125
qm = self.get_property("query-model")
126
return (qm.compute_status_normal("%d song", "%d songs"), None, 2.0)
128
def do_impl_activate(self):
129
if not self.__activated:
130
shell = self.get_property('shell')
131
self.__db = shell.get_property('db')
132
self.__entry_type = self.get_property('entry-type')
134
self.__activated = True
135
self.__show_loading_screen (True)
137
# start our catalogue updates
138
self.__update_id = gobject.timeout_add_seconds(6 * 60 * 60, self.__update_catalogue)
139
self.__update_catalogue()
141
sort_key = gconf.client_get_default().get_string(JamendoConfigureDialog.gconf_keys['sorting'])
143
sort_key = "Artist,ascending"
144
self.get_entry_view().set_sorting_type(sort_key)
146
rb.BrowserSource.do_impl_activate (self)
148
def do_impl_delete_thyself(self):
149
if self.__update_id != 0:
150
gobject.source_remove (self.__update_id)
153
if self.__notify_id != 0:
154
gobject.source_remove (self.__notify_id)
157
if self.__catalogue_loader:
158
self.__catalogue_loader.cancel()
159
self.__catalogue_loader = None
161
if self.__catalogue_check:
162
self.__catalogue_check.cancel()
163
self.__catalogue_check = None
165
gconf.client_get_default().set_string(JamendoConfigureDialog.gconf_keys['sorting'], self.get_entry_view().get_sorting_type())
166
rb.BrowserSource.do_impl_delete_thyself (self)
170
# internal catalogue downloading and loading
173
def __catalogue_chunk_cb(self, result, total):
174
if not result or isinstance (result, Exception):
176
# report error somehow?
177
print "error loading catalogue: %s" % result
179
self.__parser.close()
180
self.__db_load_finished = True
181
self.__updating = False
182
self.__saxHandler = None
183
self.__show_loading_screen (False)
184
self.__catalogue_loader = None
187
self.__parser.feed(result)
188
self.__load_current_size += len(result)
189
self.__load_total_size = total
190
self.__notify_status_changed()
192
def __load_catalogue(self):
193
print "loading catalogue %s" % self.__local_catalogue_path
194
self.__notify_status_changed()
195
self.__db_load_finished = False
197
self.__saxHandler = JamendoSaxHandler(self.__db, self.__entry_type)
198
self.__parser = xml.sax.make_parser()
199
self.__parser.setContentHandler(self.__saxHandler)
201
self.__catalogue_loader = rb.ChunkLoader()
202
self.__catalogue_loader.get_url_chunks(self.__local_catalogue_path, 64*1024, True, self.__catalogue_chunk_cb)
205
def __download_catalogue_chunk_cb (self, result, total, out):
207
# done downloading, unzip to real location
209
catalog = gzip.open(self.__local_catalogue_temp)
210
out = open(self.__local_catalogue_path, 'w')
213
s = catalog.read(4096)
220
os.unlink(self.__local_catalogue_temp)
222
self.__db_load_finished = True
223
self.__show_loading_screen (False)
224
self.__catalogue_loader = None
226
self.__load_catalogue ()
228
elif isinstance(result, Exception):
233
self.__load_current_size += len(result)
234
self.__load_total_size = total
236
self.__notify_status_changed()
238
def __download_catalogue(self):
239
print "downloading catalogue"
240
self.__updating = True
241
out = open(self.__local_catalogue_temp, 'w')
243
self.__catalogue_loader = rb.ChunkLoader()
244
self.__catalogue_loader.get_url_chunks(jamendo_song_info_uri, 4*1024, True, self.__download_catalogue_chunk_cb, out)
246
def __update_catalogue(self):
247
def update_cb (result):
248
self.__catalogue_check = None
250
self.__download_catalogue()
251
elif self.__db_load_finished is False:
252
self.__load_catalogue()
254
self.__catalogue_check = rb.UpdateCheck()
255
self.__catalogue_check.check_for_update(self.__local_catalogue_path, jamendo_song_info_uri, update_cb)
258
def __show_loading_screen(self, show):
259
if self.__info_screen is None:
260
# load the builder stuff
261
builder = gtk.Builder()
262
builder.add_from_file(self.__plugin.find_file("jamendo-loading.ui"))
264
self.__info_screen = builder.get_object("jamendo_loading_scrolledwindow")
265
self.pack_start(self.__info_screen)
266
self.get_entry_view().set_no_show_all (True)
267
self.__info_screen.set_no_show_all (True)
269
self.__info_screen.set_property("visible", show)
270
self.__paned_box.set_property("visible", not show)
273
def __notify_status_changed(self):
274
def change_idle_cb():
275
self.notify_status_changed()
279
if self.__notify_id == 0:
280
self.__notify_id = gobject.idle_add(change_idle_cb)
284
def download_album (self):
285
tracks = self.get_entry_view().get_selected_entries()
286
format = gconf.client_get_default().get_string(JamendoConfigureDialog.gconf_keys['format'])
287
if not format or format not in JamendoConfigureDialog.format_list:
290
#TODO: this should work if the album was selected in the browser
291
#without any track selected
294
albumid = self.__db.entry_get(track, rhythmdb.PROP_MUSICBRAINZ_ALBUMID)
297
formats["mp32"] = mp32_uri + albumid
298
formats["ogg3"] = ogg3_uri + albumid
300
p2plink = formats[format]
302
l.get_url(p2plink, self.__download_p2plink, albumid)
304
def __download_p2plink (self, result, albumid):
306
emsg = _("Error looking up p2plink for album %s on jamendo.com") % (albumid)
307
gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg).run()
310
gtk.show_uri(self.props.shell.props.window.get_screen(), result, gtk.gdk.CURRENT_TIME)
313
def launch_donate (self):
314
tracks = self.get_entry_view().get_selected_entries()
316
#TODO: this should work if the artist was selected in the browser
317
#without any track selected
320
# The Album ID can be used to lookup the artist, and issue a clean redirect.
321
albumid = self.__db.entry_get(track, rhythmdb.PROP_MUSICBRAINZ_ALBUMID)
322
artist = self.__db.entry_get(track, rhythmdb.PROP_ARTIST)
323
url = artist_url + albumid.__str__() + "/"
326
l.get_url(url, self.__open_donate, artist)
328
def __open_donate (self, result, artist):
330
emsg = _("Error looking up artist %s on jamendo.com") % (artist)
331
gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, emsg).run()
333
gtk.show_uri(self.props.shell.props.window.get_screen(), result + "donate/", gtk.gdk.CURRENT_TIME)
335
def playing_entry_changed (self, entry):
336
if not self.__db or not entry:
339
if entry.get_entry_type() != self.__db.entry_type_get_by_name("JamendoEntryType"):
342
gobject.idle_add(self.emit_cover_art_uri, entry)
344
def emit_cover_art_uri (self, entry):
345
stream = self.__db.entry_get (entry, rhythmdb.PROP_LOCATION)
346
albumid = self.__db.entry_get (entry, rhythmdb.PROP_MUSICBRAINZ_ALBUMID)
347
url = artwork_url % albumid
349
self.__db.emit_entry_extra_metadata_notify (entry, "rb:coverArt-uri", str(url))
352
gobject.type_register(JamendoSource)