fix #243 add sync command to gpo

This commit is contained in:
Eric Le Lay 2018-10-14 16:10:45 +02:00
parent d34c814909
commit 97db9c97d2
4 changed files with 180 additions and 10 deletions

169
bin/gpo
View File

@ -52,6 +52,10 @@
pending [URL] List new episodes (all or only from URL)
episodes [--guid] [URL] List episodes with or without GUIDs (all or only from URL)
- Episode management -
sync Sync podcasts to device
- Configuration -
set [key] [value] List one (all) keys or set to a new value
@ -75,6 +79,7 @@ import pydoc
import re
import shlex
import sys
import threading
try:
import readline
@ -112,8 +117,9 @@ if os.path.exists(os.path.join(src_dir, 'gpodder', '__init__.py')):
sys.path.insert(0, src_dir)
import gpodder # isort:skip
from gpodder import common, core, download, log, model, my, opml, util, youtube # isort:skip
from gpodder import common, core, download, log, model, my, opml, sync, util, youtube # isort:skip
from gpodder.config import config_value_to_string # isort:skip
from gpodder.syncui import gPodderSyncUI # isort:skip
_ = gpodder.gettext
N_ = gpodder.ngettext
@ -742,6 +748,167 @@ class gPodderCli(object):
print(stylize(__doc__), file=sys.stderr, end='')
return True
def sync(self):
def ep_repr(episode):
return '{} / {}'.format(episode.channel.title, episode.title)
def msg_title(title, message):
if title:
msg = '{}: {}'.format(title, message)
else:
msg = '{}'.format(message)
return msg
def _notification(message, title=None, important=False, widget=None):
print(msg_title(message, title))
def _show_confirmation(message, title=None):
msg = msg_title(message, title)
msg = _("%(title)s: %(msg)s ([yes]/no): ") % dict(title=title, msg=message)
if not interactive_console:
return True
line = input(msg)
return not line or (line.lower() == _('yes'))
def _delete_episode_list(episodes, confirm=True, skip_locked=True, callback=None):
if not episodes:
return False
if skip_locked:
episodes = [e for e in episodes if not e.archive]
if not episodes:
title = _('Episodes are locked')
message = _(
'The selected episodes are locked. Please unlock the '
'episodes that you want to delete before trying '
'to delete them.')
_notification(message, title)
return False
count = len(episodes)
title = N_('Delete %(count)d episode?', 'Delete %(count)d episodes?',
count) % {'count': count}
message = _('Deleting episodes removes downloaded files.')
if confirm and not _show_confirmation(message, title):
return False
print(_('Please wait while episodes are deleted'))
def finish_deletion(episode_urls, channel_urls):
# Episodes have been deleted - persist the database
self.db.commit()
episode_urls = set()
channel_urls = set()
episodes_status_update = []
for idx, episode in enumerate(episodes):
if not episode.archive or not skip_locked:
self._start_action(_('Deleting episode: %(episode)s') % {
'episode': episode.title})
episode.delete_from_disk()
self._finish_action(success=True)
episode_urls.add(episode.url)
channel_urls.add(episode.channel.url)
episodes_status_update.append(episode)
# Notify the web service about the status update + upload
if self.mygpo_client.can_access_webservice():
self.mygpo_client.on_delete(episodes_status_update)
self.mygpo_client.flush()
if callback is None:
util.idle_add(finish_deletion, episode_urls, channel_urls)
else:
util.idle_add(callback, episode_urls, channel_urls, None)
return True
def _episode_selector(parent_window, title=None, instructions=None, episodes=None,
selected=None, columns=None, callback=None, _config=None):
if not interactive_console:
return callback([e for i, e in enumerate(episodes) if selected[i]])
def show_list():
self._pager('\n'.join(
'[%s] %3d: %s' % (('X' if selected[index] else ' '), index + 1, ep_repr(e))
for index, e in enumerate(episodes)))
print("{}. {}".format(title, instructions))
show_list()
msg = _('Enter episode index to toggle, ? for list, X to select all, space to select none, empty when ready')
while True:
index = input(msg + ': ')
if not index:
return callback([e for i, e in enumerate(episodes) if selected[i]])
if index == '?':
show_list()
continue
elif index == 'X':
selected = [True, ] * len(episodes)
show_list()
continue
elif index == ' ':
selected = [False, ] * len(episodes)
show_list()
continue
else:
try:
index = int(index)
except ValueError:
self._error(_('Invalid value.'))
continue
if not (1 <= index <= len(episodes)):
self._error(_('Invalid value.'))
continue
e = episodes[index - 1]
selected[index - 1] = not selected[index - 1]
if selected[index - 1]:
self._info(_('Will delete %(episode)s') % dict(episode=ep_repr(e)))
else:
self._info(_("Won't delete %(episode)s") % dict(episode=ep_repr(e)))
def _not_applicable(*args, **kwargs):
pass
class DownloadStatusModel(object):
def register_task(self, ask):
pass
class DownloadQueueManager(object):
def queue_task(x, task):
def progress_updated(progress):
self._update_action(progress)
with self._action(_('Syncing %s'), ep_repr(task.episode)):
task.status = sync.SyncTask.DOWNLOADING
task.add_progress_callback(progress_updated)
task.run()
done_lock = threading.Lock()
self.mygpo_client = my.MygPoClient(self._config)
sync_ui = gPodderSyncUI(self._config,
_notification,
None,
_show_confirmation,
_not_applicable,
self._model.get_podcasts(),
DownloadStatusModel(),
DownloadQueueManager(),
_not_applicable,
self._db.commit,
_delete_episode_list,
_episode_selector)
done_lock.acquire()
sync_ui.on_synchronize_episodes(self._model.get_podcasts(), episodes=None, force_played=True, done_callback=done_lock.release)
done_lock.acquire() # block until done
# -------------------------------------------------------------------
def _pager(self, output):

View File

@ -51,7 +51,6 @@ from gpodder.gtkui.desktop.episodeselector import gPodderEpisodeSelector
from gpodder.gtkui.desktop.exportlocal import gPodderExportToLocalFolder
from gpodder.gtkui.desktop.podcastdirectory import gPodderPodcastDirectory
from gpodder.gtkui.desktop.preferences import gPodderPreferences
from gpodder.gtkui.desktop.sync import gPodderSyncUI
from gpodder.gtkui.desktop.welcome import gPodderWelcome
from gpodder.gtkui.desktopfile import UserAppsReader
from gpodder.gtkui.download import DownloadStatusModel
@ -64,6 +63,7 @@ from gpodder.gtkui.model import EpisodeListModel, Model, PodcastListModel
from gpodder.gtkui.services import CoverDownloader
from gpodder.gtkui.widgets import SimpleMessageArea
from gpodder.model import PodcastEpisode, check_root_folder_path
from gpodder.syncui import gPodderSyncUI
import gi # isort:skip
gi.require_version('Gtk', '3.0') # isort:skip
@ -3612,9 +3612,11 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.download_queue_manager,
self.enable_download_list_update,
self.commit_changes_to_database,
self.delete_episode_list)
self.delete_episode_list,
gPodderEpisodeSelector)
self.sync_ui.on_synchronize_episodes(self.channels, episodes, force_played)
self.sync_ui.on_synchronize_episodes(self.channels, episodes, force_played,
self.enable_download_list_update)
def on_update_youtube_subscriptions_activate(self, action, param):
if not self.config.youtube.api_key_v3:

View File

@ -26,8 +26,7 @@ import os
import gpodder
from gpodder import sync, util
from gpodder.gtkui.desktop.deviceplaylist import gPodderDevicePlaylist
from gpodder.gtkui.desktop.episodeselector import gPodderEpisodeSelector
from gpodder.deviceplaylist import gPodderDevicePlaylist
_ = gpodder.gettext
@ -44,7 +43,8 @@ class gPodderSyncUI(object):
download_queue_manager,
enable_download_list_update,
commit_changes_to_database,
delete_episode_list):
delete_episode_list,
select_episodes_to_delete):
self.device = None
self._config = config
@ -59,6 +59,7 @@ class gPodderSyncUI(object):
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
self.select_episodes_to_delete = select_episodes_to_delete
def _filter_sync_episodes(self, channels, only_downloaded=False):
"""Return a list of episodes for device synchronization
@ -90,7 +91,7 @@ class gPodderSyncUI(object):
message = _('Please check the settings in the preferences dialog.')
self.notification(message, title, important=True)
def on_synchronize_episodes(self, channels, episodes=None, force_played=True):
def on_synchronize_episodes(self, channels, episodes=None, force_played=True, done_callback=None):
device = sync.open_device(self)
if device is None:
@ -205,7 +206,7 @@ class gPodderSyncUI(object):
@util.run_in_background
def sync_thread_func():
device.add_sync_tasks(episodes, force_played=force_played,
done_callback=self.enable_download_list_update)
done_callback=done_callback)
return
@ -269,7 +270,7 @@ class gPodderSyncUI(object):
('markup_delete_episodes', None, None, _('Episode')),
)
gPodderEpisodeSelector(
self.select_episodes_to_delete(
self.parent_window,
title=_('Episodes have been deleted on device'),
instructions='Select the episodes you want to delete:',