[rhythmbox] magnatune: actually make download accounts work
- From: Jonathan Matthew <jmatthew src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rhythmbox] magnatune: actually make download accounts work
- Date: Sun, 4 Mar 2012 22:45:58 +0000 (UTC)
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(" & ", " & ") # 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">●</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">●</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]