improved synchronization dialog

git-svn-id: svn://svn.berlios.de/gpodder/trunk@289 b0d088ad-0a06-0410-aad2-9ed5178a7e87
This commit is contained in:
Thomas Perl 2007-03-22 16:35:51 +00:00
parent 92ca10876d
commit 30b86a530e
5 changed files with 198 additions and 50 deletions

View File

@ -1,3 +1,14 @@
Thu, 22 Mar 2007 17:33:06 +0100 <thp@perli.net>
* src/gpodder/libipodsync.py: Added support for (optional) cancelling
of the Podcast synchronization process; add some more support for
setting status on the dialog; wait for iPod to be connected when
syncing to iPod (for 30 seconds at the moment); manually write and
read the episode files for FS-based MP3 player sync, so we can have
a more detailed and more often updated progress bar
* src/gpodder/gpodder.py: Support code for new gPodderSync dialog
goodness (yay!)
* data/gpodder.glade: Add cancel button to gPodderSync dialog
Thu, 22 Mar 2007 13:14:57 +0100 <thp@perli.net>
* bin/gpodder: Bump version + release date for debian packaging

View File

@ -3830,7 +3830,7 @@ you can use the usual format of the environment variables:
<property name="spacing">5</property>
<child>
<widget class="GtkLabel" id="label65">
<widget class="GtkLabel" id="label_header">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;&lt;big&gt;Synchronizing Podcasts&lt;/big&gt;&lt;/b&gt;</property>
<property name="use_underline">False</property>
@ -3855,7 +3855,7 @@ you can use the usual format of the environment variables:
</child>
<child>
<widget class="GtkLabel" id="label66">
<widget class="GtkLabel" id="label_text">
<property name="visible">True</property>
<property name="label" translatable="yes">Episodes marked for synchronization are now transferred to your player device.</property>
<property name="use_underline">False</property>
@ -3888,7 +3888,7 @@ you can use the usual format of the environment variables:
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
@ -3973,6 +3973,41 @@ you can use the usual format of the environment variables:
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox22">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">5</property>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkButton" id="cancel_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_cancel_button_clicked" last_modification_time="Thu, 22 Mar 2007 15:11:49 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>

View File

@ -338,10 +338,7 @@ class Gpodder(SimpleGladeApp):
def sync_to_ipod_proc( self, sync, episodes = None):
if not sync.open():
title = _('Cannot access device')
message = _('Make sure your device is connected to your computer and mounted. Please also make sure you have set the correct path to your device in the preferences dialog.')
gobject.idle_add( self.show_message, message, title)
sync.close()
sync.close( success = False, access_error = True)
return False
if episodes == None:
@ -355,18 +352,16 @@ class Gpodder(SimpleGladeApp):
else:
sync.sync_channel( self.active_channel, episodes)
sync.close()
sync.close( success = not sync.cancelled)
def ipod_cleanup_proc( self, sync):
if not sync.open():
title = _('Cannot access device')
message = _('Make sure your device is connected to your computer and mounted. Please also make sure you have set the correct path to your device in the preferences dialog.')
gobject.idle_add( self.show_message, message, title)
sync.close()
sync.close( success = False, access_error = True)
return False
sync.clean_playlist()
sync.close()
sync.close( success = not sync.cancelled, cleaned = True)
def update_feed_cache_callback( self, label, progressbar, position, count):
title = _('Please wait...')
@ -592,6 +587,7 @@ class Gpodder(SimpleGladeApp):
sync_win = Gpoddersync( gpodderwindow = self.gPodder)
sync = sync_class( callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
sync_win.set_sync_object( sync)
thread_args = [ sync ]
if widget == None:
thread_args.append( args[0])
@ -633,6 +629,7 @@ class Gpodder(SimpleGladeApp):
sync_win = Gpoddersync( gpodderwindow = self.gPodder)
sync = sync_class( callback_status = sync_win.set_status, callback_progress = sync_win.set_progress, callback_done = sync_win.close)
sync_win.set_sync_object( sync)
thread = Thread( target = self.ipod_cleanup_proc, args = ( sync, ))
thread.start()
#-- Gpodder.on_cleanup_ipod_activate }
@ -1427,6 +1424,11 @@ class Gpoddersync(SimpleGladeApp):
self.max_overall = 1
self.pos_episode = 0
self.max_episode = 1
self.cancel_button.set_sensitive( False)
self.sync = None
self.default_title = self.gPodderSync.get_title()
self.default_header = self.label_header.get_text()
self.default_body = self.label_text.get_text()
#-- Gpoddersync.new {
def new(self):
@ -1434,7 +1436,13 @@ class Gpoddersync(SimpleGladeApp):
#-- Gpoddersync.new }
#-- Gpoddersync custom methods {
def set_sync_object( self, sync):
self.sync = sync
if self.sync.can_cancel:
self.cancel_button.set_sensitive( True)
def set_progress( self, pos, max, is_overall = False, is_sub_episode = False):
pos = min(pos, max)
if is_sub_episode:
fraction_episode = 1.0*(self.pos_episode+1.0*pos/max)/self.max_episode
self.pbEpisode.set_fraction( fraction_episode)
@ -1456,7 +1464,7 @@ class Gpoddersync(SimpleGladeApp):
percent = _('%d of %d done') % ( pos, max )
progressbar.set_text( percent)
def set_status( self, episode = None, channel = None, progressbar = None):
def set_status( self, episode = None, channel = None, progressbar = None, title = None, header = None, body = None):
if episode != None:
self.labelEpisode.set_markup( '<i>%s</i>' % episode)
@ -1466,8 +1474,53 @@ class Gpoddersync(SimpleGladeApp):
if progressbar != None:
self.pbSync.set_text( progressbar)
def close( self):
self.gPodderSync.destroy()
if title != None:
self.gPodderSync.set_title( title)
else:
self.gPodderSync.set_title( self.default_title)
if header != None:
self.label_header.set_markup( '<b><big>%s</big></b>' % header)
else:
self.label_header.set_markup( '<b><big>%s</big></b>' % self.default_header)
if body != None:
self.label_text.set_markup( body)
else:
self.label_text.set_markup( self.default_body)
def close( self, success = True, access_error = False, cleaned = False):
if self.sync:
self.sync.cancelled = True
self.cancel_button.set_label( gtk.STOCK_CLOSE)
self.cancel_button.set_use_stock( True)
self.cancel_button.set_sensitive( True)
self.gPodderSync.set_resizable( True)
self.pbSync.hide_all()
self.pbEpisode.hide_all()
self.labelChannel.hide_all()
self.labelEpisode.hide_all()
self.gPodderSync.set_resizable( False)
if success and not cleaned:
title = _('Synchronization finished')
header = _('Copied Podcasts')
body = _('The selected episodes have been copied to your device. You can now unplug the device.')
elif access_error:
title = _('Synchronization error')
header = _('Cannot access device')
body = _('Make sure your device is connected to your computer and mounted. Please also make sure you have set the correct path to your device in the preferences dialog.')
elif cleaned:
title = _('Device cleaned')
header = _('Podcasts removed')
body = _('Synchronized Podcasts have been removed from your device.')
else:
title = _('Synchronization aborted')
header = _('Aborted')
body = _('The synchronization progress has been interrupted by the user. Please retry synchronization at a later time.')
self.gPodderSync.set_title( title)
self.label_header.set_markup( '<big><b>%s</b></big>' % header)
self.label_text.set_text( body)
#-- Gpoddersync custom methods }
#-- Gpoddersync.on_gPodderSync_destroy {
@ -1475,6 +1528,18 @@ class Gpoddersync(SimpleGladeApp):
pass
#-- Gpoddersync.on_gPodderSync_destroy }
#-- Gpoddersync.on_cancel_button_clicked {
def on_cancel_button_clicked(self, widget, *args):
if self.sync:
if self.sync.cancelled:
self.gPodderSync.destroy()
else:
self.sync.cancelled = True
self.cancel_button.set_sensitive( False)
else:
self.gPodderSync.destroy()
#-- Gpoddersync.on_cancel_button_clicked }
class Gpodderopmllister(SimpleGladeApp):

View File

@ -342,6 +342,11 @@ class Gpoddersync(SimpleGladeApp):
print "on_gPodderSync_destroy called with self.%s" % widget.get_name()
#-- Gpoddersync.on_gPodderSync_destroy }
#-- Gpoddersync.on_cancel_button_clicked {
def on_cancel_button_clicked(self, widget, *args):
print "on_cancel_button_clicked called with self.%s" % widget.get_name()
#-- Gpoddersync.on_cancel_button_clicked }
class Gpodderopmllister(SimpleGladeApp):

View File

@ -117,6 +117,8 @@ class gPodderSyncMethod:
self.callback_progress = callback_progress
self.callback_status = callback_status
self.callback_done = callback_done
self.cancelled = False
self.can_cancel = False
def open( self):
return False
@ -143,16 +145,12 @@ class gPodderSyncMethod:
self.set_progress_sub_episode( int(percentage), 100)
self.set_status( episode = _('Converting %s (%s%%)') % ( episode, str(percentage), ))
def set_status( self, episode = None, channel = None, progressbar_text = None):
def set_status( self, episode = None, channel = None, progressbar_text = None, title = None, header = None, body = None):
if self.callback_status:
gobject.idle_add( self.callback_status, episode, channel, progressbar_text)
def set_done( self):
if self.callback_done:
gobject.idle_add( self.callback_done)
gobject.idle_add( self.callback_status, episode, channel, progressbar_text, title, header, body)
def sync_channel( self, channel, episodes = None):
if not channel.sync_to_devices and episodes == None:
if not channel.sync_to_devices and episodes == None or self.cancelled:
return False
if episodes == None:
@ -162,6 +160,8 @@ class gPodderSyncMethod:
pos = 0
for episode in episodes:
if self.cancelled:
return False
self.set_progress( pos, max)
if channel.is_downloaded( episode):
self.add_episode_from_channel( channel, episode)
@ -182,8 +182,15 @@ class gPodderSyncMethod:
self.set_episode_status( episode.title)
self.set_channel_status( channeltext)
def close( self):
self.set_done()
def close( self, success = True, access_error = False, cleaned = False):
try:
self.set_status( channel = _('Writing data to disk'))
os.system('sync')
except:
pass
if self.callback_done:
gobject.idle_add( self.callback_done, success, access_error, cleaned)
class gPodder_iPodSync( gPodderSyncMethod):
@ -203,28 +210,39 @@ class gPodder_iPodSync( gPodderSyncMethod):
def open( self):
if not ipod_supported():
return False
if self.itdb == None:
self.itdb = gpod.itdb_parse( str( self.ipod_mount), None)
if not self.itdb:
return False
self.itdb.mountpoint = str(self.ipod_mount)
self.pl_master = gpod.sw_get_playlists( self.itdb)[0]
if not self.pl_master:
return False
self.pl_podcasts = gpod.itdb_playlist_podcasts( self.itdb)
if not self.pl_podcasts:
return False
return True
tries = 0
while self.itdb == None and not self.cancelled:
if os.path.isdir( self.ipod_mount):
self.itdb = gpod.itdb_parse( str( self.ipod_mount), None)
if self.itdb:
self.itdb.mountpoint = str(self.ipod_mount)
self.pl_master = gpod.sw_get_playlists( self.itdb)[0]
self.pl_podcasts = gpod.itdb_playlist_podcasts( self.itdb)
if not self.pl_master or not self.pl_podcasts:
return False
else:
header = _('Connect your iPod')
body = _('To start the synchronization process, please connect your iPod to the computer.')
if tries > 30:
return False
elif tries > 15:
self.set_status( episode = _('Have you set up your iPod correctly?'), header = header, body = body)
else:
self.set_status( channel = _('Please connect your iPod'), episode = _('Waiting for iPod'), header = header, body = body)
def close( self):
time.sleep( 1)
tries += 1
return self.itdb != None
def close( self, success = True, access_error = False, cleaned = False):
if not ipod_supported():
return False
if self.itdb:
self.set_status( channel = _('Saving iPod database'))
gpod.itdb_write( self.itdb, None)
self.itdb = None
time.sleep( 1)
gPodderSyncMethod.close( self)
gPodderSyncMethod.close( self, success, access_error, cleaned)
return True
def remove_from_ipod( self, track, playlists):
@ -435,11 +453,6 @@ class gPodder_iPodSync( gPodderSyncMethod):
status_text = 'Done: %s' % ( episode.title, )
self.set_status( episode = status_text)
try:
os.system('sync')
except:
pass
if local_filename != original_filename:
log('(ipodsync) Removing temporary file: %s', local_filename)
try:
@ -450,10 +463,13 @@ class gPodder_iPodSync( gPodderSyncMethod):
class gPodder_FSSync( gPodderSyncMethod):
BUFFER = 1024*1024*1 # 1MB
def __init__( self, callback_progress = None, callback_status = None, callback_done = None):
gl = libgpodder.gPodderLib()
self.destination = gl.mp3_player_folder
gPodderSyncMethod.__init__( self, callback_progress, callback_status, callback_done)
self.can_cancel = True
def open( self):
gpl = libgpodder.gPodderLib()
@ -483,16 +499,32 @@ class gPodder_FSSync( gPodderSyncMethod):
if not os.path.exists( to_file):
log( 'Copying: %s => %s', os.path.basename( from_file), to_file)
shutil.copyfile( from_file, to_file)
try:
os.system('sync')
except:
pass
self.copy_file_progress( from_file, to_file)
#shutil.copyfile( from_file, to_file)
def copy_file_progress( self, from_file, to_file):
out_file = open( to_file, 'wb')
in_file = open( from_file, 'rb')
in_file.seek( 0, 2)
bytes = in_file.tell()
bytes_read = 0
in_file.seek( 0)
s = in_file.read( self.BUFFER)
bytes_read += len(s)
self.set_progress_sub_episode( bytes_read, bytes)
while s:
bytes_read += len(s)
out_file.write( s)
self.set_progress_sub_episode( bytes_read, bytes)
s = in_file.read( self.BUFFER)
out_file.close()
in_file.close()
def clean_playlist( self):
folders = glob.glob( os.path.join( self.destination, '*'))
for folder in range( len( folders)):
if self.cancelled:
return
self.set_progress_overall( folder+1, len( folders))
self.set_channel_status( os.path.basename( folders[folder]))
self.set_status( episode = _('Removing files'))