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>
30
from gi.repository import GObject, Gtk, Gdk, Gio
31
from gi.repository import RB
33
from JamendoSaxHandler import JamendoSaxHandler
34
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):
58
RB.BrowserSource.__init__(self, name=_("Jamendo"))
63
self.__saxHandler = None
64
self.__activated = False
67
self.__info_screen = None
68
self.__updating = True
69
self.__load_current_size = 0
70
self.__load_total_size = 0
71
self.__db_load_finished = False
73
self.__catalogue_loader = None
74
self.__catalogue_check = None
76
self.__jamendo_dir = RB.find_user_cache_file("jamendo")
77
if os.path.exists(self.__jamendo_dir) is False:
78
os.makedirs(self.__jamendo_dir, 0700)
80
self.__local_catalogue_path = os.path.join(self.__jamendo_dir, "dbdump.xml")
81
self.__local_catalogue_temp = os.path.join(self.__jamendo_dir, "dbdump.xml.tmp")
83
self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.jamendo")
85
def do_impl_can_delete (self):
88
def do_impl_pack_paned (self, paned):
89
self.__paned_box = Gtk.VBox(homogeneous=False, spacing=5)
90
self.pack_start(self.__paned_box, True, True, 0)
91
self.__paned_box.pack_start(paned, True, True, 0)
97
def do_impl_show_entry_popup(self):
98
self.show_source_popup ("/JamendoSourceViewPopup")
100
def do_get_ui_actions(self):
101
return ["JamendoDownloadAlbum","JamendoDonateArtist"]
104
def do_get_status(self, status, progress_text, progress):
106
if self.__load_total_size > 0:
107
progress = min (float(self.__load_current_size) / self.__load_total_size, 1.0)
110
return (_("Loading Jamendo catalog"), None, progress)
112
qm = self.props.query_model
113
return (qm.compute_status_normal("%d song", "%d songs"), None, 2.0)
115
def do_selected(self):
116
if not self.__activated:
117
shell = self.props.shell
118
self.__db = shell.props.db
119
self.__entry_type = self.props.entry_type
121
self.__activated = True
122
self.__show_loading_screen (True)
124
# start our catalogue updates
125
self.__update_id = GObject.timeout_add_seconds(6 * 60 * 60, self.__update_catalogue)
126
self.__update_catalogue()
129
def do_delete_thyself(self):
130
if self.__update_id != 0:
131
GObject.source_remove (self.__update_id)
134
if self.__notify_id != 0:
135
GObject.source_remove (self.__notify_id)
138
if self.__catalogue_loader:
139
self.__catalogue_loader.cancel()
140
self.__catalogue_loader = None
142
if self.__catalogue_check:
143
self.__catalogue_check.cancel()
144
self.__catalogue_check = None
148
# internal catalogue downloading and loading
151
def __catalogue_chunk_cb(self, result, total):
152
if not result or isinstance (result, Exception):
154
# report error somehow?
155
print "error loading catalogue: %s" % result
157
self.__parser.close()
158
self.__db_load_finished = True
159
self.__updating = False
160
self.__saxHandler = None
161
self.__show_loading_screen (False)
162
self.__catalogue_loader = None
165
self.__parser.feed(result)
166
self.__load_current_size += len(result)
167
self.__load_total_size = total
168
self.__notify_status_changed()
170
def __load_catalogue(self):
171
print "loading catalogue %s" % self.__local_catalogue_path
172
self.__notify_status_changed()
173
self.__db_load_finished = False
175
self.__saxHandler = JamendoSaxHandler(self.__db, self.__entry_type)
176
self.__parser = xml.sax.make_parser()
177
self.__parser.setContentHandler(self.__saxHandler)
179
self.__catalogue_loader = rb.ChunkLoader()
180
self.__catalogue_loader.get_url_chunks(self.__local_catalogue_path, 64*1024, True, self.__catalogue_chunk_cb)
183
def __download_catalogue_chunk_cb (self, result, total, out):
185
# done downloading, unzip to real location
187
catalog = gzip.open(self.__local_catalogue_temp)
188
out = open(self.__local_catalogue_path, 'w')
191
s = catalog.read(4096)
198
os.unlink(self.__local_catalogue_temp)
200
self.__db_load_finished = True
201
self.__show_loading_screen (False)
202
self.__catalogue_loader = None
204
self.__load_catalogue ()
206
elif isinstance(result, Exception):
211
self.__load_current_size += len(result)
212
self.__load_total_size = total
214
self.__notify_status_changed()
216
def __download_catalogue(self):
217
print "downloading catalogue"
218
self.__updating = True
219
out = open(self.__local_catalogue_temp, 'w')
221
self.__catalogue_loader = rb.ChunkLoader()
222
self.__catalogue_loader.get_url_chunks(jamendo_song_info_uri, 4*1024, True, self.__download_catalogue_chunk_cb, out)
224
def __update_catalogue(self):
225
def update_cb (result):
226
self.__catalogue_check = None
228
self.__download_catalogue()
229
elif self.__db_load_finished is False:
230
self.__load_catalogue()
232
self.__catalogue_check = rb.UpdateCheck()
233
self.__catalogue_check.check_for_update(self.__local_catalogue_path, jamendo_song_info_uri, update_cb)
236
def __show_loading_screen(self, show):
237
if self.__info_screen is None:
238
# load the builder stuff
239
builder = Gtk.Builder()
240
builder.add_from_file(rb.find_plugin_file(self.props.plugin, "jamendo-loading.ui"))
242
self.__info_screen = builder.get_object("jamendo_loading_scrolledwindow")
243
self.pack_start(self.__info_screen, True, True, 0)
244
self.get_entry_view().set_no_show_all (True)
245
self.__info_screen.set_no_show_all (True)
247
self.__info_screen.set_property("visible", show)
248
self.__paned_box.set_property("visible", not show)
251
def __notify_status_changed(self):
252
def change_idle_cb():
253
self.notify_status_changed()
257
if self.__notify_id == 0:
258
self.__notify_id = GObject.idle_add(change_idle_cb)
262
def download_album (self):
263
tracks = self.get_entry_view().get_selected_entries()
264
format = self.settings['format']
265
if not format or format not in JamendoConfigureDialog.format_list:
268
#TODO: this should work if the album was selected in the browser
269
#without any track selected
272
albumid = track.get_string(RB.RhythmDBPropType.MB_ALBUMID)
275
formats["mp32"] = mp32_uri + albumid
276
formats["ogg3"] = ogg3_uri + albumid
278
p2plink = formats[format]
280
l.get_url(p2plink, self.__download_p2plink, albumid)
282
def __download_p2plink (self, result, albumid):
284
emsg = _("Error looking up p2plink for album %s on jamendo.com") % (albumid)
285
Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, emsg).run()
288
Gtk.show_uri(self.props.shell.props.window.get_screen(), result, Gdk.CURRENT_TIME)
291
def launch_donate (self):
292
tracks = self.get_entry_view().get_selected_entries()
294
#TODO: this should work if the artist was selected in the browser
295
#without any track selected
298
# The Album ID can be used to lookup the artist, and issue a clean redirect.
299
albumid = track.get_string(RB.RhythmDBPropType.MB_ALBUMID)
300
artist = track.get_string(RB.RhythmDBPropType.ARTIST)
301
url = artist_url + albumid.__str__() + "/"
304
l.get_url(url, self.__open_donate, artist)
306
def __open_donate (self, result, artist):
308
emsg = _("Error looking up artist %s on jamendo.com") % (artist)
309
Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, emsg).run()
311
Gtk.show_uri(self.props.shell.props.window.get_screen(), result + "donate/", Gdk.CURRENT_TIME)
313
def playing_entry_changed (self, entry):
314
if not self.__db or not entry:
317
if entry.get_entry_type() != self.__db.entry_type_get_by_name("JamendoEntryType"):
320
GObject.idle_add(self.emit_cover_art_uri, entry)
322
def emit_cover_art_uri (self, entry):
323
stream = self.__db.entry_get_string (entry, RB.RhythmDBPropType.LOCATION)
324
albumid = self.__db.entry_get_string (entry, RB.RhythmDBPropType.MB_ALBUMID)
325
url = artwork_url % albumid
327
self.__db.emit_entry_extra_metadata_notify (entry, "rb:coverArt-uri", str(url))
330
GObject.type_register(JamendoSource)