Initialize woodchuck using hooks, improve "gpo"

Instead of initializing woodchuck explicitly, let the
hooks module know then the UI has been initialized,
and provide some callbacks (that Woodchuck needs, but
which could be used by other hook scripts) and the model.
This commit is contained in:
Thomas Perl 2011-10-19 12:37:55 +02:00
parent 25f1b0a267
commit 956ac7d9b1
7 changed files with 116 additions and 81 deletions

46
bin/gpo
View File

@ -87,8 +87,6 @@ if os.path.exists(src_dir) and os.path.exists(data_dir) and \
import gpodder
_ = gpodder.gettext
from gpodder.plugins import woodchuck
# Platform detection (i.e. Maemo 5, etc..)
gpodder.detect_platform()
@ -132,10 +130,17 @@ class gPodderCli(object):
def __init__(self):
self.client = api.PodcastClient()
self._current_action = ''
gpodder.user_hooks.on_ui_initialized(self.client.core.model,
self.hooks_podcast_update_cb,
self.hooks_episode_download_cb)
# Initialize Woodchuck so that we can register new podcasts,
# etc., but don't request any callbacks.
woodchuck.init(self.client.core.model)
def hooks_podcast_update_cb(self, podcast):
self._info(_('Podcast update requested by hooks.'))
self._update_podcast(podcast)
def hooks_episode_download_cb(self, episode):
self._info(_('Episode download requested by hooks.'))
self._download_episode(episode)
def _start_action(self, msg, *args):
line = msg % args
@ -265,24 +270,22 @@ class gPodderCli(object):
return True
def _update_podcast(self, podcast):
self._start_action('Updating %s', podcast.title)
try:
podcast.update()
self._finish_action()
except Exception, e:
self._finish_action(False)
@FirstArgumentIsPodcastURL
def update(self, url=None):
for podcast in self.client.get_podcasts():
if url is None and podcast.update_enabled():
self._start_action('Updating %s', podcast.title)
try:
podcast.update()
self._finish_action()
except Exception, e:
self._finish_action(False)
self._update_podcast(podcast)
elif podcast.url == url:
# Don't need to check for update_enabled()
self._start_action('Updating %s', podcast.title)
try:
podcast.update()
self._finish_action()
except Exception, e:
self._finish_action(False)
self._update_podcast(podcast)
return True
@ -317,6 +320,11 @@ class gPodderCli(object):
self.client.commit()
return True
def _download_episode(self, episode):
self._start_action('Downloading %s', episode.title)
episode.download(self._update_action)
self._finish_action()
@FirstArgumentIsPodcastURL
def download(self, url=None):
count = 0
@ -328,9 +336,7 @@ class gPodderCli(object):
if not podcast_printed:
print inblue(podcast.title)
podcast_printed = True
self._start_action('Downloading %s', episode.title)
episode.download(self._update_action)
self._finish_action()
self._download_episode(episode)
count += 1
print count, 'episodes downloaded.'

View File

@ -38,12 +38,12 @@ class Core(object):
# Initialize the gPodder home directory
util.make_directory(gpodder.home)
# Load installed/configured plugins
gpodder.load_plugins()
# Load hook modules and install the hook manager
gpodder.user_hooks = hooks.HookManager()
# Load installed/configured plugins
gpodder.load_plugins()
# Open the database and configuration file
self.db = database_class(gpodder.database_file)
self.model = model_class(self.db)

View File

@ -51,7 +51,6 @@ from gpodder import download
from gpodder import my
from gpodder import youtube
from gpodder import player
from gpodder.plugins import woodchuck
import logging
logger = logging.getLogger(__name__)
@ -209,11 +208,9 @@ class gPodder(BuilderWidget, dbus.service.Object):
self.active_channel = None
self.channels = self.model.get_podcasts()
# Initialize woodchuck after a short timeout period
gobject.timeout_add(1000, woodchuck.init,
self.channels,
self.woodchuck_channel_update_cb,
self.woodchuck_episode_download_cb)
gpodder.user_hooks.on_ui_initialized(self.model,
self.hooks_podcast_update_cb,
self.hooks_episode_download_cb)
# Check if the user has downloaded any podcast with an external program
# and mark episodes as downloaded / move them away (bug 902)
@ -3362,13 +3359,13 @@ class gPodder(BuilderWidget, dbus.service.Object):
return False
def woodchuck_channel_update_cb(self, channel):
logger.debug('woodchuck_channel_update_cb(%s)', channel)
self.update_feed_cache(channels=[channel],
show_new_episodes_dialog=False)
def hooks_podcast_update_cb(self, podcast):
logger.debug('hooks_podcast_update_cb(%s)', podcast)
self.update_feed_cache(channels=[podcast],
show_new_episodes_dialog=False)
def woodchuck_episode_download_cb(self, episode):
logger.debug('woodchuck_episode_download_cb(%s)', episode)
def hooks_episode_download_cb(self, episode):
logger.debug('hooks_episode_download_cb(%s)', episode)
self.download_episode_list(episodes=[episode])
def main(options=None):

View File

@ -93,6 +93,15 @@ class HookManager(object):
"""
self.modules.append((None, obj))
def unregister_hooks(self, obj):
"""
Unregister a previously registered object.
"""
if (None, obj) in self.modules:
self.modules.remove((None, obj))
else:
logger.warn('Unregistered hook which was not registered.')
def _load_module(self, filepath):
"""Load a Python module by filename
@ -114,6 +123,17 @@ class HookManager(object):
# the same function defined in them. If the handler functions here contain
# any code, it will be called after all the hooks have been called.
@call_hooks
def on_ui_initialized(self, model, update_podcast_callback,
download_episode_callback):
"""Called when the user interface is initialized.
@param model: A gpodder.model.Model instance
@param update_podcast_callback: Function to update a podcast feed
@param download_episode_callback: Function to download an episode
"""
pass
@call_hooks
def on_podcast_subscribe(self, podcast):
"""Called when the user subscribes to a new podcast feed.

View File

@ -878,6 +878,8 @@ class PodcastChannel(PodcastModelObject):
tmp.save()
gpodder.user_hooks.on_podcast_subscribe(tmp)
return tmp
def episode_factory(self, d):
@ -1104,7 +1106,6 @@ class PodcastChannel(PodcastModelObject):
self.db.commit()
def delete(self):
gpodder.user_hooks.on_podcast_delete(self)
self.db.delete_podcast(self)
self.model._remove_podcast(self)
@ -1112,9 +1113,6 @@ class PodcastChannel(PodcastModelObject):
if self.download_folder is None:
self.get_save_dir()
if self.id is None:
gpodder.user_hooks.on_podcast_subscribe(self)
gpodder.user_hooks.on_podcast_save(self)
self._clear_changes()
@ -1268,10 +1266,10 @@ class Model(object):
def _append_podcast(self, podcast):
if podcast not in self.children:
self.children.append(podcast)
# XXX: Sort?
def _remove_podcast(self, podcast):
self.children.remove(podcast)
gpodder.user_hooks.on_podcast_delete(self)
def get_podcasts(self):
def podcast_factory(dct, db):

View File

@ -1,3 +1,7 @@
# -*- coding: utf-8 -*-
#
# gPodder - A media aggregator and podcast client
# Copyright (c) 2005-2011 Thomas Perl and the gPodder Team
# Copyright (c) 2011 Neal H. Walfield
#
# gPodder is free software; you can redistribute it and/or modify
@ -454,39 +458,53 @@ def check_subscriptions():
w.stream_unregister(id)
yield
def init(model=None, podcast_update=None, episode_download=None):
"""
Connect to the woodchuck server and initialize any state.
class WoodchuckLoader():
def on_ui_initialized(self, model, update_podcast_callback,
download_episode_callback):
"""
Connect to the woodchuck server and initialize any state.
model is an instance of the podcast model.
model is an instance of the podcast model.
podcast_update is a function that is passed a single argument: the
PodcastPodcast that should be updated.
podcast_update is a function that is passed a single argument: the
PodcastPodcast that should be updated.
episode_download is a function that is passed a single argument:
the PodcastEpisode that should be downloaded.
episode_download is a function that is passed a single argument:
the PodcastEpisode that should be downloaded.
If podcast_update and episode_download are None, then Woodchuck
upcalls will be disabled. In this case, you don't need to specify
the list of podcasts. Just specify None.
"""
if not woodchuck_imported:
return
If podcast_update and episode_download are None, then Woodchuck
upcalls will be disabled. In this case, you don't need to specify
the list of podcasts. Just specify None.
"""
logger.info('Got on_ui_initialized. Setting up woodchuck..')
global _main_thread
_main_thread = threading.currentThread()
global woodchuck_loader
gpodder.user_hooks.unregister_hooks(woodchuck_loader)
woodchuck_loader = None
global w
w = mywoodchuck(model, podcast_update, episode_download)
if not woodchuck_imported:
return
if not w.available():
logger.info(
"Woodchuck support disabled: unable to contact Woodchuck server.")
print "Woodchuck support disabled: unable to contact Woodchuck server."
return
global _main_thread
_main_thread = threading.currentThread()
logger.info("Woodchuck appears to be available.")
global woodchuck_instance
woodchuck_instance = mywoodchuck(model,
update_podcast_callback,
download_episode_callback)
gpodder.user_hooks.register_hooks(w)
if not woodchuck_instance.available():
logger.warn('Unable to contact Woodchuck server. Disabling.')
return
logger.info('Connected to Woodchuck server.')
gpodder.user_hooks.register_hooks(woodchuck_instance)
idle_add(check_subscriptions)
woodchuck_loader = WoodchuckLoader()
woodchuck_instance = None
gpodder.user_hooks.register_hooks(woodchuck_loader)
idle_add(check_subscriptions)

View File

@ -44,8 +44,6 @@ from gpodder.qmlui import model
from gpodder.qmlui import helper
from gpodder.qmlui import images
from gpodder.plugins import woodchuck
import logging
logger = logging.getLogger("qmlui")
@ -418,6 +416,10 @@ class qtPodder(QObject):
self.db = self.core.db
self.model = self.core.model
gpodder.user_hooks.on_ui_initialized(self.model,
self.hooks_podcast_update_cb,
self.hooks_episode_download_cb)
self.view = DeclarativeView()
self.view.closing.connect(self.on_quit)
self.view.setResizeMode(QDeclarativeView.SizeRootObjectToView)
@ -531,9 +533,6 @@ class qtPodder(QObject):
podcasts = map(model.QPodcast, self.model.get_podcasts())
self.podcast_model.set_podcasts(self.db, podcasts)
woodchuck.init(self.model, self.woodchuck_podcast_update_cb,
self.woodchuck_episode_download_cb)
def wrap_episode(self, podcast, episode):
try:
return self.active_episode_wrappers[episode.id]
@ -566,27 +565,24 @@ class qtPodder(QObject):
return podcasts[0]
return None
def woodchuck_podcast_update_cb(self, podcast):
logger.debug("woodchuck_podcast_update_cb(%s)", str(podcast))
def hooks_podcast_update_cb(self, podcast):
logger.debug('hooks_podcast_update_cb(%s)', podcast)
try:
qpodcast = self.podcast_to_qpodcast(podcast)
if qpodcast is not None:
qpodcast.qupdate(
finished_callback=self.controller.update_subset_stats)
except Exception, e:
logger.exception("woodchuck_podcast_update_cb(%s): %s",
str(podcast), str(e))
logger.exception('hooks_podcast_update_cb(%s): %s', podcast, e)
def woodchuck_episode_download_cb(self, episode):
logger.debug("woodchuck_episode_download_cb(%s: %s)",
str(episode), type(episode))
def hooks_episode_download_cb(self, episode):
logger.debug('hooks_episode_download_cb(%s)', episode)
try:
qpodcast = self.podcast_to_qpodcast(episode.channel)
qepisode = self.wrap_episode(qpodcast, episode)
qepisode.qdownload(self.config, self.controller.update_subset_stats)
self.controller.downloadEpisode(qepisode)
except Exception, e:
logger.exception("woodchuck_episode_download_cb(%s): %s",
str(episode), str(e))
logger.exception('hooks_episode_download_cb(%s): %s', episode, e)
def main(args):
gui = qtPodder(args, core.Core())