Commit 53563b04 authored by Jonathan Matthew's avatar Jonathan Matthew

magnatune: use gnome-keyring via g-i, other fixes (bug #661957)

The plugin now works well enough to be enabled again.  Some things
probably still don't work.
parent 391c9836
......@@ -31,12 +31,10 @@ import urllib
import urlparse
import threading
import zipfile
import gnomekeyring as keyring
import rb
from gi.repository import RB
from gi.repository import GObject, Gtk, Gio
# XXX use GnomeKeyring when introspection is available
from gi.repository import GObject, Gtk, Gdk, Gio, GnomeKeyring
from TrackListHandler import TrackListHandler
from BuyAlbumHandler import BuyAlbumHandler, MagnatunePurchaseError
......@@ -47,7 +45,7 @@ gettext.install('rhythmbox', RB.locale_dir())
magnatune_partner_id = "rhythmbox"
# URIs
magnatune_song_info_uri = Gio.file_new_for_uri("http://magnatune.com/info/song_info_xml.zip")
magnatune_song_info_uri = "http://magnatune.com/info/song_info_xml.zip"
magnatune_buy_album_uri = "https://magnatune.com/buy/choose?"
magnatune_api_download_uri = "http://%s:%s@download.magnatune.com/buy/membership_free_dl_xml?"
......@@ -87,7 +85,7 @@ class MagnatuneSource(RB.BrowserSource):
self.__downloads = {} # keeps track of download progress for each file
self.__cancellables = {} # keeps track of Gio.Cancellable objects so we can abort album downloads
self.__art_store = RB.ExtDB("album-art")
self.__art_store = RB.ExtDB(name="album-art")
#
# RBSource methods
......@@ -115,11 +113,6 @@ class MagnatuneSource(RB.BrowserSource):
qm = self.props.query_model
return (qm.compute_status_normal("%d song", "%d songs"), None, 2.0)
def do_get_ui_actions(self):
return ["MagnatuneDownloadAlbum",
"MagnatuneArtistInfo",
"MagnatuneCancelDownload"]
def do_selected(self):
if not self.__activated:
shell = self.props.shell
......@@ -144,10 +137,10 @@ class MagnatuneSource(RB.BrowserSource):
def do_impl_can_delete(self):
return False
def do_impl_pack_paned(self, paned):
def do_pack_content(self, content):
self.__paned_box = Gtk.VBox(homogeneous=False, spacing=5)
self.pack_start(self.__paned_box, True, True, 0)
self.__paned_box.pack_start(paned, True, True, 0)
self.__paned_box.pack_start(content, True, True, 0)
def do_delete_thyself(self):
......@@ -179,7 +172,7 @@ class MagnatuneSource(RB.BrowserSource):
urls = set([])
for tr in tracks:
sku = self.__sku_dict[self.__db.entry_get_string(tr, RB.RhythmDBPropType.LOCATION)]
sku = self.__sku_dict[tr.get_string(RB.RhythmDBPropType.LOCATION)]
url = self.__home_dict[sku]
if url not in urls:
Gtk.show_uri(screen, url, Gdk.CURRENT_TIME)
......@@ -191,14 +184,14 @@ class MagnatuneSource(RB.BrowserSource):
urls = set([])
for tr in tracks:
sku = self.__sku_dict[self.__db.entry_get_string(tr, RB.RhythmDBPropType.LOCATION)]
sku = self.__sku_dict[tr.get_string(RB.RhythmDBPropType.LOCATION)]
url = magnatune_buy_album_uri + urllib.urlencode({ 'sku': sku, 'ref': magnatune_partner_id })
if url not in urls:
Gtk.show_uri(screen, url, Gdk.CURRENT_TIME)
urls.add(url)
def download_album(self):
if selt.__settings['account_type'] != 'download':
if selt.__settings['account-type'] != 'download':
# The user doesn't have a download account, so redirect them to the purchase page.
self.purchase_redirect()
return
......@@ -216,7 +209,7 @@ class MagnatuneSource(RB.BrowserSource):
skus = []
for track in tracks:
sku = self.__sku_dict[self.__db.entry_get_string(track, RB.RhythmDBPropType.LOCATION)]
sku = self.__sku_dict[track.get_string(RB.RhythmDBPropType.LOCATION)]
if sku in skus:
continue
skus.append(sku)
......@@ -241,19 +234,17 @@ class MagnatuneSource(RB.BrowserSource):
return info.filename;
return None
def download_progress(complete, total):
def download_progress(copy, complete, total, self):
self.__load_progress = (complete, total)
self.__notify_status_changed()
def download_finished(uri, result):
try:
success = uri.copy_finish(result)
except:
success = False
def download_finished(copy, success, self):
if not success:
print "catalog download failed"
print copy.get_error()
return
print "catalog download successful"
# done downloading, unzip to real location
catalog_zip = zipfile.ZipFile(magnatune_song_info_temp)
catalog = open(magnatune_song_info, 'w')
......@@ -266,7 +257,8 @@ class MagnatuneSource(RB.BrowserSource):
catalog.close()
catalog_zip.close()
dest.delete()
df = Gio.file_new_for_path(magnatune_song_info_temp)
df.delete(None)
self.__updating = False
self.__catalogue_loader = None
self.__notify_status_changed()
......@@ -276,46 +268,23 @@ class MagnatuneSource(RB.BrowserSource):
self.__updating = True
dest = Gio.file_new_for_path(magnatune_song_info_temp)
self.__catalogue_loader = Gio.Cancellable()
try:
# For some reason, Gio.FileCopyFlags.OVERWRITE doesn't work for copy_async
dest.delete()
df = Gio.file_new_for_path(magnatune_song_info_temp)
df.delete(None)
except:
pass
magnatune_song_info_uri.copy_async(dest,
download_finished,
progress_callback=download_progress,
flags=Gio.FileCopyFlags.OVERWRITE,
cancellable=self.__catalogue_loader)
self.__catalog_loader = RB.AsyncCopy()
self.__catalog_loader.set_progress(download_progress, self)
self.__catalog_loader.start(magnatune_song_info_uri, magnatune_song_info_temp, download_finished, self)
def load_catalogue():
def got_items(result, items):
account_type = self.__settings['account_type']
username = ""
password = ""
if account_type == 'none':
pass
elif result is not None or len(items) == 0:
RB.error_dialog(title = _("Couldn't get account details"),
message = str(result))
return
else:
try:
username, password = items[0].secret.split('\n')
except ValueError: # Couldn't parse secret, possibly because it's empty
pass
parser = xml.sax.make_parser()
parser.setContentHandler(TrackListHandler(self.__db, self.__entry_type, self.__sku_dict, self.__home_dict, self.__art_dict, account_type, username, password))
self.__catalogue_loader = rb.ChunkLoader()
self.__catalogue_loader.get_url_chunks(magnatune_song_info, 64*1024, True, catalogue_chunk_cb, parser)
def catalogue_chunk_cb(result, total, parser):
if not result or isinstance(result, Exception):
if result:
def catalogue_chunk_cb(loader, data, total, parser):
if data is None:
error = loader.get_error()
if error:
# report error somehow?
print "error loading catalogue: %s" % result
print "error loading catalogue: %s" % error
try:
parser.close()
......@@ -329,7 +298,7 @@ class MagnatuneSource(RB.BrowserSource):
# restart in-progress downloads
# (doesn't really belong here)
for f in magnatune_in_progress_dir.enumerate_children('standard::name'):
for f in magnatune_in_progress_dir.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, None):
name = f.get_name()
if not name.startswith("in_progress_"):
continue
......@@ -338,18 +307,19 @@ class MagnatuneSource(RB.BrowserSource):
self.__download_album(Gio.file_new_for_uri(uri), name[12:])
else:
# hack around some weird chars that show up in the catalogue for some reason
result = result.replace("\x19", "'")
result = result.replace("\x13", "-")
data = str(data.str)
data = data.replace("\x19", "'")
data = data.replace("\x13", "-")
# argh.
result = result.replace("Rock & Roll", "Rock & Roll")
data = data.replace("Rock & Roll", "Rock & Roll")
try:
parser.feed(result)
parser.feed(data)
except xml.sax.SAXParseException, e:
print "error parsing catalogue: %s" % e
load_size['size'] += len(result)
load_size['size'] += len(data)
self.__load_progress = (load_size['size'], total)
self.__notify_status_changed()
......@@ -361,11 +331,34 @@ class MagnatuneSource(RB.BrowserSource):
self.__notify_status_changed()
load_size = {'size': 0}
keyring.find_items(keyring.ITEM_GENERIC_SECRET, {'rhythmbox-plugin': 'magnatune'}, got_items)
account_type = self.__settings['account-type']
username = ""
password = ""
if account_type != 'none':
attributes = GnomeKeyring.attribute_list_new()
GnomeKeyring.attribute_list_append_string(attributes, "rhythmbox-plugin", "magnatune")
(result, items) = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.GENERIC_SECRET, attributes)
if result is not GnomeKeyring.Result.OK or len(items) == 0:
RB.error_dialog(title = _("Couldn't get account details"),
message = GnomeKeyring.result_to_message(result))
account_type = 'none'
else:
try:
username, password = items[0].get_secret().split('\n')
except ValueError: # Couldn't parse secret, possibly because it's empty
account_type = 'none'
parser = xml.sax.make_parser()
parser.setContentHandler(TrackListHandler(self.__db, self.__entry_type, self.__sku_dict, self.__home_dict, self.__art_dict, account_type, username, password))
self.__catalogue_loader = RB.ChunkLoader()
self.__catalogue_loader.set_callback(catalogue_chunk_cb, parser)
self.__catalogue_loader.start(magnatune_song_info, 64*1024)
self.__catalogue_check = rb.UpdateCheck()
self.__catalogue_check.check_for_update(magnatune_song_info, magnatune_song_info_uri.get_uri(), update_cb)
self.__catalogue_check.check_for_update(magnatune_song_info, magnatune_song_info_uri, update_cb)
def __show_loading_screen(self, show):
......
# Magnatune Store Python Plugin
# this plugin is disabled until we can access gnome-keyring
plugindir = $(PLUGINDIR)/magnatune
plugindatadir = $(PLUGINDATADIR)/magnatune
noinst_PYTHON = \
plugin_PYTHON = \
MagnatuneSource.py \
BuyAlbumHandler.py \
TrackListHandler.py \
magnatune.py
# %.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
# plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
%.plugin: %.plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
plugin_DATA = $(plugin_in_files:.plugin.in=.plugin)
plugin_in_files = magnatune.plugin.in
gtkbuilderdir = $(plugindatadir)
noinst_DATA = \
gtkbuilder_DATA = \
magnatune-loading.ui \
magnatune-prefs.ui \
magnatune_logo_color_small.png \
......@@ -26,8 +24,8 @@ themedir = $(pkgdatadir)/icons/hicolor
size = 24x24
context = places
icondir = $(themedir)/$(size)/$(context)
noinst_DATA += icons/hicolor/$(size)/$(context)/magnatune.png
icon_DATA = icons/hicolor/$(size)/$(context)/magnatune.png
EXTRA_DIST = $(plugin_in_files) $(gtkbuilder_DATA) $(icon_DATA) $(noinst_DATA)
EXTRA_DIST = $(plugin_in_files) $(gtkbuilder_DATA) $(icon_DATA)
CLEANFILES = $(plugin_DATA)
DISTCLEANFILES = $(plugin_DATA)
......@@ -31,12 +31,10 @@ import sys, os.path
import xml
import datetime
import string
import gnomekeyring as keyring
import rb
from gi.repository import RB
from gi.repository import GObject, Gtk, Gio, Peas, PeasGtk
# XXX use GnomeKeyring when available
from gi.repository import GObject, Gtk, Gio, Peas, PeasGtk, GnomeKeyring
from MagnatuneSource import MagnatuneSource
......@@ -57,6 +55,11 @@ popup_ui = """
<separator/>
<menuitem name="PropertiesLibraryPopup" action="MusicProperties"/>
</popup>
<toolbar name="MagnatuneToolBar">
<toolitem name="MagnatuneDownloadAlbumToolbar" action="MagnatuneDownloadAlbum"/>
<toolitem name="MagnatuneArtistInfoToolbar" action="MagnatuneArtistInfo"/>
<toolitem name="MagnatuneCancelDownloadToolbar" action="MagnatuneCancelDownload"/>
</toolbar>
</ui>
"""
......@@ -102,7 +105,8 @@ class Magnatune(GObject.GObject, Peas.Activatable):
pixbuf=icon,
plugin=self,
settings=settings.get_child("source"),
name=_("Magnatune"))
name=_("Magnatune"),
toolbar_path="/MagnatuneToolBar")
shell.register_entry_type_for_source(self.source, self.entry_type)
shell.append_display_page(self.source, group)
......@@ -168,41 +172,6 @@ class MagnatuneConfig(GObject.GObject, PeasGtk.Configurable):
'item': None
}
def got_items(result, items):
def created_item(result, id):
if result is None: # Item successfully created
keyring_data['id'] = id
keyring.item_get_info(None, id, got_item)
else:
print "Couldn't create keyring item: " + str(result)
fill_account_details()
dialog.present()
def got_item(result, item):
if result is None: # Item retrieved successfully
keyring_data['item'] = item
else:
print "Couldn't retrieve keyring item: " + str(result)
fill_account_details()
dialog.present()
if result is None and len(items) != 0: # Got list of search results
keyring_data['id'] = items[0].item_id
keyring.item_get_info(None, keyring_data['id'], got_item)
elif result == keyring.NoMatchError or len(items) == 0: # No items were found, so we'll create one
keyring.item_create(None,
keyring.ITEM_GENERIC_SECRET,
"Rhythmbox: Magnatune account information",
{'rhythmbox-plugin': 'magnatune'},
"", # Empty secret for now
True,
created_item)
else: # Some other error occurred
print "Couldn't access keyring: " + str(result)
fill_account_details()
dialog.present()
def fill_account_details():
account_type = self.settings['account_type']
builder.get_object("no_account_radio").set_active(account_type == "none")
......@@ -253,16 +222,16 @@ class MagnatuneConfig(GObject.GObject, PeasGtk.Configurable):
builder.get_object("account_changed_label").show()
def close_button_pressed(x, y):
try:
if keyring_data['id'] and keyring_data['item']:
# The async version is not in the python bindings, grr...
keyring.item_set_info_sync(None, keyring_data['id'], keyring_data['item'])
else:
if keyring_data['id'] and keyring_data['item']:
result = GnomeKeyring.item_set_info_sync(None,
keyring_data['id'],
keyring_data['item'])
if result != GnomeKeyring.Result.OK:
RB.error_dialog(title = _("Couldn't store account information"),
message = _("There was a problem accessing the keyring. Check the debug output for more information."))
except Exception, e:
message = GnomeKeyring.result_to_message(result))
else:
RB.error_dialog(title = _("Couldn't store account information"),
message = str(e))
message = _("There was a problem accessing the keyring. Check the debug output for more information."))
dialog.hide()
def format_selection_changed(self, button):
......@@ -289,5 +258,35 @@ class MagnatuneConfig(GObject.GObject, PeasGtk.Configurable):
builder.connect_signals(self.configure_callback_dic)
dialog.connect("response", close_button_pressed)
keyring.find_items(keyring.ITEM_GENERIC_SECRET, {'rhythmbox-plugin': 'magnatune'}, got_items)
attributes = GnomeKeyring.attribute_list_new()
GnomeKeyring.attribute_list_append_string(attributes, "rhythmbox-plugin", "magnatune")
(result, items) = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.GENERIC_SECRET, attributes)
if result == GnomeKeyring.Result.OK and len(items) != 0:
keyring_data['id'] = items[0].item_id
(result, item) = GnomeKeyring.item_get_info_sync(None, keyring_data['id'])
if result == GnomeKeyring.Result.OK:
keyring_data['item'] = item
else:
print "Couldn't get keyring item: " + GnomeKeyring.result_to_message(result)
elif result == GnomeKeyring.Result.NO_MATCH or len(items) == 0:
# no item found, so create a new one
result = GnomeKeyring.item_create_sync(None,
GnomeKeyring.ItemType.GENERIC_SECRET,
"Rhythmbox: Magnatune account information",
attributes,
"", # Empty secret for now
True)
if result == GnomeKeyring.Result.OK:
keyring_data['id'] = id
(result, item) = GnomeKeyring.item_get_info_sync(None, id)
if result == GnomeKeyring.Result.OK:
keyring_data['item'] = item
else:
print "Couldn't create keyring item: " + GnomeKeyring.result_to_message(result)
else:
print "Couldn't access keyring: " + str(result)
fill_account_details()
dialog.present()
return dialog
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment