~ubuntu-branches/ubuntu/quantal/rhythmbox/quantal-proposed

« back to all changes in this revision

Viewing changes to plugins/jamendo/JamendoSource.py

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher, Rico Tzschichholz
  • Date: 2011-12-05 19:31:23 UTC
  • mfrom: (1.1.60)
  • Revision ID: package-import@ubuntu.com-20111205193123-89047p8yplb0w1vx
Tags: 2.90.1~20111126.89c872b0-0ubuntu1
* Upload the new version to Ubuntu, should solve those issues:
  - the lack of rhythmbox-client command (lp: #875064)
  - the music sharing preferences dialog (lp: #894153)
  - several segfaults (lp: #859195, #814614)
* debian/control.in:
  - let the rhythmbox gir depends on gir1.2-peas-1.0 (lp: #874973)

[ Rico Tzschichholz ]
* New upstream git snapshot

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