| Viewing file:  MagnatuneSource.py (21.93 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
# -*- Mode: python; coding: utf-8; tab-width: 8; indent-tabs-mode: t; -*-#
 # Copyright (C) 2006 Adam Zimmerman  <adam_zimmerman@sfu.ca>
 # Copyright (C) 2006 James Livingston  <doclivingston@gmail.com>
 #
 # 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 grants 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.
 
 import rb, rhythmdb
 from TrackListHandler import TrackListHandler
 from BuyAlbumHandler import BuyAlbumHandler, MagnatunePurchaseError
 
 import gobject
 import gtk.glade
 import gnomevfs, gnome, gconf
 import xml
 import urllib, zipfile
 
 has_gnome_keyring = False
 
 #try:
 #    import gnomekeyring
 #    has_gnome_keyring = True
 #except:
 #    pass
 
 
 magnatune_partner_id = "rhythmbox"
 
 # URIs
 magnatune_dir = gnome.user_dir_get() + "rhythmbox/magnatune/"
 magnatune_song_info_uri = gnomevfs.URI("http://magnatune.com/info/song_info_xml.zip")
 local_song_info_uri = gnomevfs.URI(magnatune_dir + "song_info.xml")
 local_song_info_temp_uri = gnomevfs.URI(magnatune_dir + "song_info.xml.zip.tmp")
 ALBUM_ART_URL = 'http://www.magnatune.com/music/%s/%s/cover.jpg'
 
 class MagnatuneSource(rb.BrowserSource):
 __gproperties__ = {
 'plugin': (rb.Plugin, 'plugin', 'plugin', gobject.PARAM_WRITABLE|gobject.PARAM_CONSTRUCT_ONLY),
 }
 
 __client = gconf.client_get_default()
 
 
 def __init__(self):
 
 rb.BrowserSource.__init__(self, name=_("Magnatune"))
 self.__db = None
 
 # track data
 self.__sku_dict = {}
 self.__home_dict = {}
 self.__buy_dict = {}
 self.__art_dict = {}
 
 # catalogue stuff
 self.__activated = False
 self.__notify_id = 0
 self.__update_id = 0
 self.__xfer_handle = None
 self.__info_screen = None
 self.__updating = True
 self.__has_loaded = False
 self.__load_handle = None
 self.__load_current_size = 0
 self.__load_total_size = 0
 
 self.__downloads = {} # keeps track of amount downloaded for each file
 self.__downloading = False # keeps track of whether we are currently downloading an album
 self.__download_progress = 0.0 # progress of current download(s)
 self.purchase_filesize = 0 # total amount of bytes to download
 
 
 def do_set_property(self, property, value):
 if property.name == 'plugin':
 self.__plugin = value
 else:
 raise AttributeError, 'unknown property %s' % property.name
 
 #
 # RBSource methods
 #
 
 def do_impl_show_entry_popup(self):
 self.show_source_popup ("/MagnatuneSourceViewPopup")
 
 def do_impl_get_status(self):
 if self.__updating:
 if self.__load_total_size > 0:
 progress = min (float(self.__load_current_size) / self.__load_total_size, 1.0)
 else:
 progress = -1.0
 return (_("Loading Magnatune catalogue"), None, progress)
 elif self.__downloading:
 progress = min (self.__download_progress, 1.0)
 return (_("Downloading Magnatune Album(s)"), None, progress)
 else:
 qm = self.get_property("query-model")
 return (qm.compute_status_normal("%d song", "%d songs"), None, 0.0)
 
 def do_impl_get_ui_actions(self):
 return ["MagnatunePurchaseAlbum",
 "MagnatunePurchaseCD",
 "MagnatuneArtistInfo",
 "MagnatuneCancelDownload"]
 
 def do_impl_activate(self):
 if not self.__activated:
 shell = self.get_property('shell')
 self.__db = shell.get_property('db')
 self.__entry_type = self.get_property('entry-type')
 
 self.__activated = True
 self.__show_loading_screen (True)
 self.__load_catalogue()
 
 # start our catalogue updates
 self.__update_id = gobject.timeout_add(6 * 60 * 60 * 1000, self.__update_catalogue)
 self.__update_catalogue()
 
 self.get_entry_view().set_sorting_type(self.__client.get_string("/apps/rhythmbox/plugins/magnatune/sorting"))
 
 rb.BrowserSource.do_impl_activate (self)
 
 def do_impl_get_browser_key (self):
 return "/apps/rhythmbox/plugins/magnatune/show_browser"
 
 def do_impl_get_paned_key (self):
 return "/apps/rhythmbox/plugins/magnatune/paned_position"
 
 def do_impl_pack_paned (self, paned):
 self.__paned_box = gtk.VBox(False, 5)
 self.pack_start(self.__paned_box)
 self.__paned_box.pack_start(paned)
 
 
 def do_impl_delete_thyself(self):
 if self.__update_id != 0:
 gobject.source_remove (self.__update_id)
 self.__update_id = 0
 
 if self.__notify_id != 0:
 gobject.source_remove (self.__notify_id)
 self.__notify_id = 0
 
 if self.__xfer_handle is not None:
 self.__xfer_handle.cancel()
 self.__xfer_handle = None
 
 self.__client.set_string("/apps/rhythmbox/plugins/magnatune/sorting", self.get_entry_view().get_sorting_type())
 
 rb.BrowserSource.do_impl_delete_thyself (self)
 
 #
 # methods for use by plugin and UI
 #
 
 def display_artist_info(self):
 tracks = self.get_entry_view().get_selected_entries()
 urls = set([])
 
 for tr in tracks:
 sku = self.__sku_dict[self.__db.entry_get(tr, rhythmdb.PROP_LOCATION)]
 url = self.__home_dict[sku]
 if url not in urls:
 gnomevfs.url_show(url)
 urls.add(url)
 
 def buy_cd(self):
 tracks = self.get_entry_view().get_selected_entries()
 urls = set([])
 
 for tr in tracks:
 sku = self.__sku_dict[self.__db.entry_get(tr, rhythmdb.PROP_LOCATION)]
 url = self.__buy_dict[sku]
 if url not in urls:
 gnomevfs.url_show(url)
 urls.add(url)
 
 def radio_toggled(self, gladexml):
 gc = gladexml.get_widget("radio_gc").get_active()
 gladexml.get_widget("remember_cc_details").set_sensitive(not gc)
 gladexml.get_widget("name_entry").set_sensitive(not gc)
 gladexml.get_widget("cc_entry").set_sensitive(not gc)
 gladexml.get_widget("mm_entry").set_sensitive(not gc)
 gladexml.get_widget("yy_entry").set_sensitive(not gc)
 
 gladexml.get_widget("gc_entry").set_sensitive(gc)
 if not gc:
 gladexml.get_widget("gc_entry").set_text("")
 
 def purchase_album(self):
 try:
 library_location = self.__client.get_list("/apps/rhythmbox/library_locations", gconf.VALUE_STRING)[0] # Just use the first library location
 except IndexError, e:
 rb.error_dialog(title = _("Couldn't purchase album"),
 message = _("You must have a library location set to purchase an album."))
 return
 
 tracks = self.get_entry_view().get_selected_entries()
 skus = []
 
 for track in tracks:
 sku = self.__sku_dict[self.__db.entry_get(track, rhythmdb.PROP_LOCATION)]
 if sku in skus:
 continue
 skus.append(sku)
 artist = self.__db.entry_get(track, rhythmdb.PROP_ARTIST)
 album = self.__db.entry_get(track, rhythmdb.PROP_ALBUM)
 
 gladexml = gtk.glade.XML(self.__plugin.find_file("magnatune-purchase.glade"))
 cb_dict = {"rb_magnatune_on_radio_cc_toggled_cb":lambda w:self.radio_toggled(gladexml)}
 gladexml.signal_autoconnect(cb_dict)
 
 gladexml.get_widget("gc_entry").set_sensitive(False)
 gladexml.get_widget("pay_combobox").set_active(self.__client.get_int(self.__plugin.gconf_keys['pay']) - 5)
 gladexml.get_widget("audio_combobox").set_active(self.__plugin.format_list.index(self.__client.get_string(self.__plugin.gconf_keys['format'])))
 gladexml.get_widget("info_label").set_markup(_("Would you like to purchase the album <i>%(album)s</i> by '%(artist)s'?") % {"album":album, "artist":artist})
 gladexml.get_widget("remember_cc_details").props.visible = has_gnome_keyring
 
 try:
 (ccnumber, ccyear, ccmonth, name, email) = self.__plugin.get_cc_details()
 gladexml.get_widget("cc_entry").set_text(ccnumber)
 gladexml.get_widget("yy_entry").set_text(ccyear)
 gladexml.get_widget("mm_entry").set_active(ccmonth-1)
 gladexml.get_widget("name_entry").set_text(name)
 gladexml.get_widget("email_entry").set_text(email)
 
 gladexml.get_widget("remember_cc_details").set_active(True)
 except Exception, e:
 print e
 
 gladexml.get_widget("cc_entry").set_text("")
 gladexml.get_widget("yy_entry").set_text("")
 gladexml.get_widget("mm_entry").set_active(0)
 gladexml.get_widget("name_entry").set_text("")
 gladexml.get_widget("email_entry").set_text("")
 
 gladexml.get_widget("remember_cc_details").set_active(False)
 
 window = gladexml.get_widget("purchase_dialog")
 if window.run() == gtk.RESPONSE_ACCEPT:
 amount = gladexml.get_widget("pay_combobox").get_active() + 5
 format = self.__plugin.format_list[gladexml.get_widget("audio_combobox").get_active()]
 ccnumber = gladexml.get_widget("cc_entry").get_text()
 ccyear = gladexml.get_widget("yy_entry").get_text()
 ccmonth = str(gladexml.get_widget("mm_entry").get_active() + 1).zfill(2)
 name = gladexml.get_widget("name_entry").get_text()
 email = gladexml.get_widget("email_entry").get_text()
 gc = gladexml.get_widget("radio_gc").get_active()
 gc_text = gladexml.get_widget("gc_entry").get_text()
 
 if gladexml.get_widget("remember_cc_details").props.active:
 self.__plugin.store_cc_details(ccnumber, ccyear, ccmonth, name, email)
 else:
 self.__plugin.clear_cc_details()
 
 self.__buy_album (sku, amount, format, ccnumber, ccyear, ccmonth, name, email, gc, gc_text)
 
 window.destroy()
 
 #
 # internal catalogue downloading and loading
 #
 def __load_catalogue_read_cb (self, handle, data, exc_type, bytes_requested, parser):
 if exc_type:
 if issubclass (exc_type, gnomevfs.EOFError):
 def finish_loadscreen():
 # successfully loaded
 gtk.gdk.threads_enter()
 self.__show_loading_screen (False)
 
 in_progress_dir = gnomevfs.DirectoryHandle(gnomevfs.URI(magnatune_dir))
 in_progress = in_progress_dir.next()
 while True:
 if in_progress.name[0:12] == "in_progress_":
 in_progress = gnomevfs.read_entire_file(magnatune_dir + in_progress.name)
 for uri in in_progress.split("\n"):
 if uri == '':
 continue
 self.__download_album(gnomevfs.URI(uri))
 try:
 in_progress = in_progress_dir.next()
 except:
 break
 gtk.gdk.threads_leave()
 gobject.idle_add (finish_loadscreen)
 else:
 # error reading file
 raise exc_type
 
 parser.close()
 handle.close(lambda handle, exc: None) # FIXME: report it?
 self.__load_handle = None
 self.__updating = False
 self.__notify_status_changed()
 else:
 
 parser.feed(data)
 handle.read(64 * 1024, self.__load_catalogue_read_cb, parser)
 
 self.__notify_status_changed()
 
 def __load_catalogue_open_cb (self, handle, exc_type):
 if exc_type:
 self.__load_handle = None
 self.__notify_status_changed()
 
 if gnomevfs.exists(local_song_info_uri):
 raise exc_type
 else:
 return
 
 parser = xml.sax.make_parser()
 parser.setContentHandler(TrackListHandler(self.__db, self.__entry_type, self.__sku_dict, self.__home_dict, self.__buy_dict, self.__art_dict))
 handle.read (64 * 1024, self.__load_catalogue_read_cb, parser)
 
 def __load_catalogue(self):
 self.__notify_status_changed()
 self.__load_handle = gnomevfs.async.open (local_song_info_uri, self.__load_catalogue_open_cb)
 
 def __find_song_info(self, catalogue):
 for info in catalogue.infolist():
 if info.filename.endswith("song_info.xml"):
 return info.filename;
 return None
 
 def __download_update_cb (self, _reserved, info, moving):
 self.__load_current_size = info.bytes_copied
 self.__load_total_size = info.bytes_total
 self.__notify_status_changed()
 
 if info.phase == gnomevfs.XFER_PHASE_COMPLETED:
 # done downloading, unzip to real location
 catalog = zipfile.ZipFile(local_song_info_temp_uri.path)
 out = create_if_needed(local_song_info_uri, gnomevfs.OPEN_WRITE)
 filename = self.__find_song_info(catalog)
 if filename is None:
 rb.error_dialog(title="Unable to load catalogue", message=_("Rhythmbox could not understand the Magnatune catalogue, please file a bug."))
 return
 out.write(catalog.read(filename))
 out.close()
 catalog.close()
 gnomevfs.unlink(local_song_info_temp_uri)
 self.__updating = False
 self.__load_catalogue()
 self.__xfer_handle = None
 else:
 #print info
 pass
 
 return 1
 
 def __download_catalogue(self):
 self.__updating = True
 create_if_needed(local_song_info_temp_uri, gnomevfs.OPEN_WRITE).close()
 self.__xfer_handle = gnomevfs.async.xfer (source_uri_list = [magnatune_song_info_uri],
 target_uri_list = [local_song_info_temp_uri],
 xfer_options = gnomevfs.XFER_FOLLOW_LINKS_RECURSIVE,
 error_mode = gnomevfs.XFER_ERROR_MODE_ABORT,
 overwrite_mode = gnomevfs.XFER_OVERWRITE_MODE_REPLACE,
 progress_update_callback = self.__download_update_cb,
 update_callback_data = False)
 
 
 def __update_catalogue(self):
 def info_cb (handle, results):
 (remote_uri, remote_exc, remote_info) = results[0]
 (local_uri, local_exc, local_info) = results[1]
 
 if remote_exc:
 # error locating remote file
 print "error locating remote catalogue", remote_exc
 elif local_exc:
 if issubclass (local_exc, gnomevfs.NotFoundError):
 # we haven't got it yet
 print "no local copy of catalogue"
 self.__download_catalogue()
 else:
 # error locating local file
 print "error locating local catalogue", local_exc
 self.__download_catalogue()
 else:
 try:
 if remote_info.mtime > local_info.mtime:
 # newer version available
 self.__download_catalogue()
 else:
 # up to date
 pass
 except ValueError, e:
 # couldn't get the mtimes. download?
 print "error checking times", e
 self.__download_catalogue()
 return
 
 gnomevfs.async.get_file_info ((magnatune_song_info_uri, local_song_info_uri), info_cb)
 
 def __show_loading_screen(self, show):
 if self.__info_screen is None:
 # load the glade stuff
 gladexml = gtk.glade.XML(self.__plugin.find_file("magnatune-loading.glade"), root="magnatune_loading_scrolledwindow")
 self.__info_screen = gladexml.get_widget("magnatune_loading_scrolledwindow")
 self.pack_start(self.__info_screen)
 self.get_entry_view().set_no_show_all (True)
 self.__info_screen.set_no_show_all (True)
 
 self.__info_screen.set_property("visible", show)
 self.__paned_box.set_property("visible", not show)
 
 def __notify_status_changed(self):
 def change_idle_cb():
 self.notify_status_changed()
 self.__notify_id = 0
 return False
 
 if self.__notify_id == 0:
 self.__notify_id = gobject.idle_add(change_idle_cb)
 
 #
 # internal purchasing code
 #
 def __buy_album(self, sku, pay, format, ccnumber, ccyear, ccmonth, name, email, gc, gc_text): # http://magnatune.com/info/api#purchase
 print "purchasing tracks:", sku, pay, format, name, email
 url_dict = {
 'id':    magnatune_partner_id,
 'sku':    sku,
 'amount': pay,
 'name': name,
 'email':email
 }
 if gc:
 url_dict['gc'] = gc
 else:
 url_dict['cc'] = ccnumber
 url_dict['yy'] = ccyear
 url_dict['mm'] = ccmonth
 url = "https://magnatune.com/buy/buy_dl_cc_xml?"
 url = url + urllib.urlencode(url_dict)
 
 buy_album_handler = BuyAlbumHandler(format) # so we can get the url and auth info
 auth_parser = xml.sax.make_parser()
 auth_parser.setContentHandler(buy_album_handler)
 
 self.__wait_dlg = gtk.Dialog(title="Authorizing Purchase", flags=gtk.DIALOG_NO_SEPARATOR|gtk.DIALOG_DESTROY_WITH_PARENT)
 lbl = gtk.Label("Authorizing purchase with the Magnatune server. Please wait...")
 self.__wait_dlg.vbox.pack_start(lbl)
 lbl.show()
 self.__wait_dlg.show()
 gnomevfs.async.open(gnomevfs.URI(url), self.__auth_open_cb, data=(buy_album_handler, auth_parser))
 
 def __auth_open_cb(self, handle, exc_type, data):
 if exc_type:
 raise exc_type
 
 handle.read(64 * 1024, self.__auth_read_cb, data)
 
 
 def __auth_read_cb (self, handle, data, exc_type, bytes_requested, parser):
 buy_album_handler = parser[0]
 auth_parser = parser[1]
 data = data.replace("<br>", "") # get rid of any stray <br> tags that will mess up the parser
 if exc_type:
 if issubclass (exc_type, gnomevfs.EOFError):
 def start_download ():
 # successfully loaded
 gtk.gdk.threads_enter()
 audio_dl_uri = gnomevfs.URI(buy_album_handler.url)
 audio_dl_uri = gnomevfs.URI(buy_album_handler.url[0:buy_album_handler.url.rfind("/") + 1] + urllib.quote(audio_dl_uri.short_name))
 audio_dl_uri.user_name = str(buy_album_handler.username) # URI objects don't like unicode strings
 audio_dl_uri.password = str(buy_album_handler.password)
 
 in_progress = create_if_needed(gnomevfs.URI(magnatune_dir + "in_progress_" + audio_dl_uri.short_name), gnomevfs.OPEN_WRITE)
 in_progress.write(str(audio_dl_uri))
 in_progress.close()
 self.__download_album(audio_dl_uri)
 self.__wait_dlg.destroy()
 gtk.gdk.threads_leave()
 gobject.idle_add (start_download)
 else:
 # error reading file
 raise exc_type
 
 auth_parser.close()
 handle.close(lambda handle, exc: None) # FIXME: report it?
 
 else:
 try :
 print data
 auth_parser.feed(data)
 handle.read(64 * 1024, self.__auth_read_cb, parser)
 except MagnatunePurchaseError, e:
 self.__wait_dlg.destroy()
 rb.error_dialog(title = _("Purchase Error"),
 message = _("An error occurred while trying to purchase the album.\nThe Magnatune server returned:\n%s") % str(e))
 except Exception, e:
 self.__wait_dlg.destroy()
 rb.error_dialog(title = _("Error"),
 message = _("An error occurred while trying to purchase the album.\nThe error text is:\n%s") % str(e))
 
 def __download_album(self, audio_dl_uri):
 library_location = self.__client.get_list("/apps/rhythmbox/library_locations", gconf.VALUE_STRING)[0] # Just use the first library location
 to_file_uri = gnomevfs.URI(magnatune_dir + audio_dl_uri.short_name)
 
 shell = self.get_property('shell')
 manager = shell.get_player().get_property('ui-manager')
 manager.get_action("/MagnatuneSourceViewPopup/MagnatuneCancelDownload").set_sensitive(True)
 self.__downloading = True
 self.cancelled = False
 gtk.gdk.threads_leave()
 self.purchase_filesize += gnomevfs.get_file_info(audio_dl_uri).size
 gtk.gdk.threads_enter()
 create_if_needed(to_file_uri, gnomevfs.OPEN_WRITE).close()
 gnomevfs.async.xfer (source_uri_list = [audio_dl_uri],
 target_uri_list = [to_file_uri],
 xfer_options = gnomevfs.XFER_FOLLOW_LINKS_RECURSIVE,
 error_mode = gnomevfs.XFER_ERROR_MODE_ABORT,
 overwrite_mode = gnomevfs.XFER_OVERWRITE_MODE_REPLACE,
 progress_update_callback = self.__purchase_download_update_cb,
 update_callback_data = (to_file_uri, library_location, audio_dl_uri),
 progress_sync_callback = self.__purchase_download_progress_cb,
 sync_callback_data = (to_file_uri, audio_dl_uri))
 
 def __purchase_download_update_cb(self, _reserved, info, data):
 if (info.phase == gnomevfs.XFER_PHASE_COMPLETED):
 to_file_uri = data[0]
 library_location = data[1]
 audio_dl_uri = data[2]
 
 try:
 del self.__downloads[str(audio_dl_uri)]
 except:
 return 0
 self.purchase_filesize -= gnomevfs.get_file_info(audio_dl_uri).size
 album = zipfile.ZipFile(to_file_uri.path)
 for track in album.namelist():
 track_uri = gnomevfs.URI(library_location + "/" + track)
 out = create_if_needed(track_uri, gnomevfs.OPEN_WRITE)
 out.write(album.read(track))
 out.close()
 album.close()
 gnomevfs.unlink(gnomevfs.URI(magnatune_dir + "in_progress_" + to_file_uri.short_name))
 gnomevfs.unlink(to_file_uri)
 if self.purchase_filesize == 0:
 self.__downloading = False
 self.__db.add_uri("file://" + urllib.quote(track_uri.dirname))
 return 1
 
 def __purchase_download_progress_cb(self, info, data):
 to_file_uri = data[0]
 audio_dl_uri = data[1]
 
 if self.cancelled:
 try:
 del self.__downloads[str(audio_dl_uri)]
 self.purchase_filesize -= gnomevfs.get_file_info(audio_dl_uri).size
 gnomevfs.unlink(gnomevfs.URI(magnatune_dir + "in_progress_" + to_file_uri.short_name))
 gnomevfs.unlink(to_file_uri)
 except: # this may get run more than once
 pass
 if self.purchase_filesize == 0:
 self.__downloading = False
 return 0
 
 self.__downloads[str(audio_dl_uri)] = info.bytes_copied
 purchase_downloaded = 0
 for i in self.__downloads.values():
 purchase_downloaded += i
 self.__download_progress = purchase_downloaded / float(self.purchase_filesize)
 self.__notify_status_changed()
 return 1
 
 def cancel_downloads(self):
 self.cancelled = True
 shell = self.get_property('shell')
 manager = shell.get_player().get_property('ui-manager')
 manager.get_action("/MagnatuneSourceViewPopup/MagnatuneCancelDownload").set_sensitive(False)
 
 def playing_entry_changed (self, entry):
 if not self.__db or not entry:
 return
 
 if entry.get_entry_type() != self.__db.entry_type_get_by_name("MagnatuneEntryType"):
 return
 
 gobject.idle_add (self.emit_cover_art_uri, entry)
 
 def emit_cover_art_uri (self, entry):
 sku = self.__sku_dict[self.__db.entry_get(entry, rhythmdb.PROP_LOCATION)]
 url = self.__art_dict[sku]
 self.__db.emit_entry_extra_metadata_notify (entry, 'rb:coverArt-uri', url)
 return False
 
 gobject.type_register(MagnatuneSource)
 
 
 def create_if_needed(uri, mode):
 if not gnomevfs.exists(uri):
 for directory in URIIterator(uri):
 if not gnomevfs.exists(directory):
 gnomevfs.make_directory(directory, 0755)
 out = gnomevfs.create(uri, open_mode=mode)
 else:
 out = gnomevfs.open(uri, open_mode=mode)
 return out
 
 class URIIterator:
 def __init__(self, uri):
 self.uri_list = uri.dirname.split("/")[1:] # dirname starts with /
 self.counter = 0
 def __iter__(self):
 return self
 def next(self):
 if self.counter == len(self.uri_list) + 1:
 raise StopIteration
 value = "file://"
 for i in range(self.counter):
 value += "/" + self.uri_list[i]
 self.counter += 1
 return gnomevfs.URI(value)
 
 |