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

« back to all changes in this revision

Viewing changes to plugins/magnatune/magnatune.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
# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
 
2
#
 
3
# Copyright (C) 2006 Adam Zimmerman  <adam_zimmerman@sfu.ca>
 
4
# Copyright (C) 2006 James Livingston  <doclivingston@gmail.com>
 
5
#
 
6
# This program is free software; you can redistribute it and/or modify
 
7
# it under the terms of the GNU General Public License as published by
 
8
# the Free Software Foundation; either version 2, or (at your option)
 
9
# any later version.
 
10
#
 
11
# The Rhythmbox authors hereby grant permission for non-GPL compatible
 
12
# GStreamer plugins to be used and distributed together with GStreamer
 
13
# and Rhythmbox. This permission is above and beyond the permissions granted
 
14
# by the GPL license by which Rhythmbox is covered. If you modify this code
 
15
# you may extend this exception to your version of the code, but you are not
 
16
# obligated to do so. If you do not wish to do so, delete this exception
 
17
# statement from your version.
 
18
#
 
19
# This program is distributed in the hope that it will be useful,
 
20
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
22
# GNU General Public License for more details.
 
23
#
 
24
# You should have received a copy of the GNU General Public License
 
25
# along with this program; if not, write to the Free Software
 
26
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 
27
 
 
28
import gobject
 
29
 
 
30
import urllib
 
31
import zipfile
 
32
import sys, os.path
 
33
import xml
 
34
import datetime
 
35
import string
 
36
import gnomekeyring as keyring
 
37
 
 
38
import rb
 
39
from gi.repository import RB
 
40
from gi.repository import Gtk, Gio, Peas, PeasGtk
 
41
# XXX use GnomeKeyring when available
 
42
 
 
43
from MagnatuneSource import MagnatuneSource
 
44
 
 
45
popup_ui = """
 
46
<ui>
 
47
  <popup name="MagnatuneSourceViewPopup">
 
48
    <menuitem name="AddToQueueLibraryPopup" action="AddToQueue"/>
 
49
    <menuitem name="MagnatuneDownloadAlbum" action="MagnatuneDownloadAlbum"/>
 
50
    <menuitem name="MagnatuneArtistInfo" action="MagnatuneArtistInfo"/>
 
51
    <menuitem name="MagnatuneCancelDownload" action="MagnatuneCancelDownload"/>
 
52
    <separator/>
 
53
    <menuitem name="BrowseGenreLibraryPopup" action="BrowserSrcChooseGenre"/>
 
54
    <menuitem name="BrowseArtistLibraryPopup" action="BrowserSrcChooseArtist"/>
 
55
    <menuitem name="BrowseAlbumLibraryPopup" action="BrowserSrcChooseAlbum"/>
 
56
    <separator/>
 
57
    <menuitem name="PropertiesLibraryPopup" action="MusicProperties"/>
 
58
  </popup>
 
59
</ui>
 
60
"""
 
61
 
 
62
class MagnatuneEntryType(RB.RhythmDBEntryType):
 
63
        def __init__(self):
 
64
                RB.RhythmDBEntryType.__init__(self, name='magnatune')
 
65
 
 
66
        def can_sync_metadata(self, entry):
 
67
                return True
 
68
 
 
69
        def sync_metadata(self, entry, changes):
 
70
                return
 
71
 
 
72
class Magnatune(gobject.GObject, Peas.Activatable):
 
73
        __gtype_name__ = 'Magnatune'
 
74
        object = gobject.property(type=gobject.GObject)
 
75
 
 
76
        format_list = ['ogg', 'flac', 'wav', 'mp3-vbr', 'mp3-cbr']
 
77
 
 
78
        def __init__(self):
 
79
                gobject.GObject.__init__(self)
 
80
 
 
81
        def do_activate(self):
 
82
                shell = self.object
 
83
                self.db = shell.props.db
 
84
 
 
85
                self.entry_type = MagnatuneEntryType()
 
86
                self.db.register_entry_type(self.entry_type)
 
87
 
 
88
                self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.magnatune")
 
89
 
 
90
                theme = Gtk.IconTheme.get_default()
 
91
                rb.append_plugin_source_path(theme, "/icons")
 
92
 
 
93
                what, width, height = Gtk.icon_size_lookup(Gtk.IconSize.LARGE_TOOLBAR)
 
94
                icon = rb.try_load_icon(theme, "magnatune", width, 0)
 
95
 
 
96
                group = RB.DisplayPageGroup.get_by_id ("stores")
 
97
                settings = Gio.Settings("org.gnome.rhythmbox.plugins.magnatune")
 
98
                self.source = gobject.new(MagnatuneSource,
 
99
                                          shell=shell,
 
100
                                          entry_type=self.entry_type,
 
101
                                          pixbuf=icon,
 
102
                                          plugin=self,
 
103
                                          settings=settings.get_child("source"),
 
104
                                          name=_("Magnatune"))
 
105
 
 
106
                shell.register_entry_type_for_source(self.source, self.entry_type)
 
107
                shell.append_display_page(self.source, group)
 
108
 
 
109
                manager = shell.props.ui_manager
 
110
                # Add the popup menu actions
 
111
                self.action_group = Gtk.ActionGroup(name='MagnatunePluginActions')
 
112
 
 
113
                action = Gtk.Action(name='MagnatuneDownloadAlbum', label=_('Download Album'),
 
114
                                tooltip=_("Download this album from Magnatune"),
 
115
                                stock_id='gtk-save')
 
116
                action.connect('activate', lambda a: shell.props.selected_page.download_album())
 
117
                self.action_group.add_action(action)
 
118
                action = Gtk.Action(name='MagnatuneArtistInfo', label=_('Artist Information'),
 
119
                                tooltip=_("Get information about this artist"),
 
120
                                stock_id='gtk-info')
 
121
                action.connect('activate', lambda a: shell.props.selected_page.display_artist_info())
 
122
                self.action_group.add_action(action)
 
123
                action = Gtk.Action(name='MagnatuneCancelDownload', label=_('Cancel Downloads'),
 
124
                                tooltip=_("Stop downloading purchased albums"),
 
125
                                stock_id='gtk-stop')
 
126
                action.connect('activate', lambda a: shell.props.selected_page.cancel_downloads())
 
127
                action.set_sensitive(False)
 
128
                self.action_group.add_action(action)
 
129
 
 
130
                manager.insert_action_group(self.action_group, 0)
 
131
                self.ui_id = manager.add_ui_from_string(popup_ui)
 
132
 
 
133
                self.pec_id = shell.props.shell_player.connect('playing-song-changed', self.playing_entry_changed)
 
134
                manager.ensure_update()
 
135
 
 
136
        def do_deactivate(self):
 
137
                shell = self.object
 
138
                manager = shell.props.ui_manager
 
139
                manager.remove_ui(self.ui_id)
 
140
                manager.remove_action_group(self.action_group)
 
141
                self.action_group = None
 
142
 
 
143
                shell.props.shell_player.disconnect(self.pec_id)
 
144
 
 
145
                self.db.entry_delete_by_type(self.entry_type)
 
146
                self.db.commit()
 
147
                self.db = None
 
148
                self.entry_type = None
 
149
                self.source.delete_thyself()
 
150
                self.source = None
 
151
 
 
152
        def playing_entry_changed (self, sp, entry):
 
153
                self.source.playing_entry_changed(entry)
 
154
 
 
155
 
 
156
class MagnatuneConfig(gobject.GObject, PeasGtk.Configurable):
 
157
        __gtype_name__ = 'MagnatuneConfig'
 
158
        object = gobject.property(type=gobject.GObject)
 
159
 
 
160
        def __init__(self):
 
161
                gobject.GObject.__init__(self)
 
162
 
 
163
        def do_create_configure_widget(self):
 
164
                # We use a dictionary so we can modify these values from within inner functions
 
165
                keyring_data = {
 
166
                        'id': 0,
 
167
                        'item': None
 
168
                }
 
169
 
 
170
                def got_items(result, items):
 
171
                        def created_item(result, id):
 
172
                                if result is None: # Item successfully created
 
173
                                        keyring_data['id'] = id
 
174
                                        keyring.item_get_info(None, id, got_item)
 
175
                                else:
 
176
                                        print "Couldn't create keyring item: " + str(result)
 
177
                                        fill_account_details()
 
178
                                        dialog.present()
 
179
                        def got_item(result, item):
 
180
                                if result is None: # Item retrieved successfully
 
181
                                        keyring_data['item'] = item
 
182
                                else:
 
183
                                        print "Couldn't retrieve keyring item: " + str(result)
 
184
                                fill_account_details()
 
185
                                dialog.present()
 
186
 
 
187
 
 
188
                        if result is None and len(items) != 0: # Got list of search results
 
189
                                keyring_data['id'] = items[0].item_id
 
190
                                keyring.item_get_info(None, keyring_data['id'], got_item)
 
191
                        elif result == keyring.NoMatchError or len(items) == 0: # No items were found, so we'll create one
 
192
                                keyring.item_create(None,
 
193
                                                    keyring.ITEM_GENERIC_SECRET,
 
194
                                                    "Rhythmbox: Magnatune account information",
 
195
                                                    {'rhythmbox-plugin': 'magnatune'},
 
196
                                                    "", # Empty secret for now
 
197
                                                    True,
 
198
                                                    created_item)
 
199
                        else: # Some other error occurred
 
200
                                print "Couldn't access keyring: " + str(result)
 
201
                                fill_account_details()
 
202
                                dialog.present()
 
203
 
 
204
 
 
205
                def fill_account_details():
 
206
                        account_type = self.settings['account_type']
 
207
                        builder.get_object("no_account_radio").set_active(account_type == "none")
 
208
                        builder.get_object("stream_account_radio").set_active(account_type == "stream")
 
209
                        builder.get_object("download_account_radio").set_active(account_type == "download")
 
210
 
 
211
                        username = ""
 
212
                        password = ""
 
213
                        try:
 
214
                                if keyring_data['item']:
 
215
                                        username, password = keyring_data['item'].get_secret().split('\n')
 
216
                        except ValueError: # Couldn't parse the secret, probably because it's empty
 
217
                                pass
 
218
                        builder.get_object("username_entry").set_text(username)
 
219
                        builder.get_object("password_entry").set_text(password)
 
220
 
 
221
                        has_account = account_type != "none"
 
222
                        builder.get_object("username_entry").set_sensitive(has_account)
 
223
                        builder.get_object("password_entry").set_sensitive(has_account)
 
224
                        builder.get_object("username_label").set_sensitive(has_account)
 
225
                        builder.get_object("password_label").set_sensitive(has_account)
 
226
 
 
227
                        builder.get_object("account_changed_label").hide()
 
228
 
 
229
                def account_type_toggled (button):
 
230
                        print "account type radiobutton toggled: " + button.get_name()
 
231
                        account_type = {"no_account_radio": "none", "stream_account_radio": "stream", "download_account_radio": "download"}
 
232
                        if button.get_active():
 
233
                                self.settings['account_type'] = account_type[button.get_name()]
 
234
                                if account_type[button.get_name()] == 'none':
 
235
                                        builder.get_object("username_label").set_sensitive(False)
 
236
                                        builder.get_object("username_entry").set_sensitive(False)
 
237
                                        builder.get_object("password_label").set_sensitive(False)
 
238
                                        builder.get_object("password_entry").set_sensitive(False)
 
239
                                else:
 
240
                                        builder.get_object("username_label").set_sensitive(True)
 
241
                                        builder.get_object("username_entry").set_sensitive(True)
 
242
                                        builder.get_object("password_label").set_sensitive(True)
 
243
                                        builder.get_object("password_entry").set_sensitive(True)
 
244
                                builder.get_object("account_changed_label").show()
 
245
 
 
246
                def account_details_changed(entry):
 
247
                        username = builder.get_object("username_entry").get_text()
 
248
                        password = builder.get_object("password_entry").get_text()
 
249
                        if keyring_data['item']:
 
250
                                keyring_data['item'].set_secret('\n'.join((username, password)))
 
251
 
 
252
                        builder.get_object("account_changed_label").show()
 
253
 
 
254
                def close_button_pressed(x, y):
 
255
                        try:
 
256
                                if keyring_data['id'] and keyring_data['item']:
 
257
                                        # The async version is not in the python bindings, grr...
 
258
                                        keyring.item_set_info_sync(None, keyring_data['id'], keyring_data['item'])
 
259
                                else:
 
260
                                        RB.error_dialog(title = _("Couldn't store account information"),
 
261
                                                        message = _("There was a problem accessing the keyring. Check the debug output for more information."))
 
262
                        except Exception, e:
 
263
                                RB.error_dialog(title = _("Couldn't store account information"),
 
264
                                                message = str(e))
 
265
                        dialog.hide()
 
266
 
 
267
                def format_selection_changed(self, button):
 
268
                        self.settings['format'] = self.format_list[button.get_active()]
 
269
 
 
270
                self.configure_callback_dic = {
 
271
                        "rb_magnatune_audio_combobox_changed_cb" : self.format_selection_changed,
 
272
                        "rb_magnatune_radio_account_toggled_cb" : account_type_toggled,
 
273
                        "rb_magnatune_username_changed_cb" : account_details_changed,
 
274
                        "rb_magnatune_password_changed_cb" : account_details_changed
 
275
                }
 
276
 
 
277
                builder = Gtk.Builder()
 
278
                builder.add_from_file(rb.find_plugin_file(self, "magnatune-prefs.ui"))
 
279
 
 
280
                dialog = builder.get_object('preferences_dialog')
 
281
 
 
282
                # Set the names of the radio buttons so we can tell which one has been clicked
 
283
                for name in ("no_account_radio", "stream_account_radio", "download_account_radio"):
 
284
                        builder.get_object(name).set_name(name)
 
285
 
 
286
                builder.get_object("audio_combobox").set_active(self.format_list.index(self.settings['format']))
 
287
 
 
288
                builder.connect_signals(self.configure_callback_dic)
 
289
                dialog.connect("response", close_button_pressed)
 
290
 
 
291
                keyring.find_items(keyring.ITEM_GENERIC_SECRET, {'rhythmbox-plugin': 'magnatune'}, got_items)
 
292
                return dialog