def synchronize_device(db, config): device = sync.open_device(config) if device is None: print >>sys.stderr, _('No device configured.') return False def msg(s): print >>sys.stderr, s device.register('status', msg) def callback_progress(index, count): d = {'index': index, 'count': count} msg(_('Synchronizing: %(index)s of %(count)s') % d) device.register('progress', callback_progress) if device.open(): channels = [c for c in PodcastChannel.load_from_db(db, \ config.download_dir) if c.sync_to_devices] for channel in channels: episodes = [e for e in channel.get_downloaded_episodes() \ if e.was_downloaded(and_exists=True)] device.add_tracks(episodes) db.commit() device.close() print >>sys.stderr, _('Device synchronized successfully.') return True else: print >>sys.stderr, _('Error: Cannot open device!') return False
def sync_device(): device=sync.open_device() if device is None: msg('error', _('No device configured. Please use the GUI.')) return False callback_status=lambda s: msg('status', s) device.register('status', callback_status) callback_done=lambda: msg('done', _('Synchronization finished.')) device.register('done', callback_done) callback_progress=lambda i, n: msg('progress', _('Synchronizing: %d of %d') % (i, n)) device.register('progress', callback_progress) if not device.open(): msg('error', _('Cannot open device.')) return False for channel in load_channels(): if not channel.sync_to_devices: msg('info', _('Skipping podcast: %s') % channel.title) continue episodes_to_sync=[] for episode in channel.get_all_episodes(): if episode.is_downloaded(): episodes_to_sync.append(episode) device.add_tracks(episodes_to_sync) if not device.close(): msg('error', _('Cannot close device.')) return False
def on_cleanup_device(self): columns = ( ('title', None, None, _('Episode')), ('podcast', None, None, _('Podcast')), ('filesize', 'length', int, _('Size')), ('modified', 'modified_sort', int, _('Copied')), ('playcount_str', 'playcount', int, _('Play count')), ('released', None, None, _('Released')), ) device = sync.open_device(self._config) if device is None: return self._show_message_unconfigured() if not device.open(): return self._show_message_cannot_open() tracks = device.get_all_tracks() if tracks: def remove_tracks_callback(tracks): title = _('Delete podcasts from device?') message = _('Do you really want to remove these episodes from your device? Episodes in your library will not be deleted.') if tracks and self.show_confirmation(message, title): gPodderSyncProgress(self.parent_window, device=device) def cleanup_thread_func(): device.remove_tracks(tracks) if not device.close(): title = _('Error closing device') message = _('There has been an error closing your device.') self.notification(message, title, important=True) threading.Thread(target=cleanup_thread_func).start() wanted_columns = [] for key, sort_name, sort_type, caption in columns: want_this_column = False for track in tracks: if getattr(track, key) is not None: want_this_column = True break if want_this_column: wanted_columns.append((key, sort_name, sort_type, caption)) title = _('Remove podcasts from device') instructions = _('Select episodes to remove from your device.') self.episode_selector_class(self.parent_window, title=title, \ instructions=instructions, \ episodes=tracks, columns=wanted_columns, \ stock_ok_button=gtk.STOCK_DELETE, \ callback=remove_tracks_callback, \ tooltip_attribute=None, \ _config=self._config) else: device.close() title = _('No files on device') message = _('The devices contains no files to be removed.') self.notification(message, title)
def on_manage_device_playlist(self): if self._config.device_type == 'ipod' and not sync.gpod_available: title = _('Cannot manage iPod playlist') message = _('This feature is not available for iPods.') self.notification(message, title) return elif self._config.device_type == 'mtp' and not sync.pymtp_available: title = _('Cannot manage MTP device playlist') message = _('This feature is not available for MTP devices.') self.notification(message, title) return device = sync.open_device(self._config) if device is None: return self._show_message_unconfigured() if not device.open(): return self._show_message_cannot_open() gPodderDevicePlaylist(self.parent_window, \ device=device, \ _config=self._config) device.close()
def synchronize_device(db, config): device = sync.open_device(config) if device is None: print >> sys.stderr, _('No device configured.') return False def msg(s): print >> sys.stderr, s device.register('status', msg) def callback_progress(index, count): d = {'index': index, 'count': count} msg(_('Synchronizing: %(index)s of %(count)s') % d) device.register('progress', callback_progress) if device.open(): channels = [c for c in PodcastChannel.load_from_db(db, \ config.download_dir) if c.sync_to_devices] for channel in channels: episodes = [e for e in channel.get_downloaded_episodes() \ if e.was_downloaded(and_exists=True)] device.add_tracks(episodes) if config.ipod_purge_old_episodes: device.purge() db.commit() device.close() print >> sys.stderr, _('Device synchronized successfully.') return True else: print >> sys.stderr, _('Error: Cannot open device!') return False
def on_synchronize_episodes(self, channels, episodes=None, force_played=True): if self._config.device_type == 'ipod' and not sync.gpod_available: title = _('Cannot sync to iPod') message = _('Please install python-gpod and restart gPodder.') self.notification(message, title, important=True) return elif self._config.device_type == 'mtp' and not sync.pymtp_available: title = _('Cannot sync to MTP device') message = _('Please install libmtp and restart gPodder.') self.notification(message, title, important=True) return device = sync.open_device(self._config) if device is not None: def after_device_sync_callback(device, successful_sync): if device.cancelled: log('Cancelled by user.', sender=self) elif successful_sync: title = _('Device synchronized') message = _('Your device has been synchronized.') self.notification(message, title) else: title = _('Error closing device') message = _('Please check settings and permission.') self.notification(message, title, important=True) # Update the UI to reflect changes from the sync process episode_urls = set() channel_urls = set() for episode in episodes: episode_urls.add(episode.url) channel_urls.add(episode.channel.url) util.idle_add(self.update_episode_list_icons, episode_urls) util.idle_add(self.update_podcast_list_model, channel_urls) util.idle_add(self.commit_changes_to_database) device.register('post-done', after_device_sync_callback) if device is None: return self._show_message_unconfigured() if not device.open(): return self._show_message_cannot_open() if self._config.device_type == 'ipod': #update played episodes and delete if requested for channel in channels: if channel.sync_to_devices: allepisodes = [e for e in channel.get_all_episodes() \ if e.was_downloaded(and_exists=True)] device.update_played_or_delete(channel, allepisodes, \ self._config.ipod_delete_played_from_db) if self._config.ipod_purge_old_episodes: device.purge() if episodes is None: force_played = False episodes = self._filter_sync_episodes(channels) def check_free_space(): # "Will we add this episode to the device?" def will_add(episode): # If already on-device, it won't take up any space if device.episode_on_device(episode): return False # Might not be synced if it's played already if not force_played and \ self._config.only_sync_not_played and \ episode.is_played: return False # In all other cases, we expect the episode to be # synchronized to the device, so "answer" positive return True # "What is the file size of this episode?" def file_size(episode): filename = episode.local_filename(create=False) if filename is None: return 0 return util.calculate_size(str(filename)) # Calculate total size of sync and free space on device total_size = sum(file_size(e) for e in episodes if will_add(e)) free_space = max(device.get_free_space(), 0) if total_size > free_space: title = _('Not enough space left on device') message = _('You need to free up %s.\nDo you want to continue?') \ % (util.format_filesize(total_size-free_space),) if not self.show_confirmation(message, title): device.cancel() device.close() return # Finally start the synchronization process gPodderSyncProgress(self.parent_window, device=device) def sync_thread_func(): device.add_tracks(episodes, force_played=force_played) device.close() threading.Thread(target=sync_thread_func).start() # This function is used to remove files from the device def cleanup_episodes(): # 'only_sync_not_played' must be used or else all the # played tracks will be copied then immediately deleted if self._config.mp3_player_delete_played and \ self._config.only_sync_not_played: all_episodes = self._filter_sync_episodes(channels, \ only_downloaded=False) episodes_on_device = device.get_all_tracks() for local_episode in all_episodes: episode = device.episode_on_device(local_episode) if episode is None: continue if local_episode.state == gpodder.STATE_DELETED \ or (local_episode.is_played and \ not local_episode.is_locked): log('Removing episode from device: %s', episode.title, sender=self) device.remove_track(episode) # When this is done, start the callback in the UI code util.idle_add(check_free_space) # This will run the following chain of actions: # 1. Remove old episodes (in worker thread) # 2. Check for free space (in UI thread) # 3. Sync the device (in UI thread) threading.Thread(target=cleanup_episodes).start()
def on_synchronize_episodes(self, channels, episodes=None, force_played=True): device = sync.open_device(self) if device is None: return self._show_message_unconfigured() if not device.open(): return self._show_message_cannot_open() else: # Only set if device is configured and opened successfully self.device = device if episodes is None: force_played = False episodes = self._filter_sync_episodes(channels) def check_free_space(): # "Will we add this episode to the device?" def will_add(episode): # If already on-device, it won't take up any space if device.episode_on_device(episode): return False # Might not be synced if it's played already if (not force_played and self._config.device_sync.skip_played_episodes): return False # In all other cases, we expect the episode to be # synchronized to the device, so "answer" positive return True # "What is the file size of this episode?" def file_size(episode): filename = episode.local_filename(create=False) if filename is None: return 0 return util.calculate_size(str(filename)) # Calculate total size of sync and free space on device total_size = sum(file_size(e) for e in episodes if will_add(e)) free_space = max(device.get_free_space(), 0) if total_size > free_space: title = _('Not enough space left on device') message = (_('Additional free space required: %(required_space)s\nDo you want to continue?') % {'required_space': util.format_filesize(total_size - free_space)}) if not self.show_confirmation(message, title): device.cancel() device.close() return #enable updating of UI self.enable_download_list_update() #Update device playlists #General approach is as follows: #When a episode is downloaded and synched, it is added to the #standard playlist for that podcast which is then written to #the device. #After the user has played that episode on their device, they #can delete that episode from their device. #At the next sync, gPodder will then compare the standard #podcast-specific playlists on the device (as written by #gPodder during the last sync), with the episodes on the #device.If there is an episode referenced in the playlist #that is no longer on the device, gPodder will assume that #the episode has already been synced and subsequently deleted #from the device, and will hence mark that episode as deleted #in gPodder. If there are no playlists, nothing is deleted. #At the next sync, the playlists will be refreshed based on #the downloaded, undeleted episodes in gPodder, and the #cycle begins again... def resume_sync(episode_urls, channel_urls,progress): if progress is not None: progress.on_finished() #rest of sync process should continue here self.commit_changes_to_database() for current_channel in self.channels: #only sync those channels marked for syncing if (self._config.device_sync.device_type=='filesystem' and current_channel.sync_to_mp3_player and self._config.device_sync.playlists.create): #get playlist object playlist=gPodderDevicePlaylist(self._config, current_channel.title) #need to refresh episode list so that #deleted episodes aren't included in playlists episodes_for_playlist=sorted(current_channel.get_episodes(gpodder.STATE_DOWNLOADED), key=lambda ep: ep.published) #don't add played episodes to playlist if skip_played_episodes is True if self._config.device_sync.skip_played_episodes: episodes_for_playlist=[ep for ep in episodes_for_playlist if ep.is_new] playlist.write_m3u(episodes_for_playlist) #enable updating of UI self.enable_download_list_update() if (self._config.device_sync.device_type=='filesystem' and self._config.device_sync.playlists.create): title = _('Update successful') message = _('The playlist on your MP3 player has been updated.') self.notification(message, title) # Finally start the synchronization process @util.run_in_background def sync_thread_func(): device.add_sync_tasks(episodes, force_played=force_played, done_callback=self.enable_download_list_update) return if self._config.device_sync.playlists.create: try: episodes_to_delete=[] if self._config.device_sync.playlists.two_way_sync: for current_channel in self.channels: #only include channels that are included in the sync if current_channel.sync_to_mp3_player: #get playlist object playlist=gPodderDevicePlaylist(self._config, current_channel.title) #get episodes to be written to playlist episodes_for_playlist=sorted(current_channel.get_episodes(gpodder.STATE_DOWNLOADED), key=lambda ep: ep.published) episode_keys=list(map(playlist.get_absolute_filename_for_playlist, episodes_for_playlist)) episode_dict=dict(list(zip(episode_keys, episodes_for_playlist))) #then get episodes in playlist (if it exists) already on device episodes_in_playlists = playlist.read_m3u() #if playlist doesn't exist (yet) episodes_in_playlist will be empty if episodes_in_playlists: for episode_filename in episodes_in_playlists: if not(os.path.exists(os.path.join(playlist.mountpoint, episode_filename))): #episode was synced but no longer on device #i.e. must have been deleted by user, so delete from gpodder try: episodes_to_delete.append(episode_dict[episode_filename]) except KeyError as ioe: logger.warn('Episode %s, removed from device has already been deleted from gpodder', episode_filename) #delete all episodes from gpodder (will prompt user) #not using playlists to delete def auto_delete_callback(episodes): if not episodes: #episodes were deleted on device #but user decided not to delete them from gpodder #so jump straight to sync logger.info('Starting sync - no episodes selected for deletion') resume_sync([],[],None) else: #episodes need to be deleted from gpodder for episode_to_delete in episodes: logger.info("Deleting episode %s", episode_to_delete.title) logger.info('Will start sync - after deleting episodes') self.delete_episode_list(episodes,False, True,resume_sync) return if episodes_to_delete: columns = ( ('markup_delete_episodes', None, None, _('Episode')), ) gPodderEpisodeSelector(self.parent_window, title = _('Episodes have been deleted on device'), instructions = 'Select the episodes you want to delete:', episodes = episodes_to_delete, selected = [True,] * len(episodes_to_delete), columns = columns, callback = auto_delete_callback, _config=self._config) else: logger.warning("Starting sync - no episodes to delete") resume_sync([],[],None) except IOError as ioe: title = _('Error writing playlist files') message = _(str(ioe)) self.notification(message, title) else: logger.info('Not creating playlists - starting sync') resume_sync([],[],None) # This function is used to remove files from the device def cleanup_episodes(): # 'skip_played_episodes' must be used or else all the # played tracks will be copied then immediately deleted if (self._config.device_sync.delete_played_episodes and self._config.device_sync.skip_played_episodes): all_episodes = self._filter_sync_episodes(channels, only_downloaded=False) for local_episode in all_episodes: episode = device.episode_on_device(local_episode) if episode is None: continue if local_episode.state == gpodder.STATE_DELETED: logger.info('Removing episode from device: %s', episode.title) device.remove_track(episode) # When this is done, start the callback in the UI code util.idle_add(check_free_space) # This will run the following chain of actions: # 1. Remove old episodes (in worker thread) # 2. Check for free space (in UI thread) # 3. Sync the device (in UI thread) util.run_in_background(cleanup_episodes)
def on_synchronize_episodes(self, channels, episodes=None, force_played=True): device = sync.open_device(self) if device is None: return self._show_message_unconfigured() if not device.open(): return self._show_message_cannot_open() else: # Only set if device is configured and opened successfully self.device = device if episodes is None: force_played = False episodes = self._filter_sync_episodes(channels) def check_free_space(): # "Will we add this episode to the device?" def will_add(episode): # If already on-device, it won't take up any space if device.episode_on_device(episode): return False # Might not be synced if it's played already if (not force_played and self._config.device_sync.skip_played_episodes): return False # In all other cases, we expect the episode to be # synchronized to the device, so "answer" positive return True # "What is the file size of this episode?" def file_size(episode): filename = episode.local_filename(create=False) if filename is None: return 0 return util.calculate_size(str(filename)) # Calculate total size of sync and free space on device total_size = sum(file_size(e) for e in episodes if will_add(e)) free_space = max(device.get_free_space(), 0) if total_size > free_space: title = _('Not enough space left on device') message = (_( 'Additional free space required: %(required_space)s\nDo you want to continue?' ) % { 'required_space': util.format_filesize(total_size - free_space) }) if not self.show_confirmation(message, title): device.cancel() device.close() return # Finally start the synchronization process @util.run_in_background def sync_thread_func(): self.enable_download_list_update() device.add_sync_tasks(episodes, force_played=force_played) # This function is used to remove files from the device def cleanup_episodes(): # 'skip_played_episodes' must be used or else all the # played tracks will be copied then immediately deleted if (self._config.device_sync.delete_played_episodes and self._config.device_sync.skip_played_episodes): all_episodes = self._filter_sync_episodes( channels, only_downloaded=False) episodes_on_device = device.get_all_tracks() for local_episode in all_episodes: episode = device.episode_on_device(local_episode) if episode is None: continue if local_episode.state == gpodder.STATE_DELETED: logger.info('Removing episode from device: %s', episode.title) device.remove_track(episode) # When this is done, start the callback in the UI code util.idle_add(check_free_space) # This will run the following chain of actions: # 1. Remove old episodes (in worker thread) # 2. Check for free space (in UI thread) # 3. Sync the device (in UI thread) util.run_in_background(cleanup_episodes)
def on_synchronize_episodes(self, channels, episodes=None, force_played=True): device = sync.open_device(self) if device is None: return self._show_message_unconfigured() if not device.open(): return self._show_message_cannot_open() else: # Only set if device is configured and opened successfully self.device = device if episodes is None: force_played = False episodes = self._filter_sync_episodes(channels) def check_free_space(): # "Will we add this episode to the device?" def will_add(episode): # If already on-device, it won't take up any space if device.episode_on_device(episode): return False # Might not be synced if it's played already if (not force_played and self._config.device_sync.skip_played_episodes): return False # In all other cases, we expect the episode to be # synchronized to the device, so "answer" positive return True # "What is the file size of this episode?" def file_size(episode): filename = episode.local_filename(create=False) if filename is None: return 0 return util.calculate_size(str(filename)) # Calculate total size of sync and free space on device total_size = sum(file_size(e) for e in episodes if will_add(e)) free_space = max(device.get_free_space(), 0) if total_size > free_space: title = _('Not enough space left on device') message = (_('Additional free space required: %(required_space)s\nDo you want to continue?') % {'required_space': util.format_filesize(total_size - free_space)}) if not self.show_confirmation(message, title): device.cancel() device.close() return #enable updating of UI self.enable_download_list_update() #Update device playlists #General approach is as follows: #When a episode is downloaded and synched, it is added to the #standard playlist for that podcast which is then written to #the device. #After the user has played that episode on their device, they #can delete that episode from their device. #At the next sync, gPodder will then compare the standard #podcast-specific playlists on the device (as written by #gPodder during the last sync), with the episodes on the #device.If there is an episode referenced in the playlist #that is no longer on the device, gPodder will assume that #the episode has already been synced and subsequently deleted #from the device, and will hence mark that episode as deleted #in gPodder. If there are no playlists, nothing is deleted. #At the next sync, the playlists will be refreshed based on #the downloaded, undeleted episodes in gPodder, and the #cycle begins again... def resume_sync(episode_urls, channel_urls,progress): if progress is not None: progress.on_finished() #rest of sync process should continue here self.commit_changes_to_database() for current_channel in self.channels: #only sync those channels marked for syncing if (self._config.device_sync.device_type=='filesystem' and current_channel.sync_to_mp3_player and self._config.device_sync.playlists.create): #get playlist object playlist=gPodderDevicePlaylist(self._config, current_channel.title) #need to refresh episode list so that #deleted episodes aren't included in playlists episodes_for_playlist=sorted(current_channel.get_episodes(gpodder.STATE_DOWNLOADED), key=lambda ep: ep.published) #don't add played episodes to playlist if skip_played_episodes is True if self._config.device_sync.skip_played_episodes: episodes_for_playlist=filter(lambda ep: ep.is_new, episodes_for_playlist) playlist.write_m3u(episodes_for_playlist) #enable updating of UI self.enable_download_list_update() if (self._config.device_sync.device_type=='filesystem' and self._config.device_sync.playlists.create): title = _('Update successful') message = _('The playlist on your MP3 player has been updated.') self.notification(message, title, widget=self.preferences_widget) # Finally start the synchronization process @util.run_in_background def sync_thread_func(): device.add_sync_tasks(episodes, force_played=force_played, done_callback=self.enable_download_list_update) return if self._config.device_sync.playlists.create: try: episodes_to_delete=[] if self._config.device_sync.playlists.two_way_sync: for current_channel in self.channels: #only include channels that are included in the sync if current_channel.sync_to_mp3_player: #get playlist object playlist=gPodderDevicePlaylist(self._config, current_channel.title) #get episodes to be written to playlist episodes_for_playlist=sorted(current_channel.get_episodes(gpodder.STATE_DOWNLOADED), key=lambda ep: ep.published) episode_keys=map(playlist.get_absolute_filename_for_playlist, episodes_for_playlist) episode_dict=dict(zip(episode_keys, episodes_for_playlist)) #then get episodes in playlist (if it exists) already on device episodes_in_playlists = playlist.read_m3u() #if playlist doesn't exist (yet) episodes_in_playlist will be empty if episodes_in_playlists: for episode_filename in episodes_in_playlists: if not(os.path.exists(os.path.join(playlist.mountpoint, episode_filename))): #episode was synced but no longer on device #i.e. must have been deleted by user, so delete from gpodder try: episodes_to_delete.append(episode_dict[episode_filename]) except KeyError, ioe: logger.warn('Episode %s, removed from device has already been deleted from gpodder', episode_filename) #delete all episodes from gpodder (will prompt user) #not using playlists to delete def auto_delete_callback(episodes): if not episodes: #episodes were deleted on device #but user decided not to delete them from gpodder #so jump straight to sync logger.info ('Starting sync - no episodes selected for deletion') resume_sync([],[],None) else: #episodes need to be deleted from gpodder for episode_to_delete in episodes: logger.info("Deleting episode %s", episode_to_delete.title) logger.info ('Will start sync - after deleting episodes') self.delete_episode_list(episodes,False, True,resume_sync) return if episodes_to_delete: columns = ( ('markup_delete_episodes', None, None, _('Episode')), ) gPodderEpisodeSelector(self.parent_window, title = _('Episodes have been deleted on device'), instructions = 'Select the episodes you want to delete:', episodes = episodes_to_delete, selected = [True,]*len(episodes_to_delete), columns = columns, callback = auto_delete_callback, _config=self._config) else: logger.warning("Starting sync - no episodes to delete") resume_sync([],[],None) except IOError, ioe: title = _('Error writing playlist files') message = _(str(ioe)) self.notification(message, title, widget=self.preferences_widget)
def on_cleanup_device(self): columns = ( ('title', None, None, _('Episode')), ('podcast', None, None, _('Podcast')), ('filesize', 'length', int, _('Size')), ('modified', 'modified_sort', int, _('Copied')), ('playcount', 'playcount', int, _('Play count')), ('released', None, None, _('Released')), ) device = sync.open_device(self._config) if device is None: return self._show_message_unconfigured() if not device.open(): return self._show_message_cannot_open() tracks = device.get_all_tracks() if tracks: def remove_tracks_callback(tracks): title = _('Delete podcasts from device?') message = _( 'Do you really want to remove these episodes from your device? Episodes in your library will not be deleted.' ) if tracks and self.show_confirmation(message, title): gPodderSyncProgress(self.parent_window, device=device) def cleanup_thread_func(): device.remove_tracks(tracks) if not device.close(): title = _('Error closing device') message = _( 'There has been an error closing your device.') self.notification(message, title, important=True) threading.Thread(target=cleanup_thread_func).start() wanted_columns = [] for key, sort_name, sort_type, caption in columns: want_this_column = False for track in tracks: if getattr(track, key) is not None: want_this_column = True break if want_this_column: wanted_columns.append((key, sort_name, sort_type, caption)) title = _('Remove podcasts from device') instructions = _('Select episodes to remove from your device.') self.episode_selector_class(self.parent_window, title=title, \ instructions=instructions, \ episodes=tracks, columns=wanted_columns, \ stock_ok_button=gtk.STOCK_DELETE, \ callback=remove_tracks_callback, \ tooltip_attribute=None, \ _config=self._config) else: device.close() title = _('No files on device') message = _('The devices contains no files to be removed.') self.notification(message, title)
def on_synchronize_episodes(self, channels, episodes=None, force_played=True): device = sync.open_device(self) if device is None: return self._show_message_unconfigured() if not device.open(): return self._show_message_cannot_open() else: # Only set if device is configured and opened successfully self.device = device if episodes is None: force_played = False episodes = self._filter_sync_episodes(channels) def check_free_space(): # "Will we add this episode to the device?" def will_add(episode): # If already on-device, it won't take up any space if device.episode_on_device(episode): return False # Might not be synced if it's played already if (not force_played and self._config.device_sync.skip_played_episodes): return False # In all other cases, we expect the episode to be # synchronized to the device, so "answer" positive return True # "What is the file size of this episode?" def file_size(episode): filename = episode.local_filename(create=False) if filename is None: return 0 return util.calculate_size(str(filename)) # Calculate total size of sync and free space on device total_size = sum(file_size(e) for e in episodes if will_add(e)) free_space = max(device.get_free_space(), 0) if total_size > free_space: title = _('Not enough space left on device') message = (_('Additional free space required: %(required_space)s\nDo you want to continue?') % {'required_space': util.format_filesize(total_size - free_space)}) if not self.show_confirmation(message, title): device.cancel() device.close() return # Finally start the synchronization process @util.run_in_background def sync_thread_func(): self.enable_download_list_update() device.add_sync_tasks(episodes, force_played=force_played) # This function is used to remove files from the device def cleanup_episodes(): # 'skip_played_episodes' must be used or else all the # played tracks will be copied then immediately deleted if (self._config.device_sync.delete_played_episodes and self._config.device_sync.skip_played_episodes): all_episodes = self._filter_sync_episodes(channels, only_downloaded=False) episodes_on_device = device.get_all_tracks() for local_episode in all_episodes: episode = device.episode_on_device(local_episode) if episode is None: continue if local_episode.state == gpodder.STATE_DELETED: logger.info('Removing episode from device: %s', episode.title) device.remove_track(episode) # When this is done, start the callback in the UI code util.idle_add(check_free_space) # This will run the following chain of actions: # 1. Remove old episodes (in worker thread) # 2. Check for free space (in UI thread) # 3. Sync the device (in UI thread) util.run_in_background(cleanup_episodes)