Device playlists & two-way sync
This commit is contained in:
parent
0434bb529b
commit
b9b752df40
|
@ -567,13 +567,13 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox_devices">
|
||||
<property name="border_width">12</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">12</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table_devices">
|
||||
<property name="visible">True</property>
|
||||
<property name="n_rows">3</property>
|
||||
<property name="n_rows">6</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="column_spacing">6</property>
|
||||
<property name="row_spacing">6</property>
|
||||
|
@ -618,7 +618,6 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="label" translatable="no"></property>
|
||||
<signal name="clicked" handler="on_btn_device_mountpoint_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
|
@ -637,10 +636,10 @@
|
|||
<property name="justify">right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -651,18 +650,78 @@
|
|||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">5</property>
|
||||
<property name="bottom_attach">6</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="checkbutton_create_playlists">
|
||||
<property name="label" translatable="yes">Create playlists on device</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_checkbutton_create_playlists_toggled"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_device_playlists">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">1</property>
|
||||
<property name="label" translatable="yes">Playlists Folder:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="btn_playlistfolder">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<signal name="clicked" handler="on_btn_playlist_folder_clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="checkbutton_delete_using_playlists">
|
||||
<property name="label" translatable="yes">Remove episodes deleted on device from gPodder</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="checkbutton_skip_played_episodes">
|
||||
<property name="label" translatable="yes">Only sync unplayed episodes</property>
|
||||
<property name="visible">True</property>
|
||||
|
@ -672,7 +731,7 @@
|
|||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
|
|
@ -173,6 +173,13 @@ defaults = {
|
|||
'delete_episodes': False,
|
||||
'sync_disks': False,
|
||||
},
|
||||
'playlists': {
|
||||
'create': True,
|
||||
'two_way_sync': False,
|
||||
'use_absolute_path': True,
|
||||
'folder': 'Playlists',
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
'youtube': {
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# gPodder - A media aggregator and podcast client
|
||||
# Copyright (c) 2005-2011 Thomas Perl and the gPodder Team
|
||||
#
|
||||
# gPodder is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gPodder is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
import os
|
||||
import gpodder
|
||||
|
||||
_ = gpodder.gettext
|
||||
|
||||
from gpodder import util
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class gPodderDevicePlaylist(object):
|
||||
def __init__(self, config, playlist_name):
|
||||
self._config=config
|
||||
self.linebreak = '\r\n'
|
||||
self.playlist_file=playlist_name + '.m3u'
|
||||
self.playlist_folder = os.path.join(self._config.device_sync.device_folder, self._config.device_sync.playlists.folder)
|
||||
self.mountpoint = util.find_mount_point(self.playlist_folder)
|
||||
if self.mountpoint == '/':
|
||||
self.mountpoint = self.playlist_folder
|
||||
logger.warning('MP3 player resides on / - using %s as MP3 player root', self.mountpoint)
|
||||
self.playlist_absolute_filename=os.path.join(self.playlist_folder, self.playlist_file)
|
||||
|
||||
def build_extinf(self, filename):
|
||||
#TO DO: Windows playlists
|
||||
# if self._config.mp3_player_playlist_win_path:
|
||||
# filename = filename.replace('\\', os.sep)
|
||||
|
||||
# # rebuild the whole filename including the mountpoint
|
||||
# if self._config.device_sync.playlist_absolute_path:
|
||||
# absfile = os.path.join(self.mountpoint,filename)
|
||||
# else: #TODO: Test rel filenames
|
||||
# absfile = util.rel2abs(filename, os.path.dirname(self.playlist_file))
|
||||
|
||||
# fallback: use the basename of the file
|
||||
(title, extension) = os.path.splitext(os.path.basename(filename))
|
||||
|
||||
return "#EXTINF:0,%s%s" % (title.strip(), self.linebreak)
|
||||
|
||||
def read_m3u(self):
|
||||
"""
|
||||
read all files from the existing playlist
|
||||
"""
|
||||
tracks = []
|
||||
logger.info("Read data from the playlistfile %s" % self.playlist_absolute_filename)
|
||||
if os.path.exists(self.playlist_absolute_filename):
|
||||
for line in open(self.playlist_absolute_filename, 'r'):
|
||||
if not line.startswith('#EXT'):
|
||||
tracks.append(line.rstrip('\r\n'))
|
||||
return tracks
|
||||
|
||||
def get_filename_for_playlist(self, episode):
|
||||
"""
|
||||
get the filename for the given episode for the playlist
|
||||
"""
|
||||
filename_base = util.sanitize_filename(episode.sync_filename(
|
||||
self._config.device_sync.custom_sync_name_enabled,
|
||||
self._config.device_sync.custom_sync_name),
|
||||
self._config.device_sync.max_filename_length)
|
||||
filename = filename_base + os.path.splitext(episode.local_filename(create=False))[1].lower()
|
||||
return filename
|
||||
|
||||
def get_absolute_filename_for_playlist(self, episode):
|
||||
"""
|
||||
get the filename including full path for the given episode for the playlist
|
||||
"""
|
||||
filename = self.get_filename_for_playlist(episode)
|
||||
if self._config.device_sync.one_folder_per_podcast:
|
||||
filename = os.path.join(episode.channel.title, filename)
|
||||
if self._config.device_sync.playlist.absolute_path:
|
||||
filename = os.path.join(util.relpath(self.mountpoint, self._config.device_sync.device_folder), filename)
|
||||
return filename
|
||||
|
||||
def write_m3u(self, episodes):
|
||||
"""
|
||||
write the list into the playlist on the device
|
||||
"""
|
||||
logger.info('Writing playlist file: %s', self.playlist_file)
|
||||
if not util.make_directory(self.playlist_folder):
|
||||
raise IOError(_('Folder %s could not be created.') % self.playlist_folder, _('Error writing playlist'))
|
||||
else:
|
||||
fp = open(os.path.join(self.playlist_folder, self.playlist_file), 'w')
|
||||
fp.write('#EXTM3U%s' % self.linebreak)
|
||||
for current_episode in episodes:
|
||||
filename_base = util.sanitize_filename(current_episode.sync_filename(
|
||||
self._config.device_sync.custom_sync_name_enabled,
|
||||
self._config.device_sync.custom_sync_name),
|
||||
self._config.device_sync.max_filename_length)
|
||||
filename = filename_base + os.path.splitext(current_episode.local_filename(create=False))[1].lower()
|
||||
filename = self.get_filename_for_playlist(current_episode)
|
||||
fp.write(self.build_extinf(filename))
|
||||
filename = self.get_absolute_filename_for_playlist(current_episode)
|
||||
fp.write(filename)
|
||||
fp.write(self.linebreak)
|
||||
fp.close()
|
||||
|
|
@ -229,14 +229,17 @@ class gPodderPreferences(BuilderWidget):
|
|||
else:
|
||||
self.hscale_expiration.set_value(0)
|
||||
|
||||
self._config.connect_gtk_togglebutton('auto_remove_unplayed_episodes', self.checkbutton_expiration_unplayed)
|
||||
self._config.connect_gtk_togglebutton('auto_remove_unfinished_episodes', self.checkbutton_expiration_unfinished)
|
||||
self._config.connect_gtk_togglebutton('auto_remove_unplayed_episodes',
|
||||
self.checkbutton_expiration_unplayed)
|
||||
self._config.connect_gtk_togglebutton('auto_remove_unfinished_episodes',
|
||||
self.checkbutton_expiration_unfinished)
|
||||
|
||||
self.device_type_model = DeviceTypeActionList(self._config)
|
||||
self.combobox_device_type.set_model(self.device_type_model)
|
||||
cellrenderer = gtk.CellRendererText()
|
||||
self.combobox_device_type.pack_start(cellrenderer, True)
|
||||
self.combobox_device_type.add_attribute(cellrenderer, 'text', DeviceTypeActionList.C_CAPTION)
|
||||
self.combobox_device_type.add_attribute(cellrenderer, 'text',
|
||||
DeviceTypeActionList.C_CAPTION)
|
||||
self.combobox_device_type.set_active(self.device_type_model.get_index())
|
||||
|
||||
self.on_sync_model = OnSyncActionList(self._config)
|
||||
|
@ -246,7 +249,12 @@ class gPodderPreferences(BuilderWidget):
|
|||
self.combobox_on_sync.add_attribute(cellrenderer, 'text', OnSyncActionList.C_CAPTION)
|
||||
self.combobox_on_sync.set_active(self.on_sync_model.get_index())
|
||||
|
||||
self._config.connect_gtk_togglebutton('device_sync.skip_played_episodes', self.checkbutton_skip_played_episodes)
|
||||
self._config.connect_gtk_togglebutton('device_sync.skip_played_episodes',
|
||||
self.checkbutton_skip_played_episodes)
|
||||
self._config.connect_gtk_togglebutton('device_sync.playlists.create',
|
||||
self.checkbutton_create_playlists)
|
||||
self._config.connect_gtk_togglebutton('device_sync.playlists.two_way_sync',
|
||||
self.checkbutton_delete_using_playlists)
|
||||
|
||||
# Have to do this before calling set_active on checkbutton_enable
|
||||
self._enable_mygpo = self._config.mygpo.enabled
|
||||
|
@ -501,6 +509,31 @@ class gPodderPreferences(BuilderWidget):
|
|||
index = self.combobox_on_sync.get_active()
|
||||
self.on_sync_model.set_index(index)
|
||||
|
||||
def on_checkbutton_create_playlists_toggled(self, widget,device_type_changed=False):
|
||||
if not widget.get_active():
|
||||
self._config.device_sync.playlists.create=False
|
||||
self.toggle_playlist_interface(False)
|
||||
#need to read value of checkbutton from interface,
|
||||
#rather than value of parameter
|
||||
else:
|
||||
self._config.device_sync.playlists.create=True
|
||||
self.toggle_playlist_interface(True)
|
||||
|
||||
def toggle_playlist_interface(self,enabled):
|
||||
if (enabled==True and not(self._config.device_sync.device_type == 'none')):
|
||||
self.btn_playlistfolder.set_sensitive(True)
|
||||
self.btn_playlistfolder.set_label(self._config.device_sync.playlists.folder)
|
||||
self.checkbutton_delete_using_playlists.set_sensitive(True)
|
||||
children = self.btn_playlistfolder.get_children()
|
||||
if children:
|
||||
label = children.pop()
|
||||
label.set_alignment(0., .5)
|
||||
else:
|
||||
self.btn_playlistfolder.set_sensitive(False)
|
||||
self.btn_playlistfolder.set_label('')
|
||||
self.checkbutton_delete_using_playlists.set_sensitive(False)
|
||||
|
||||
|
||||
def on_combobox_device_type_changed(self, widget):
|
||||
index = self.combobox_device_type.get_active()
|
||||
self.device_type_model.set_index(index)
|
||||
|
@ -508,17 +541,20 @@ class gPodderPreferences(BuilderWidget):
|
|||
if device_type == 'none':
|
||||
self.btn_filesystemMountpoint.set_label('')
|
||||
self.btn_filesystemMountpoint.set_sensitive(False)
|
||||
self.checkbutton_create_playlists.set_sensitive(False)
|
||||
self.toggle_playlist_interface(False)
|
||||
elif device_type == 'filesystem':
|
||||
self.btn_filesystemMountpoint.set_label(self._config.device_sync.device_folder)
|
||||
self.btn_filesystemMountpoint.set_sensitive(True)
|
||||
self.checkbutton_create_playlists.set_sensitive(True)
|
||||
children = self.btn_filesystemMountpoint.get_children()
|
||||
if children:
|
||||
label = children.pop()
|
||||
label.set_alignment(0., .5)
|
||||
self.toggle_playlist_interface(self._config.device_sync.playlists.create)
|
||||
else:
|
||||
# TODO: Add support for iPod and MTP devices
|
||||
pass
|
||||
|
||||
children = self.btn_filesystemMountpoint.get_children()
|
||||
if children:
|
||||
label = children.pop()
|
||||
label.set_alignment(0., .5)
|
||||
pass
|
||||
|
||||
def on_btn_device_mountpoint_clicked(self, widget):
|
||||
fs = gtk.FileChooserDialog(title=_('Select folder for mount point'),
|
||||
|
@ -530,9 +566,26 @@ class gPodderPreferences(BuilderWidget):
|
|||
filename = fs.get_filename()
|
||||
if self._config.device_sync.device_type == 'filesystem':
|
||||
self._config.device_sync.device_folder = filename
|
||||
|
||||
# Request an update of the mountpoint button
|
||||
self.on_combobox_device_type_changed(None)
|
||||
|
||||
fs.destroy()
|
||||
|
||||
def on_btn_playlist_folder_clicked(self, widget):
|
||||
fs = gtk.FileChooserDialog(title=_('Select folder for playlists'),
|
||||
action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
|
||||
fs.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||
fs.add_button(gtk.STOCK_OPEN, gtk.RESPONSE_OK)
|
||||
fs.set_current_folder(self.btn_playlistfolder.get_label())
|
||||
if fs.run() == gtk.RESPONSE_OK:
|
||||
filename = util.relpath(self._config.device_sync.device_folder,
|
||||
fs.get_filename())
|
||||
if self._config.device_sync.device_type == 'filesystem':
|
||||
self._config.device_sync.playlists.folder = filename
|
||||
self.btn_playlistfolder.set_label(filename)
|
||||
children = self.btn_playlistfolder.get_children()
|
||||
if children:
|
||||
label = children.pop()
|
||||
label.set_alignment(0., .5)
|
||||
|
||||
fs.destroy()
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
# Thomas Perl <thp@gpodder.org>; 2009-09-05 (based on code from gui.py)
|
||||
# Ported to gPodder 3 by Joseph Wickremasinghe in June 2012
|
||||
|
||||
import gtk
|
||||
import os
|
||||
import gpodder
|
||||
|
||||
_ = gpodder.gettext
|
||||
|
@ -29,6 +29,8 @@ _ = gpodder.gettext
|
|||
from gpodder import util
|
||||
from gpodder import sync
|
||||
|
||||
from gpodder.gtkui.desktop.episodeselector import gPodderEpisodeSelector
|
||||
from gpodder.gtkui.desktop.deviceplaylist import gPodderDevicePlaylist
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -38,11 +40,12 @@ class gPodderSyncUI(object):
|
|||
update_episode_list_icons,
|
||||
update_podcast_list_model,
|
||||
preferences_widget,
|
||||
episode_selector_class,
|
||||
channels,
|
||||
download_status_model,
|
||||
download_queue_manager,
|
||||
enable_download_list_update,
|
||||
commit_changes_to_database):
|
||||
commit_changes_to_database,
|
||||
delete_episode_list):
|
||||
self.device = None
|
||||
|
||||
self._config = config
|
||||
|
@ -53,12 +56,12 @@ class gPodderSyncUI(object):
|
|||
self.update_episode_list_icons = update_episode_list_icons
|
||||
self.update_podcast_list_model = update_podcast_list_model
|
||||
self.preferences_widget = preferences_widget
|
||||
self.episode_selector_class = episode_selector_class
|
||||
self.channels=channels
|
||||
self.download_status_model = download_status_model
|
||||
self.download_queue_manager = download_queue_manager
|
||||
self.enable_download_list_update = enable_download_list_update
|
||||
self.commit_changes_to_database = commit_changes_to_database
|
||||
|
||||
self.delete_episode_list=delete_episode_list
|
||||
|
||||
def _filter_sync_episodes(self, channels, only_downloaded=False):
|
||||
"""Return a list of episodes for device synchronization
|
||||
|
@ -141,11 +144,143 @@ class gPodderSyncUI(object):
|
|||
device.close()
|
||||
return
|
||||
|
||||
# Finally start the synchronization process
|
||||
@util.run_in_background
|
||||
def sync_thread_func():
|
||||
#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 current_channel.sync_to_mp3_player:
|
||||
|
||||
#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)
|
||||
playlist.write_m3u(episodes_for_playlist)
|
||||
|
||||
#enable updating of UI
|
||||
self.enable_download_list_update()
|
||||
device.add_sync_tasks(episodes, force_played=force_played)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# This function is used to remove files from the device
|
||||
def cleanup_episodes():
|
||||
|
@ -155,7 +290,6 @@ class gPodderSyncUI(object):
|
|||
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:
|
||||
|
|
|
@ -2536,7 +2536,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
if macapp is None:
|
||||
sys.exit(0)
|
||||
|
||||
def delete_episode_list(self, episodes, confirm=True, skip_locked=True):
|
||||
def delete_episode_list(self, episodes, confirm=True, skip_locked=True,callback=None):
|
||||
if not episodes:
|
||||
return False
|
||||
|
||||
|
@ -2589,7 +2589,10 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
self.mygpo_client.on_delete(episodes_status_update)
|
||||
self.mygpo_client.flush()
|
||||
|
||||
util.idle_add(finish_deletion, episode_urls, channel_urls)
|
||||
if callback==None:
|
||||
util.idle_add(finish_deletion, episode_urls, channel_urls)
|
||||
else:
|
||||
util.idle_add(callback, episode_urls, channel_urls,progress)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -3437,11 +3440,12 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
self.update_episode_list_icons,
|
||||
self.update_podcast_list_model,
|
||||
self.toolPreferences,
|
||||
gPodderEpisodeSelector,
|
||||
self.channels,
|
||||
self.download_status_model,
|
||||
self.download_queue_manager,
|
||||
self.enable_download_list_update,
|
||||
self.commit_changes_to_database)
|
||||
self.commit_changes_to_database,
|
||||
self.delete_episode_list)
|
||||
|
||||
self.sync_ui.on_synchronize_episodes(self.channels, episodes, force_played)
|
||||
|
||||
|
|
Loading…
Reference in New Issue