Skip to content

Commit

Permalink
fix #243 add sync command to gpo
Browse files Browse the repository at this point in the history
  • Loading branch information
elelay committed Oct 14, 2018
1 parent d34c814 commit 97db9c9
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 10 deletions.
169 changes: 168 additions & 1 deletion bin/gpo
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -75,6 +79,7 @@ import pydoc
import re
import shlex
import sys
import threading

try:
import readline
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
File renamed without changes.
8 changes: 5 additions & 3 deletions src/gpodder/gtkui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
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
Expand All @@ -64,6 +63,7 @@
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
Expand Down Expand Up @@ -3612,9 +3612,11 @@ def on_sync_to_device_activate(self, widget, episodes=None, force_played=True):
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:
Expand Down
13 changes: 7 additions & 6 deletions src/gpodder/gtkui/desktop/sync.py → src/gpodder/syncui.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@

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

Expand All @@ -44,7 +43,8 @@ def __init__(self, config, notification, parent_window,
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
Expand All @@ -59,6 +59,7 @@ def __init__(self, config, notification, parent_window,
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
Expand Down Expand Up @@ -90,7 +91,7 @@ def _show_message_cannot_open(self):
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:
Expand Down Expand Up @@ -205,7 +206,7 @@ def resume_sync(episode_urls, channel_urls, progress):
@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

Expand Down Expand Up @@ -269,7 +270,7 @@ def auto_delete_callback(episodes):
('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:',
Expand Down

0 comments on commit 97db9c9

Please sign in to comment.