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>
31
from gi.repository import Gtk, Gdk, Gio
32
from gi.repository import RB
34
from JamendoSaxHandler import JamendoSaxHandler
35
import JamendoConfigureDialog
39
jamendo_song_info_uri = "http://img.jamendo.com/data/dbdump_artistalbumtrack.xml.gz"
41
mp32_uri = "http://api.jamendo.com/get2/bittorrent/file/plain/?type=archive&class=mp32&album_id="
42
ogg3_uri = "http://api.jamendo.com/get2/bittorrent/file/plain/?type=archive&class=ogg3&album_id="
45
# MP3s for streaming : http://api.jamendo.com/get2/stream/track/redirect/?id={TRACKID}&streamencoding=mp31
46
# OGGs for streaming : http://api.jamendo.com/get2/stream/track/redirect/?id={TRACKID}&streamencoding=ogg2
48
# .torrent file for download (MP3 archive) : http://api.jamendo.com/get2/bittorrent/file/plain/?album_id={ALBUMID}&type=archive&class=mp32
49
# .torrent file for download (OGG archive) : http://api.jamendo.com/get2/bittorrent/file/plain/?album_id={ALBUMID}&type=archive&class=ogg3
51
# Album Covers are available here: http://api.jamendo.com/get2/image/album/redirect/?id={ALBUMID}&imagesize={100-600}
53
artwork_url = "http://api.jamendo.com/get2/image/album/redirect/?id=%s&imagesize=200"
54
artist_url = "http://www.jamendo.com/get/artist/id/album/page/plain/"
56
class JamendoSource(RB.BrowserSource):
59
RB.BrowserSource.__init__(self, name=_("Jamendo"))
64
self.__saxHandler = None
65
self.__activated = False
68
self.__info_screen = None
69
self.__updating = True
70
self.__load_current_size = 0
71
self.__load_total_size = 0
72
self.__db_load_finished = False
74
self.__catalogue_loader = None
75
self.__catalogue_check = None
77
self.__jamendo_dir = RB.find_user_cache_file("jamendo")
78
if os.path.exists(self.__jamendo_dir) is False:
79
os.makedirs(self.__jamendo_dir, 0700)
81
self.__local_catalogue_path = os.path.join(self.__jamendo_dir, "dbdump.xml")
82
self.__local_catalogue_temp = os.path.join(self.__jamendo_dir, "dbdump.xml.tmp")
84
self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.jamendo")
86
def do_impl_can_delete (self):
89
def do_impl_pack_paned (self, paned):
90
self.__paned_box = Gtk.VBox(homogeneous=False, spacing=5)
91
self.pack_start(self.__paned_box, True, True, 0)
92
self.__paned_box.pack_start(paned, True, True, 0)
98
def do_impl_show_entry_popup(self):
99
self.show_source_popup ("/JamendoSourceViewPopup")
101
def do_get_ui_actions(self):
102
return ["JamendoDownloadAlbum","JamendoDonateArtist"]
105
def do_get_status(self, status, progress_text, progress):
107
if self.__load_total_size > 0:
108
progress = min (float(self.__load_current_size) / self.__load_total_size, 1.0)
111
return (_("Loading Jamendo catalog"), None, progress)
113
qm = self.props.query_model
114
return (qm.compute_status_normal("%d song", "%d songs"), None, 2.0)
116
def do_selected(self):
117
if not self.__activated:
118
shell = self.props.shell
119
self.__db = shell.props.db
120
self.__entry_type = self.props.entry_type
122
self.__activated = True
123
self.__show_loading_screen (True)
125
# start our catalogue updates
126
self.__update_id = gobject.timeout_add_seconds(6 * 60 * 60, self.__update_catalogue)
127
self.__update_catalogue()
130
def do_delete_thyself(self):
131
if self.__update_id != 0:
132
gobject.source_remove (self.__update_id)
135
if self.__notify_id != 0:
136
gobject.source_remove (self.__notify_id)
139
if self.__catalogue_loader:
140
self.__catalogue_loader.cancel()
141
self.__catalogue_loader = None
143
if self.__catalogue_check:
144
self.__catalogue_check.cancel()
145
self.__catalogue_check = None
149
# internal catalogue downloading and loading
152
def __catalogue_chunk_cb(self, result, total):
153
if not result or isinstance (result, Exception):
155
# report error somehow?
156
print "error loading catalogue: %s" % result
158
self.__parser.close()
159
self.__db_load_finished = True
160
self.__updating = False
161
self.__saxHandler = None
162
self.__show_loading_screen (False)
163
self.__catalogue_loader = None
166
self.__parser.feed(result)
167
self.__load_current_size += len(result)
168
self.__load_total_size = total
169
self.__notify_status_changed()
171
def __load_catalogue(self):
172
print "loading catalogue %s" % self.__local_catalogue_path
173
self.__notify_status_changed()
174
self.__db_load_finished = False
176
self.__saxHandler = JamendoSaxHandler(self.__db, self.__entry_type)
177
self.__parser = xml.sax.make_parser()
178
self.__parser.setContentHandler(self.__saxHandler)
180
self.__catalogue_loader = rb.ChunkLoader()
181
self.__catalogue_loader.get_url_chunks(self.__local_catalogue_path, 64*1024, True, self.__catalogue_chunk_cb)
184
def __download_catalogue_chunk_cb (self, result, total, out):
186
# done downloading, unzip to real location
188
catalog = gzip.open(self.__local_catalogue_temp)
189
out = open(self.__local_catalogue_path, 'w')
192
s = catalog.read(4096)
199
os.unlink(self.__local_catalogue_temp)
201
self.__db_load_finished = True
202
self.__show_loading_screen (False)
203
self.__catalogue_loader = None
205
self.__load_catalogue ()
207
elif isinstance(result, Exception):
212
self.__load_current_size += len(result)
213
self.__load_total_size = total
215
self.__notify_status_changed()
217
def __download_catalogue(self):
218
print "downloading catalogue"
219
self.__updating = True
220
out = open(self.__local_catalogue_temp, 'w')
222
self.__catalogue_loader = rb.ChunkLoader()
223
self.__catalogue_loader.get_url_chunks(jamendo_song_info_uri, 4*1024, True, self.__download_catalogue_chunk_cb, out)
225
def __update_catalogue(self):
226
def update_cb (result):
227
self.__catalogue_check = None
229
self.__download_catalogue()
230
elif self.__db_load_finished is False:
231
self.__load_catalogue()
233
self.__catalogue_check = rb.UpdateCheck()
234
self.__catalogue_check.check_for_update(self.__local_catalogue_path, jamendo_song_info_uri, update_cb)
237
def __show_loading_screen(self, show):
238
if self.__info_screen is None:
239
# load the builder stuff
240
builder = Gtk.Builder()
241
builder.add_from_file(rb.find_plugin_file(self.props.plugin, "jamendo-loading.ui"))
243
self.__info_screen = builder.get_object("jamendo_loading_scrolledwindow")
244
self.pack_start(self.__info_screen, True, True, 0)
245
self.get_entry_view().set_no_show_all (True)
246
self.__info_screen.set_no_show_all (True)
248
self.__info_screen.set_property("visible", show)
249
self.__paned_box.set_property("visible", not show)
252
def __notify_status_changed(self):
253
def change_idle_cb():
254
self.notify_status_changed()
258
if self.__notify_id == 0:
259
self.__notify_id = gobject.idle_add(change_idle_cb)
263
def download_album (self):
264
tracks = self.get_entry_view().get_selected_entries()
265
format = self.settings['format']
266
if not format or format not in JamendoConfigureDialog.format_list:
269
#TODO: this should work if the album was selected in the browser
270
#without any track selected
273
albumid = track.get_string(RB.RhythmDBPropType.MB_ALBUMID)
276
formats["mp32"] = mp32_uri + albumid
277
formats["ogg3"] = ogg3_uri + albumid
279
p2plink = formats[format]
281
l.get_url(p2plink, self.__download_p2plink, albumid)
283
def __download_p2plink (self, result, albumid):
285
emsg = _("Error looking up p2plink for album %s on jamendo.com") % (albumid)
286
Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, emsg).run()
289
Gtk.show_uri(self.props.shell.props.window.get_screen(), result, Gdk.CURRENT_TIME)
292
def launch_donate (self):
293
tracks = self.get_entry_view().get_selected_entries()
295
#TODO: this should work if the artist was selected in the browser
296
#without any track selected
299
# The Album ID can be used to lookup the artist, and issue a clean redirect.
300
albumid = track.get_string(RB.RhythmDBPropType.MB_ALBUMID)
301
artist = track.get_string(RB.RhythmDBPropType.ARTIST)
302
url = artist_url + albumid.__str__() + "/"
305
l.get_url(url, self.__open_donate, artist)
307
def __open_donate (self, result, artist):
309
emsg = _("Error looking up artist %s on jamendo.com") % (artist)
310
Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, emsg).run()
312
Gtk.show_uri(self.props.shell.props.window.get_screen(), result + "donate/", Gdk.CURRENT_TIME)
314
def playing_entry_changed (self, entry):
315
if not self.__db or not entry:
318
if entry.get_entry_type() != self.__db.entry_type_get_by_name("JamendoEntryType"):
321
gobject.idle_add(self.emit_cover_art_uri, entry)
323
def emit_cover_art_uri (self, entry):
324
stream = self.__db.entry_get_string (entry, RB.RhythmDBPropType.LOCATION)
325
albumid = self.__db.entry_get_string (entry, RB.RhythmDBPropType.MB_ALBUMID)
326
url = artwork_url % albumid
328
self.__db.emit_entry_extra_metadata_notify (entry, "rb:coverArt-uri", str(url))
331
gobject.type_register(JamendoSource)