示例#1
0
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
示例#2
0
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
示例#3
0
    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)
示例#4
0
    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()
示例#5
0
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
示例#6
0
文件: sync.py 项目: xerxes2/gpodder
    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()
示例#7
0
    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()
示例#8
0
    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)
示例#9
0
    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)
示例#10
0
    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)
示例#11
0
文件: sync.py 项目: xerxes2/gpodder
    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()
示例#12
0
文件: sync.py 项目: xerxes2/gpodder
    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)
示例#13
0
文件: sync.py 项目: obris/gpodder
    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)