[rhythmbox] magnatune: actually make download accounts work



commit 687cf94c3ebe14663913ffc5123bf3639a19f71b
Author: Jonathan Matthew <jonathan d14n org>
Date:   Sun Mar 4 17:37:29 2012 +1000

    magnatune: actually make download accounts work
    
    previous commit only made streaming accounts work, this one
    does download accounts.  oops.

 ...{BuyAlbumHandler.py => DownloadAlbumHandler.py} |    6 +-
 plugins/magnatune/MagnatuneAccount.py              |   89 +++++
 plugins/magnatune/MagnatuneSource.py               |  150 ++++-----
 plugins/magnatune/Makefile.am                      |    3 +-
 plugins/magnatune/TrackListHandler.py              |   15 +-
 plugins/magnatune/magnatune-prefs.ui               |  342 ++++++++++----------
 plugins/magnatune/magnatune.py                     |  140 ++++-----
 7 files changed, 379 insertions(+), 366 deletions(-)
---
diff --git a/plugins/magnatune/BuyAlbumHandler.py b/plugins/magnatune/DownloadAlbumHandler.py
similarity index 92%
rename from plugins/magnatune/BuyAlbumHandler.py
rename to plugins/magnatune/DownloadAlbumHandler.py
index 40b8621..5a8e178 100644
--- a/plugins/magnatune/BuyAlbumHandler.py
+++ b/plugins/magnatune/DownloadAlbumHandler.py
@@ -27,7 +27,7 @@
 
 import xml.sax, xml.sax.handler
 
-class BuyAlbumHandler(xml.sax.handler.ContentHandler): # Class to download the track, etc.
+class DownloadAlbumHandler(xml.sax.handler.ContentHandler): # Class to download the track, etc.
 	format_map =	{
 			'ogg'		:	'URL_OGGZIP',
 			'flac'		:	'URL_FLACZIP',
@@ -45,7 +45,7 @@ class BuyAlbumHandler(xml.sax.handler.ContentHandler): # Class to download the t
 
 	def endElement(self, name):
 		if name == "ERROR": # Something went wrong. Display error message to user.
-			raise MagnatunePurchaseError(self._text)
+			raise MagnatuneDownloadError(self._text)
 		elif name == self._format_tag:
 			self.url = self._text
 		# Response also contains:
@@ -55,5 +55,5 @@ class BuyAlbumHandler(xml.sax.handler.ContentHandler): # Class to download the t
 	def characters(self, content):
 		self._text = self._text + content
 
-class MagnatunePurchaseError(Exception):
+class MagnatuneDownloadError(Exception):
 	pass
diff --git a/plugins/magnatune/MagnatuneAccount.py b/plugins/magnatune/MagnatuneAccount.py
new file mode 100644
index 0000000..f8cf81b
--- /dev/null
+++ b/plugins/magnatune/MagnatuneAccount.py
@@ -0,0 +1,89 @@
+# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-
+#
+# Copyright (C) 2012 Jonathan Matthew  <jonathan d14n org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# The Rhythmbox authors hereby grant permission for non-GPL compatible
+# GStreamer plugins to be used and distributed together with GStreamer
+# and Rhythmbox. This permission is above and beyond the permissions granted
+# by the GPL license by which Rhythmbox is covered. If you modify this code
+# you may extend this exception to your version of the code, but you are not
+# obligated to do so. If you do not wish to do so, delete this exception
+# statement from your version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+
+from gi.repository import Gio, GnomeKeyring
+
+__instance = None
+
+def instance():
+	global __instance
+	if __instance is None:
+		__instance = MagnatuneAccount()
+	return __instance
+
+class MagnatuneAccount(object):
+	def __init__(self):
+		self.keyring_item = None
+		self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.magnatune")
+
+		self.keyring_attributes = GnomeKeyring.attribute_list_new()
+		GnomeKeyring.attribute_list_append_string(self.keyring_attributes,
+							  "rhythmbox-plugin",
+							  "magnatune")
+		(result, items) = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.GENERIC_SECRET,
+							       self.keyring_attributes)
+		if result == GnomeKeyring.Result.OK and len(items) != 0:
+			(result, item) = GnomeKeyring.item_get_info_sync(None, items[0].item_id)
+			if result == GnomeKeyring.Result.OK:
+				self.keyring_item = item
+			else:
+				print "Couldn't get keyring item: " + GnomeKeyring.result_to_message(result)
+		else:
+			print "couldn't search keyring items: " + GnomeKeyring.result_to_message(result)
+
+	def get(self):
+		if self.keyring_item is None:
+			return ('none', None, None)
+
+		account_type = self.settings['account-type']
+		try:
+			(username, password) = self.keyring_item.get_secret().split("\n")
+			return (account_type, username, password)
+		except ValueError:
+			return ('none', None, None)
+
+	def update(self, username, password):
+		secret = '\n'.join((username, password))
+		if self.keyring_item is not None:
+			if secret == self.keyring_item.get_secret():
+				print "account details not changed"
+				return
+
+		(result, id) = GnomeKeyring.item_create_sync(None,
+							     GnomeKeyring.ItemType.GENERIC_SECRET,
+							     "Rhythmbox: Magnatune account information",
+							     self.keyring_attributes,
+							     secret,
+							     True)
+		if result == GnomeKeyring.Result.OK:
+			if self.keyring_item is None:
+				(result, item) = GnomeKeyring.item_get_info_sync(None, id)
+				if result == GnomeKeyring.Result.OK:
+					self.keyring_item = item
+				else:
+					print "couldn't fetch keyring itme: " + GnomeKeyring.result_to_message(result)
+		else:
+			print "couldn't create keyring item: " + GnomeKeyring.result_to_message(result)
diff --git a/plugins/magnatune/MagnatuneSource.py b/plugins/magnatune/MagnatuneSource.py
index 033a77c..b52322e 100644
--- a/plugins/magnatune/MagnatuneSource.py
+++ b/plugins/magnatune/MagnatuneSource.py
@@ -26,6 +26,7 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 
 import os
+import sys
 import xml
 import urllib
 import urlparse
@@ -34,10 +35,11 @@ import zipfile
 
 import rb
 from gi.repository import RB
-from gi.repository import GObject, Gtk, Gdk, Gio, GnomeKeyring
+from gi.repository import GObject, Gtk, Gdk, Gio
 
 from TrackListHandler import TrackListHandler
-from BuyAlbumHandler import BuyAlbumHandler, MagnatunePurchaseError
+from DownloadAlbumHandler import DownloadAlbumHandler, MagnatuneDownloadError
+import MagnatuneAccount
 
 import gettext
 gettext.install('rhythmbox', RB.locale_dir())
@@ -85,7 +87,7 @@ class MagnatuneSource(RB.BrowserSource):
 
 		# album download stuff
 		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.__copies = {} # keeps copy objects for each file
 
 		self.__art_store = RB.ExtDB(name="album-art")
 
@@ -180,7 +182,7 @@ class MagnatuneSource(RB.BrowserSource):
 				Gtk.show_uri(screen, url, Gdk.CURRENT_TIME)
 				urls.add(url)
 
-	def purchase_redirect(self):
+	def download_redirect(self):
 		screen = self.props.shell.props.window.get_screen()
 		tracks = self.get_entry_view().get_selected_entries()
 		urls = set([])
@@ -194,8 +196,8 @@ class MagnatuneSource(RB.BrowserSource):
 
 	def download_album(self):
 		if self.__settings['account-type'] != 'download':
-			# The user doesn't have a download account, so redirect them to the purchase page.
-			self.purchase_redirect()
+			# The user doesn't have a download account, so redirect them to the download signup page
+			self.download_redirect()
 			return
 
 		try:
@@ -203,8 +205,8 @@ class MagnatuneSource(RB.BrowserSource):
 			library = Gio.Settings("org.gnome.rhythmbox.rhythmdb")
 			library_location = library['locations'][0]
 		except IndexError, e:
-			RB.error_dialog(title = _("Couldn't purchase album"),
-				        message = _("You must have a library location set to purchase an album."))
+			RB.error_dialog(title = _("Couldn't download album"),
+				        message = _("You must have a library location set to download an album."))
 			return
 
 		tracks = self.get_entry_view().get_selected_entries()
@@ -319,9 +321,9 @@ class MagnatuneSource(RB.BrowserSource):
 						name = f.get_name()
 						if not name.startswith("in_progress_"):
 							continue
-						uri = magnatune_in_progress_dir.resolve_relative_path(name).load_contents()[0]
+						(result, uri, etag) = magnatune_in_progress_dir.resolve_relative_path(name).load_contents(None)
 						print "restarting download from %s" % uri
-						self.__download_album(Gio.file_new_for_uri(uri), name[12:])
+						self.__download_album(uri, name[12:])
 				else:
 					# hack around some weird chars that show up in the catalogue for some reason
 					data = str(data.str)
@@ -348,26 +350,9 @@ class MagnatuneSource(RB.BrowserSource):
 			self.__notify_status_changed()
 
 			load_size = {'size': 0}
-			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))
+			parser.setContentHandler(TrackListHandler(self.__db, self.__entry_type, self.__sku_dict, self.__home_dict, self.__art_dict))
 
 			self.__catalogue_loader = RB.ChunkLoader()
 			self.__catalogue_loader.set_callback(catalogue_chunk_cb, parser)
@@ -405,32 +390,11 @@ class MagnatuneSource(RB.BrowserSource):
 	#
 
 	def __auth_download(self, sku): # http://magnatune.com/info/api
-		def got_items(result, items):
-			if result is not None or len(items) == 0:
-				RB.error_dialog(title = _("Couldn't get account details"),
-				                message = str(result))
-				return
-
-			try:
-				username, password = items[0].secret.split('\n')
-			except ValueError: # Couldn't parse secret, possibly because it's empty
-				username = ""
-				password = ""
-			print "downloading album: " + sku
-			url_dict = {
-				'id':	magnatune_partner_id,
-				'sku':	sku
-			}
-			url = magnatune_api_download_uri % (username, password)
-			url = url + urllib.urlencode(url_dict)
-
-			l = rb.Loader()
-			l.get_url(url, auth_data_cb, (username, password))
 
 		def auth_data_cb(data, (username, password)):
-			buy_album_handler = BuyAlbumHandler(self.__settings['format'])
+			dl_album_handler = DownloadAlbumHandler(self.__settings['format'])
 			auth_parser = xml.sax.make_parser()
-			auth_parser.setContentHandler(buy_album_handler)
+			auth_parser.setContentHandler(dl_album_handler)
 
 			if data is None:
 				# hmm.
@@ -438,12 +402,13 @@ class MagnatuneSource(RB.BrowserSource):
 
 			try:
 				data = data.replace("<br>", "") # get rid of any stray <br> tags that will mess up the parser
+				data = data.replace(" & ", " &amp; ") # clean up some missing escaping
 				# print data
 				auth_parser.feed(data)
 				auth_parser.close()
 
 				# process the URI: add authentication info, quote the filename component for some reason
-				parsed = urlparse.urlparse(buy_album_handler.url)
+				parsed = urlparse.urlparse(dl_album_handler.url)
 				netloc = "%s:%s %s" % (username, password, parsed.hostname)
 
 				spath = os.path.split(urllib.url2pathname(parsed.path))
@@ -453,33 +418,41 @@ class MagnatuneSource(RB.BrowserSource):
 				authed = (parsed[0], netloc, path) + parsed[3:]
 				audio_dl_uri = urlparse.urlunparse(authed)
 
-				self.__download_album(Gio.file_new_for_uri(audio_dl_uri), sku)
+				print "download uri for %s is %s" % (sku, audio_dl_uri)
+				self.__download_album(audio_dl_uri, sku)
 
-			except MagnatunePurchaseError, e:
+			except MagnatuneDownloadError, e:
 				RB.error_dialog(title = _("Download Error"),
 						message = _("An error occurred while trying to authorize the download.\nThe Magnatune server returned:\n%s") % str(e))
 			except Exception, e:
+				sys.excepthook(*sys.exc_info())
 				RB.error_dialog(title = _("Error"),
 						message = _("An error occurred while trying to download the album.\nThe error text is:\n%s") % str(e))
 
+		print "downloading album: " + sku
+		account = MagnatuneAccount.instance()
+		(account_type, username, password) = account.get()
+		url_dict = {
+			'id':	magnatune_partner_id,
+			'sku':	sku
+		}
+		url = magnatune_api_download_uri % (username, password)
+		url = url + urllib.urlencode(url_dict)
+
+		l = rb.Loader()
+		l.get_url(url, auth_data_cb, (username, password))
 
-		keyring.find_items(keyring.ITEM_GENERIC_SECRET, {'rhythmbox-plugin': 'magnatune'}, got_items)
 
 	def __download_album(self, audio_dl_uri, sku):
-		def download_progress(current, total):
-			self.__downloads[str_uri] = (current, total)
+		def download_progress(copy, complete, total, self):
+			self.__downloads[audio_dl_uri] = (complete, total)
 			self.__notify_status_changed()
 
-		def download_finished(uri, result):
-			del self.__cancellables[str_uri]
-			del self.__downloads[str_uri]
-
-			try:
-				success = uri.copy_finish(result)
-			except Exception, e:
-				success = False
-				print "Download not completed: " + str(e)
+		def download_finished(copy, success, self):
+			del self.__downloads[audio_dl_uri]
+			del self.__copies[audio_dl_uri]
 
+			print "download of %s finished: %s" % (audio_dl_uri, success)
 			if success:
 				threading.Thread(target=unzip_album).start()
 			else:
@@ -490,7 +463,7 @@ class MagnatuneSource(RB.BrowserSource):
 				manager = shell.props.ui_manager
 				manager.get_action("/MagnatuneSourceViewPopup/MagnatuneCancelDownload").set_sensitive(False)
 				if success:
-					shell.notify_custom(4000, _("Finished Downloading"), _("All Magnatune downloads have been completed."))
+					shell.notify_custom(4000, _("Finished Downloading"), _("All Magnatune downloads have been completed."), None, False)
 
 			self.__notify_status_changed()
 
@@ -499,58 +472,59 @@ class MagnatuneSource(RB.BrowserSource):
 			library = Gio.Settings("org.gnome.rhythmbox.rhythmdb")
 			library_location = Gio.file_new_for_uri(library['locations'][0])
 
+			print "unzipping %s" % dest.get_path()
 			album = zipfile.ZipFile(dest.get_path())
 			for track in album.namelist():
 				track_uri = library_location.resolve_relative_path(track).get_uri()
+				print "zip file entry: %s => %s" % (track, track_uri)
 
 				track_uri = RB.sanitize_uri_for_filesystem(track_uri)
 				RB.uri_create_parent_dirs(track_uri)
 
-				track_out = Gio.file_new_for_uri(track_uri).create()
+				track_out = Gio.file_new_for_uri(track_uri).create(Gio.FileCreateFlags.NONE, None)
 				if track_out is not None:
-					track_out.write(album.read(track))
-					track_out.close()
+					track_out.write(album.read(track), None)
+					track_out.close(None)
+					print "adding %s to library" % track_uri
 					self.__db.add_uri(track_uri)
 
 			album.close()
 			remove_download_files()
 
 		def remove_download_files():
-			in_progress.delete()
-			dest.delete()
-
+			print "removing download files"
+			in_progress.delete(None)
+			dest.delete(None)
 
 		in_progress = magnatune_in_progress_dir.resolve_relative_path("in_progress_" + sku)
 		dest = magnatune_in_progress_dir.resolve_relative_path(sku)
 
-		str_uri = audio_dl_uri.get_uri()
-		in_progress.replace_contents(str_uri, None, False, flags=Gio.FileCreateFlags.PRIVATE|Gio.FileCreateFlags.REPLACE_DESTINATION)
+		in_progress.replace_contents(str(audio_dl_uri),
+					     None,
+					     False,
+					     Gio.FileCreateFlags.PRIVATE|Gio.FileCreateFlags.REPLACE_DESTINATION,
+					     None)
 
 		shell = self.props.shell
 		manager = shell.props.ui_manager
 		manager.get_action("/MagnatuneSourceViewPopup/MagnatuneCancelDownload").set_sensitive(True)
 
-		self.__downloads[str_uri] = (0, 0) # (current, total)
-
-		cancel = Gio.Cancellable()
-		self.__cancellables[str_uri] = cancel
 		try:
 			# For some reason, Gio.FileCopyFlags.OVERWRITE doesn't work for copy_async
-			dest.delete()
+			dest.delete(None)
 		except:
 			pass
 
-		# no way to resume downloads, sadly
-		audio_dl_uri.copy_async(dest,
-		                        download_finished,
-					progress_callback=download_progress,
-					flags=Gio.FileCopyFlags.OVERWRITE,
-					cancellable=cancel)
+		dl = RB.AsyncCopy()
+		dl.set_progress(download_progress, self)
+		dl.start(audio_dl_uri, dest.get_uri(), download_finished, self)
+		self.__downloads[audio_dl_uri] = (0, 0) # (current, total)
+		self.__copies[audio_dl_uri] = dl
 
 
 	def cancel_downloads(self):
-		for cancel in self.__cancellables.values():
-			cancel.cancel()
+		for download in self.__copies.values():
+			download.cancel()
 
 		shell = self.props.shell
 		manager = shell.props.ui_manager
diff --git a/plugins/magnatune/Makefile.am b/plugins/magnatune/Makefile.am
index 1fa6861..098e9f2 100644
--- a/plugins/magnatune/Makefile.am
+++ b/plugins/magnatune/Makefile.am
@@ -4,8 +4,9 @@ plugindir = $(PLUGINDIR)/magnatune
 plugindatadir = $(PLUGINDATADIR)/magnatune
 plugin_PYTHON =                       \
        MagnatuneSource.py             \
-       BuyAlbumHandler.py             \
+       DownloadAlbumHandler.py        \
        TrackListHandler.py            \
+       MagnatuneAccount.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
diff --git a/plugins/magnatune/TrackListHandler.py b/plugins/magnatune/TrackListHandler.py
index 996eba1..96a5a0f 100644
--- a/plugins/magnatune/TrackListHandler.py
+++ b/plugins/magnatune/TrackListHandler.py
@@ -32,7 +32,7 @@ import rb
 from gi.repository import RB
 
 class TrackListHandler(xml.sax.handler.ContentHandler):
-	def __init__(self, db, entry_type, sku_dict, home_dict, art_dict, account_type, username, password):
+	def __init__(self, db, entry_type, sku_dict, home_dict, art_dict):
 		xml.sax.handler.ContentHandler.__init__(self)
 		self.__db = db
 		self.__entry_type = entry_type
@@ -40,20 +40,10 @@ class TrackListHandler(xml.sax.handler.ContentHandler):
 		self.__home_dict = home_dict
 		self.__art_dict = art_dict
 		self.__track = {}
-		self.__account_type = account_type
-		self.__user = urllib.quote(username)
-		self.__pw = urllib.quote(password)
-		self.__URIre = re.compile(r'^http://[^.]+\.magnatune\.com/')
-		self.__nsre = re.compile(r'\.(mp3|ogg)$')
 
 	def startElement(self, name, attrs):
 		self.__text = ""
 
-	def fix_trackurl(self, trackurl):
-		trackurl = self.__URIre.sub("http://%s:%s %s magnatune com/" % (self.__user, self.__pw, self.__account_type), trackurl)
-		trackurl = self.__nsre.sub(r"_nospeech.\1", trackurl)
-		return trackurl
-
 	def endElement(self, name):
 		if name == "Track":
 			try:
@@ -62,9 +52,6 @@ class TrackListHandler(xml.sax.handler.ContentHandler):
 					trackurl = self.__track['oggurl']
 				else:
 					trackurl = self.__track['url']
-				# use ad-free tracks if available
-				if self.__account_type != 'none':
-					trackurl = self.fix_trackurl(trackurl)
 
 				trackurl = str(trackurl)
 
diff --git a/plugins/magnatune/magnatune-prefs.ui b/plugins/magnatune/magnatune-prefs.ui
index 7b90fe9..457bc73 100644
--- a/plugins/magnatune/magnatune-prefs.ui
+++ b/plugins/magnatune/magnatune-prefs.ui
@@ -1,146 +1,32 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 2.12 -->
-  <!-- interface-naming-policy toplevel-contextual -->
-  <object class="GtkListStore" id="model1">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">January (01)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">February (02)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">March (03)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">April (04)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">May (05)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">June (06)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">July (07)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">August (08)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">September (09)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">October (10)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">November (11)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">December (12)</col>
-      </row>
-    </data>
-  </object>
-  <object class="GtkListStore" id="model2">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">$5 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$6 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$7 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$8 US (typical)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$9 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$10 US (better than average)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$11 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$12 US (generous)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$13 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$14 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$15 US (VERY generous!)</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$16 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$17 US</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">$18 US (We love you!)</col>
-      </row>
-    </data>
-  </object>
-  <object class="GtkListStore" id="model3">
-    <columns>
-      <!-- column-name gchararray -->
-      <column type="gchararray"/>
-    </columns>
-    <data>
-      <row>
-        <col id="0" translatable="yes">Ogg Vorbis</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">FLAC</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">WAV</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">VBR MP3</col>
-      </row>
-      <row>
-        <col id="0" translatable="yes">128K MP3</col>
-      </row>
-    </data>
-  </object>
   <object class="GtkVBox" id="magnatune_vbox">
     <property name="visible">True</property>
+    <property name="can_focus">False</property>
     <property name="border_width">5</property>
-    <property name="orientation">vertical</property>
     <property name="spacing">18</property>
     <child>
       <object class="GtkImage" id="image1">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <property name="pixbuf">magnatune_logo_color_small.png</property>
       </object>
       <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
         <property name="position">0</property>
       </packing>
     </child>
     <child>
       <object class="GtkVBox" id="account_details_box">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+        <property name="can_focus">False</property>
         <property name="spacing">6</property>
         <child>
           <object class="GtkLabel" id="magnatune_label">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="xalign">0</property>
             <property name="label" translatable="yes">Magnatune Information</property>
           </object>
@@ -153,18 +39,21 @@
         <child>
           <object class="GtkVBox" id="vbox1">
             <property name="visible">True</property>
-            <property name="orientation">vertical</property>
+            <property name="can_focus">False</property>
             <child>
               <object class="GtkRadioButton" id="no_account_radio">
                 <property name="label" translatable="yes">I don't have a Magnatune account</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
                 <property name="active">True</property>
                 <property name="draw_indicator">True</property>
-                <signal name="toggled" handler="rb_magnatune_radio_account_toggled_cb"/>
+                <signal name="toggled" handler="rb_magnatune_radio_account_toggled_cb" swapped="no"/>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">0</property>
               </packing>
             </child>
@@ -174,11 +63,14 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
                 <property name="draw_indicator">True</property>
                 <property name="group">no_account_radio</property>
-                <signal name="toggled" handler="rb_magnatune_radio_account_toggled_cb"/>
+                <signal name="toggled" handler="rb_magnatune_radio_account_toggled_cb" swapped="no"/>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">1</property>
               </packing>
             </child>
@@ -188,28 +80,34 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">False</property>
+                <property name="use_action_appearance">False</property>
                 <property name="draw_indicator">True</property>
                 <property name="group">no_account_radio</property>
-                <signal name="toggled" handler="rb_magnatune_radio_account_toggled_cb"/>
+                <signal name="toggled" handler="rb_magnatune_radio_account_toggled_cb" swapped="no"/>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">2</property>
               </packing>
             </child>
             <child>
               <object class="GtkTable" id="table1">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="n_rows">2</property>
                 <property name="n_columns">2</property>
                 <child>
                   <object class="GtkLabel" id="username_label">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="label" translatable="yes">Username:</property>
                   </object>
                 </child>
                 <child>
                   <object class="GtkLabel" id="password_label">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="label" translatable="yes">Password:</property>
                   </object>
                   <packing>
@@ -221,8 +119,7 @@
                   <object class="GtkEntry" id="username_entry">
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                    <signal name="changed" handler="rb_magnatune_username_changed_cb"/>
+                    <property name="invisible_char">â</property>
                   </object>
                   <packing>
                     <property name="left_attach">1</property>
@@ -234,8 +131,7 @@
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="visibility">False</property>
-                    <property name="invisible_char">&#x25CF;</property>
-                    <signal name="changed" handler="rb_magnatune_password_changed_cb"/>
+                    <property name="invisible_char">â</property>
                   </object>
                   <packing>
                     <property name="left_attach">1</property>
@@ -246,11 +142,15 @@
                 </child>
               </object>
               <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
                 <property name="position">3</property>
               </packing>
             </child>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">1</property>
           </packing>
         </child>
@@ -264,9 +164,11 @@
     <child>
       <object class="GtkHBox" id="hbox21">
         <property name="visible">True</property>
+        <property name="can_focus">False</property>
         <child>
           <object class="GtkLabel" id="audio_label">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="xpad">8</property>
             <property name="label" translatable="yes">Preferred audio _format:</property>
             <property name="use_underline">True</property>
@@ -281,8 +183,9 @@
         <child>
           <object class="GtkComboBox" id="audio_combobox">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <property name="model">model3</property>
-            <signal name="changed" handler="rb_magnatune_audio_combobox_changed_cb"/>
+            <signal name="changed" handler="rb_magnatune_audio_combobox_changed_cb" swapped="no"/>
             <child>
               <object class="GtkCellRendererText" id="renderer3"/>
               <attributes>
@@ -292,24 +195,29 @@
           </object>
           <packing>
             <property name="expand">False</property>
+            <property name="fill">True</property>
             <property name="position">1</property>
           </packing>
         </child>
       </object>
       <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
         <property name="position">2</property>
       </packing>
     </child>
     <child>
       <object class="GtkVBox" id="vbox14">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
+        <property name="can_focus">False</property>
         <child>
           <object class="GtkHBox" id="hbox23">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <child>
               <object class="GtkLabel" id="label57">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="label" translatable="yes">Get an account at </property>
               </object>
               <packing>
@@ -324,6 +232,7 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
                 <property name="relief">none</property>
                 <property name="uri">http://magnatune.com/compare_plans?referal_id=rhythmbox</property>
               </object>
@@ -335,15 +244,19 @@
             </child>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">0</property>
           </packing>
         </child>
         <child>
           <object class="GtkHBox" id="hbox15">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
             <child>
               <object class="GtkLabel" id="label21">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="label" translatable="yes">Find out about Magnatune at </property>
                 <property name="use_underline">True</property>
                 <property name="mnemonic_widget">magnatune_link</property>
@@ -360,6 +273,7 @@
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
                 <property name="relief">none</property>
                 <property name="uri">http://www.magnatune.com/info/</property>
               </object>
@@ -371,58 +285,134 @@
             </child>
           </object>
           <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
             <property name="position">1</property>
           </packing>
         </child>
-        <child>
-          <object class="GtkHBox" id="hbox17">
-            <property name="visible">True</property>
-            <child>
-              <object class="GtkLabel" id="label27">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Redownload purchased music at </property>
-                <property name="use_underline">True</property>
-                <property name="mnemonic_widget">redownload_link</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLinkButton" id="redownload_link">
-                <property name="label" translatable="yes">http://www.magnatune.com/info/redownload</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="relief">none</property>
-                <property name="uri">http://www.magnatune.com/info/redownload</property>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="position">2</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkLabel" id="account_changed_label">
-            <property name="visible">True</property>
-            <property name="label" translatable="yes">Your account details have changed. Changes will be applied the next time you start Rhythmbox.</property>
-          </object>
-          <packing>
-            <property name="position">3</property>
-          </packing>
-        </child>
       </object>
       <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
         <property name="position">3</property>
       </packing>
     </child>
   </object>
+  <object class="GtkListStore" id="model1">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">January (01)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">February (02)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">March (03)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">April (04)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">May (05)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">June (06)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">July (07)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">August (08)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">September (09)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">October (10)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">November (11)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">December (12)</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkListStore" id="model2">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">$5 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$6 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$7 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$8 US (typical)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$9 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$10 US (better than average)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$11 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$12 US (generous)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$13 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$14 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$15 US (VERY generous!)</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$16 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$17 US</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">$18 US (We love you!)</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkListStore" id="model3">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">Ogg Vorbis</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">FLAC</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">WAV</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">VBR MP3</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">128K MP3</col>
+      </row>
+    </data>
+  </object>
 </interface>
diff --git a/plugins/magnatune/magnatune.py b/plugins/magnatune/magnatune.py
index b813f72..ce2aeb3 100644
--- a/plugins/magnatune/magnatune.py
+++ b/plugins/magnatune/magnatune.py
@@ -31,12 +31,14 @@ import sys, os.path
 import xml
 import datetime
 import string
+import re
 
 import rb
 from gi.repository import RB
-from gi.repository import GObject, Gtk, Gio, Peas, PeasGtk, GnomeKeyring
+from gi.repository import GObject, Gtk, Gio, Peas, PeasGtk
 
 from MagnatuneSource import MagnatuneSource
+import MagnatuneAccount
 
 import gettext
 gettext.install('rhythmbox', RB.locale_dir())
@@ -64,21 +66,40 @@ popup_ui = """
 </ui>
 """
 
+
+
 class MagnatuneEntryType(RB.RhythmDBEntryType):
 	def __init__(self):
 		RB.RhythmDBEntryType.__init__(self, name='magnatune')
+		self.URIre = re.compile(r'^http://[^.]+\.magnatune\.com/')
+		self.nsre = re.compile(r'\.(mp3|ogg)$')
+		self.account = MagnatuneAccount.instance()
+
+	def fix_trackurl(self, trackurl, account_type, username, password):
+		return trackurl
+
+	def do_get_playback_uri(self, entry):
+		(account_type, username, password) = self.account.get()
+		uri = entry.get_string(RB.RhythmDBPropType.LOCATION)
+		if account_type != "none":
+			uri = self.URIre.sub("http://%s:%s %s magnatune com/" % (username, password, account_type), uri)
+			uri = self.nsre.sub(r"_nospeech.\1", uri)
+			print "converted track uri: %s" % uri
 
-	def can_sync_metadata(self, entry):
+		return uri
+
+	def do_can_sync_metadata(self, entry):
 		return True
 
-	def sync_metadata(self, entry, changes):
+	def do_sync_metadata(self, entry, changes):
 		return
 
+
+
 class Magnatune(GObject.GObject, Peas.Activatable):
 	__gtype_name__ = 'Magnatune'
 	object = GObject.property(type=GObject.GObject)
 
-
 	def __init__(self):
 		GObject.GObject.__init__(self)
 
@@ -126,7 +147,7 @@ class Magnatune(GObject.GObject, Peas.Activatable):
 		action.connect('activate', lambda a: shell.props.selected_page.display_artist_info())
 		self.action_group.add_action(action)
 		action = Gtk.Action(name='MagnatuneCancelDownload', label=_("Cancel Downloads"),
-				tooltip=_("Stop downloading purchased albums"),
+				tooltip=_("Stop album downloads"),
 				stock_id='gtk-stop')
 		action.connect('activate', lambda a: shell.props.selected_page.cancel_downloads())
 		action.set_sensitive(False)
@@ -138,6 +159,7 @@ class Magnatune(GObject.GObject, Peas.Activatable):
 		self.pec_id = shell.props.shell_player.connect('playing-song-changed', self.playing_entry_changed)
 		manager.ensure_update()
 
+
 	def do_deactivate(self):
 		shell = self.object
 		manager = shell.props.ui_manager
@@ -158,6 +180,8 @@ class Magnatune(GObject.GObject, Peas.Activatable):
 		self.source.playing_entry_changed(entry)
 
 
+
+
 class MagnatuneConfig(GObject.GObject, PeasGtk.Configurable):
 	__gtype_name__ = 'MagnatuneConfig'
 	object = GObject.property(type=GObject.GObject)
@@ -167,6 +191,7 @@ class MagnatuneConfig(GObject.GObject, PeasGtk.Configurable):
 	def __init__(self):
 		GObject.GObject.__init__(self)
 		self.settings = Gio.Settings("org.gnome.rhythmbox.plugins.magnatune")
+		self.account = MagnatuneAccount.instance()
 
 	def do_create_configure_widget(self):
 		# We use a dictionary so we can modify these values from within inner functions
@@ -175,76 +200,52 @@ class MagnatuneConfig(GObject.GObject, PeasGtk.Configurable):
 			'item': None
 		}
 
+		def update_sensitivity(account_type):
+			has_account = account_type != "none"
+			builder.get_object("username_entry").set_sensitive(has_account)
+			builder.get_object("password_entry").set_sensitive(has_account)
+			builder.get_object("username_label").set_sensitive(has_account)
+			builder.get_object("password_label").set_sensitive(has_account)
+
+
 		def fill_account_details():
-			account_type = self.settings['account-type']
+			(account_type, username, password) = self.account.get()
 			builder.get_object("no_account_radio").set_active(account_type == "none")
 			builder.get_object("stream_account_radio").set_active(account_type == "stream")
 			builder.get_object("download_account_radio").set_active(account_type == "download")
 
-			username = ""
-			password = ""
-			try:
-				if keyring_data['item']:
-					username, password = keyring_data['item'].get_secret().split('\n')
-			except ValueError: # Couldn't parse the secret, probably because it's empty
-				pass
 			builder.get_object("username_entry").set_text(username)
 			builder.get_object("password_entry").set_text(password)
 
-			has_account = account_type != "none"
-			builder.get_object("username_entry").set_sensitive(has_account)
-			builder.get_object("password_entry").set_sensitive(has_account)
-			builder.get_object("username_label").set_sensitive(has_account)
-			builder.get_object("password_label").set_sensitive(has_account)
+			update_sensitivity(account_type)
 
-			builder.get_object("account_changed_label").hide()
 
-		def account_type_toggled (button):
+		def account_type_toggled(button):
 			print "account type radiobutton toggled: " + button.get_name()
 			account_type = {"no_account_radio": 'none', "stream_account_radio": 'stream', "download_account_radio": 'download'} 
 			if button.get_active():
 				self.settings['account-type'] = account_type[button.get_name()]
-				if account_type[button.get_name()] == 'none':
-					builder.get_object("username_label").set_sensitive(False)
-					builder.get_object("username_entry").set_sensitive(False)
-					builder.get_object("password_label").set_sensitive(False)
-					builder.get_object("password_entry").set_sensitive(False)
-				else:
-					builder.get_object("username_label").set_sensitive(True)
-					builder.get_object("username_entry").set_sensitive(True)
-					builder.get_object("password_label").set_sensitive(True)
-					builder.get_object("password_entry").set_sensitive(True)
-				builder.get_object("account_changed_label").show()
-
-		def account_details_changed(entry):
+				update_sensitivity(account_type[button.get_name()])
+
+		def account_details_changed(entry, event):
 			username = builder.get_object("username_entry").get_text()
 			password = builder.get_object("password_entry").get_text()
-			if keyring_data['item']:
-				keyring_data['item'].set_secret('\n'.join((username, password)))
-
-			builder.get_object("account_changed_label").show()
-
-		def close_button_pressed(x, y):
-			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 = GnomeKeyring.result_to_message(result))
-			else:
-				RB.error_dialog(title = _("Couldn't store account information"),
-						message = _("There was a problem accessing the keyring. Check the debug output for more information."))
-			dialog.hide()
+
+			if username == "" or password == "":
+				print "missing something"
+				return
+
+			# should actually try a request to http://username:password account-type magnatune com/
+			# to check the password is correct..
+
+			MagnatuneAccount.instance().update(username, password)
 
 		def format_selection_changed(button):
 			self.settings['format'] = self.format_list[button.get_active()]
 
 		self.configure_callback_dic = {
 			"rb_magnatune_audio_combobox_changed_cb" : format_selection_changed,
-			"rb_magnatune_radio_account_toggled_cb" : account_type_toggled,
-			"rb_magnatune_username_changed_cb" : account_details_changed,
-			"rb_magnatune_password_changed_cb" : account_details_changed
+			"rb_magnatune_radio_account_toggled_cb" : account_type_toggled
 		}
 
 		builder = Gtk.Builder()
@@ -259,37 +260,8 @@ class MagnatuneConfig(GObject.GObject, PeasGtk.Configurable):
 		builder.get_object("audio_combobox").set_active(self.format_list.index(self.settings['format']))
 
 		builder.connect_signals(self.configure_callback_dic)
-		# hrm, how do we do this?
-		# dialog.connect("response", close_button_pressed)
-
-		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, id) = 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)
+		builder.get_object("username_entry").connect("focus-out-event", account_details_changed)
+		builder.get_object("password_entry").connect("focus-out-event", account_details_changed)
 
 		fill_account_details()
 		return dialog



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]