improved synchronization dialog
git-svn-id: svn://svn.berlios.de/gpodder/trunk@289 b0d088ad-0a06-0410-aad2-9ed5178a7e87
This commit is contained in:
parent
92ca10876d
commit
30b86a530e
11
ChangeLog
11
ChangeLog
|
@ -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
|
||||
|
||||
|
|
|
@ -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"><b><big>Synchronizing Podcasts</big></b></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>
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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'))
|
||||
|
|
Loading…
Reference in New Issue