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

« back to all changes in this revision

Viewing changes to plugins/jamendo/JamendoSource.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
# -*- coding: utf-8 -*-
 
2
 
 
3
# JamendoSource.py
 
4
#
 
5
# Copyright (C) 2007 - Guillaume Desmottes
 
6
#
 
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)
 
10
# any later version.
 
11
#
 
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.
 
16
#
 
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.
 
20
 
 
21
# Parts from "Magnatune Rhythmbox plugin" (stolen from rhythmbox's MagnatuneSource.py)
 
22
#     Copyright (C), 2006 Adam Zimmerman <adam_zimmerman@sfu.ca>
 
23
 
 
24
import os
 
25
import gobject
 
26
import xml
 
27
import gzip
 
28
import datetime
 
29
 
 
30
import rb
 
31
from gi.repository import Gtk, Gdk, Gio
 
32
from gi.repository import RB
 
33
 
 
34
from JamendoSaxHandler import JamendoSaxHandler
 
35
import JamendoConfigureDialog
 
36
 
 
37
# URIs
 
38
 
 
39
jamendo_song_info_uri = "http://img.jamendo.com/data/dbdump_artistalbumtrack.xml.gz"
 
40
 
 
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="
 
43
 
 
44
 
 
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
 
47
 
 
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
 
50
 
 
51
# Album Covers are available here: http://api.jamendo.com/get2/image/album/redirect/?id={ALBUMID}&imagesize={100-600}
 
52
 
 
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/"
 
55
 
 
56
class JamendoSource(RB.BrowserSource):
 
57
 
 
58
        def __init__(self):
 
59
                RB.BrowserSource.__init__(self, name=_("Jamendo"))
 
60
 
 
61
                # catalogue stuff
 
62
                self.hate = self
 
63
                self.__db = None
 
64
                self.__saxHandler = None
 
65
                self.__activated = False
 
66
                self.__notify_id = 0
 
67
                self.__update_id = 0
 
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
 
73
 
 
74
                self.__catalogue_loader = None
 
75
                self.__catalogue_check = None
 
76
 
 
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)
 
80
 
 
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")
 
83
 
 
84
                self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.jamendo")
 
85
 
 
86
        def do_impl_can_delete (self):
 
87
                return False
 
88
 
 
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)
 
93
 
 
94
        #
 
95
        # RBSource methods
 
96
        #
 
97
 
 
98
        def do_impl_show_entry_popup(self):
 
99
                self.show_source_popup ("/JamendoSourceViewPopup")
 
100
 
 
101
        def do_get_ui_actions(self):
 
102
                return ["JamendoDownloadAlbum","JamendoDonateArtist"]
 
103
 
 
104
 
 
105
        def do_get_status(self, status, progress_text, progress):
 
106
                if self.__updating:
 
107
                        if self.__load_total_size > 0:
 
108
                                progress = min (float(self.__load_current_size) / self.__load_total_size, 1.0)
 
109
                        else:
 
110
                                progress = -1.0
 
111
                        return (_("Loading Jamendo catalog"), None, progress)
 
112
                else:
 
113
                        qm = self.props.query_model
 
114
                        return (qm.compute_status_normal("%d song", "%d songs"), None, 2.0)
 
115
 
 
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
 
121
 
 
122
                        self.__activated = True
 
123
                        self.__show_loading_screen (True)
 
124
 
 
125
                        # start our catalogue updates
 
126
                        self.__update_id = gobject.timeout_add_seconds(6 * 60 * 60, self.__update_catalogue)
 
127
                        self.__update_catalogue()
 
128
 
 
129
 
 
130
        def do_delete_thyself(self):
 
131
                if self.__update_id != 0:
 
132
                        gobject.source_remove (self.__update_id)
 
133
                        self.__update_id = 0
 
134
 
 
135
                if self.__notify_id != 0:
 
136
                        gobject.source_remove (self.__notify_id)
 
137
                        self.__notify_id = 0
 
138
 
 
139
                if self.__catalogue_loader:
 
140
                        self.__catalogue_loader.cancel()
 
141
                        self.__catalogue_loader = None
 
142
 
 
143
                if self.__catalogue_check:
 
144
                        self.__catalogue_check.cancel()
 
145
                        self.__catalogue_check = None
 
146
 
 
147
 
 
148
        #
 
149
        # internal catalogue downloading and loading
 
150
        #
 
151
 
 
152
        def __catalogue_chunk_cb(self, result, total):
 
153
                if not result or isinstance (result, Exception):
 
154
                        if result:
 
155
                                # report error somehow?
 
156
                                print "error loading catalogue: %s" % result
 
157
 
 
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
 
164
                        return
 
165
 
 
166
                self.__parser.feed(result)
 
167
                self.__load_current_size += len(result)
 
168
                self.__load_total_size = total
 
169
                self.__notify_status_changed()
 
170
 
 
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
 
175
 
 
176
                self.__saxHandler = JamendoSaxHandler(self.__db, self.__entry_type)
 
177
                self.__parser = xml.sax.make_parser()
 
178
                self.__parser.setContentHandler(self.__saxHandler)
 
179
 
 
180
                self.__catalogue_loader = rb.ChunkLoader()
 
181
                self.__catalogue_loader.get_url_chunks(self.__local_catalogue_path, 64*1024, True, self.__catalogue_chunk_cb)
 
182
 
 
183
 
 
184
        def __download_catalogue_chunk_cb (self, result, total, out):
 
185
                if not result:
 
186
                        # done downloading, unzip to real location
 
187
                        out.close()
 
188
                        catalog = gzip.open(self.__local_catalogue_temp)
 
189
                        out = open(self.__local_catalogue_path, 'w')
 
190
 
 
191
                        while True:
 
192
                                s = catalog.read(4096)
 
193
                                if s == "":
 
194
                                        break
 
195
                                out.write(s)
 
196
 
 
197
                        out.close()
 
198
                        catalog.close()
 
199
                        os.unlink(self.__local_catalogue_temp)
 
200
 
 
201
                        self.__db_load_finished = True
 
202
                        self.__show_loading_screen (False)
 
203
                        self.__catalogue_loader = None
 
204
 
 
205
                        self.__load_catalogue ()
 
206
 
 
207
                elif isinstance(result, Exception):
 
208
                        # complain
 
209
                        pass
 
210
                else:
 
211
                        out.write(result)
 
212
                        self.__load_current_size += len(result)
 
213
                        self.__load_total_size = total
 
214
 
 
215
                self.__notify_status_changed()
 
216
 
 
217
        def __download_catalogue(self):
 
218
                print "downloading catalogue"
 
219
                self.__updating = True
 
220
                out = open(self.__local_catalogue_temp, 'w')
 
221
 
 
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)
 
224
 
 
225
        def __update_catalogue(self):
 
226
                def update_cb (result):
 
227
                        self.__catalogue_check = None
 
228
                        if result is True:
 
229
                                self.__download_catalogue()
 
230
                        elif self.__db_load_finished is False:
 
231
                                self.__load_catalogue()
 
232
 
 
233
                self.__catalogue_check = rb.UpdateCheck()
 
234
                self.__catalogue_check.check_for_update(self.__local_catalogue_path, jamendo_song_info_uri, update_cb)
 
235
 
 
236
 
 
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"))
 
242
 
 
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)
 
247
 
 
248
                self.__info_screen.set_property("visible", show)
 
249
                self.__paned_box.set_property("visible", not show)
 
250
 
 
251
 
 
252
        def __notify_status_changed(self):
 
253
                def change_idle_cb():
 
254
                        self.notify_status_changed()
 
255
                        self.__notify_id = 0
 
256
                        return False
 
257
 
 
258
                if self.__notify_id == 0:
 
259
                        self.__notify_id = gobject.idle_add(change_idle_cb)
 
260
 
 
261
 
 
262
        # Download album
 
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:
 
267
                        format = 'ogg3'
 
268
 
 
269
                #TODO: this should work if the album was selected in the browser
 
270
                #without any track selected
 
271
                if len(tracks) == 1:
 
272
                        track = tracks[0]
 
273
                        albumid = track.get_string(RB.RhythmDBPropType.MB_ALBUMID)
 
274
 
 
275
                        formats = {}
 
276
                        formats["mp32"] = mp32_uri + albumid
 
277
                        formats["ogg3"] = ogg3_uri + albumid
 
278
 
 
279
                        p2plink = formats[format]
 
280
                        l = rb.Loader()
 
281
                        l.get_url(p2plink, self.__download_p2plink, albumid)
 
282
 
 
283
        def __download_p2plink (self, result, albumid):
 
284
                if result is None:
 
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()
 
287
                        return
 
288
 
 
289
                Gtk.show_uri(self.props.shell.props.window.get_screen(), result, Gdk.CURRENT_TIME)
 
290
 
 
291
        # Donate to Artist
 
292
        def launch_donate (self):
 
293
                tracks = self.get_entry_view().get_selected_entries()
 
294
 
 
295
                #TODO: this should work if the artist was selected in the browser
 
296
                #without any track selected
 
297
                if len(tracks) == 1:
 
298
                        track = tracks[0]
 
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__() + "/"
 
303
 
 
304
                        l = rb.Loader()
 
305
                        l.get_url(url, self.__open_donate, artist)
 
306
 
 
307
        def __open_donate (self, result, artist):
 
308
                if result is None:
 
309
                        emsg = _("Error looking up artist %s on jamendo.com") % (artist)
 
310
                        Gtk.MessageDialog(None, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, emsg).run()
 
311
                        return
 
312
                Gtk.show_uri(self.props.shell.props.window.get_screen(), result + "donate/", Gdk.CURRENT_TIME)
 
313
 
 
314
        def playing_entry_changed (self, entry):
 
315
                if not self.__db or not entry:
 
316
                        return
 
317
 
 
318
                if entry.get_entry_type() != self.__db.entry_type_get_by_name("JamendoEntryType"):
 
319
                        return
 
320
 
 
321
                gobject.idle_add(self.emit_cover_art_uri, entry)
 
322
 
 
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
 
327
 
 
328
                self.__db.emit_entry_extra_metadata_notify (entry, "rb:coverArt-uri", str(url))
 
329
                return False
 
330
 
 
331
gobject.type_register(JamendoSource)