dbus)
cd /opt/FeedingIt
#cp feedingit_status.desktop /usr/share/applications/hildon-status-menu/
- nice python2.5 update_feeds.py
+ nice python2.5 update_feeds.py --daemon
;;
noqml)
start_gtk
if markAllAsRead=="True":
feed.markAllAsRead()
listing.updateUnread(key)
- updateDbusHandler.ArticleCountUpdated()
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
if onlyUnread == "False":
onlyUnread = False
feed = listing.getFeed(key)
feed.setEntryRead(article)
listing.updateUnread(key)
- updateDbusHandler.ArticleCountUpdated()
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
elif request[1]=="updateAll":
#app.automaticUpdate()
self.updateAll()
- updateDbusHandler.ArticleCountUpdated()
xml = "<xml>OK</xml>"
elif request[1] == "addCat":
catName = request[2]
# Initialize the glib mainloop, for dbus purposes
from feedingitdbus import ServerObject
-from updatedbus import UpdateServerObject, get_lock
+from updatedbus import UpdateServerObject
import gobject
gobject.threads_init()
from jobmanager import JobManager
JobManager(True)
-global updateDbusHandler, dbusHandler
app = App()
dbusHandler = ServerObject(app)
-updateDbusHandler = UpdateServerObject(app)
# Start the HTTP server in a new thread
thread.start_new_thread(start_server, ())
from portrait import FremantleRotation
from threading import Thread, activeCount
from feedingitdbus import ServerObject
-from updatedbus import UpdateServerObject, get_lock
from config import Config
from cgi import escape
import weakref
+import dbus
import debugging
import logging
logger = logging.getLogger(__name__)
from opml import GetOpmlData, ExportOpmlData
import mainthread
-from jobmanager import JobManager
from socket import setdefaulttimeout
timeout = 5
if hasattr (cls, 'class_init_done'):
return
- jm = JobManager ()
- jm.stats_hook_register (cls.update_progress,
- run_in_main_thread=True)
-
cls.downloadbars = []
# Total number of jobs we are monitoring.
cls.total = 0
cls.class_init_done = True
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(handler_function=cls.update_progress,
+ bus_name=None,
+ signal_name='UpdateProgress',
+ dbus_interface='org.marcoz.feedingit',
+ path='/org/marcoz/feedingit/update')
+
def __init__(self, parent):
self.class_init ()
@classmethod
def downloading(cls):
- return hasattr (cls, 'jobs_at_start')
+ cls.class_init ()
+ return cls.done != cls.total
@classmethod
- def update_progress(cls, jm, old_stats, new_stats, updated_feed):
- if not cls.downloading():
- cls.jobs_at_start = old_stats['jobs-completed']
-
+ def update_progress(cls, percent_complete,
+ completed, in_progress, queued,
+ bytes_downloaded, bytes_updated, bytes_per_second,
+ feed_updated):
if not cls.downloadbars:
return
- if new_stats['jobs-in-progress'] + new_stats['jobs-queued'] == 0:
- del cls.jobs_at_start
+ cls.total = completed + in_progress + queued
+ cls.done = completed
+ cls.progress = percent_complete / 100.
+ if cls.progress < 0: cls.progress = 0
+ if cls.progress > 1: cls.progress = 1
+
+ if feed_updated:
for ref in cls.downloadbars:
bar = ref ()
if bar is None:
# The download bar disappeared.
cls.downloadbars.remove (ref)
else:
- bar.emit("download-done", None)
- return
+ bar.emit("download-done", feed_updated)
- # This should never be called if new_stats['jobs'] is 0, but
- # just in case...
- cls.total = max (1, new_stats['jobs'] - cls.jobs_at_start)
- cls.done = new_stats['jobs-completed'] - cls.jobs_at_start
- cls.progress = 1 - (new_stats['jobs-in-progress'] / 2.
- + new_stats['jobs-queued']) / cls.total
- cls.update_bars()
-
- if updated_feed:
+ if in_progress == 0 and queued == 0:
for ref in cls.downloadbars:
bar = ref ()
if bar is None:
# The download bar disappeared.
cls.downloadbars.remove (ref)
else:
- bar.emit("download-done", updated_feed)
+ bar.emit("download-done", None)
+ return
+
+ cls.update_bars()
@classmethod
def update_bars(cls):
iface.open_new_window(link)
class DisplayFeed(hildon.StackableWindow):
- def __init__(self, listing, feed, title, key, config, updateDbusHandler):
+ def __init__(self, listing, feed, title, key, config):
hildon.StackableWindow.__init__(self)
self.listing = listing
self.feed = feed
self.key=key
self.current = list()
self.config = config
- self.updateDbusHandler = updateDbusHandler
self.downloadDialog = False
#self.feed.saveFeed(CONFIGDIR)
self.displayFeed()
- def button_update_clicked(self, button):
+
+ def do_update_feed(self):
self.listing.updateFeed (self.key, priority=-1)
+
+ def button_update_clicked(self, button):
+ gobject.idle_add(self.do_update_feed)
def show_download_bar(self):
if not type(self.downloadDialog).__name__=="DownloadBar":
self.show_all()
def onDownloadDone(self, widget, feed):
- if feed == self.feed or feed is None:
- self.downloadDialog.destroy()
- self.downloadDialog = False
+ if feed == self.feed:
self.feed = self.listing.getFeed(self.key)
self.displayFeed()
- self.updateDbusHandler.ArticleCountUpdated()
+
+ if feed is None:
+ self.downloadDialog.destroy()
+ self.downloadDialog = False
def buttonReadAllClicked(self, button):
#self.clear()
self.config = Config(self.window, CONFIGDIR+"config.ini")
gobject.idle_add(self.createWindow)
- # This is set to try when the user interacts with the program.
- # If, after an update is complete, we discover that the
- # environment variable DBUS_STARTED_ADDRESS is set and
- # self.had_interaction is False, we quit.
- self.had_interaction = False
-
def createWindow(self):
self.category = 0
-
- self.app_lock = get_lock("app_lock")
- if self.app_lock == None:
- try:
- self.stopButton.set_sensitive(True)
- except:
- self.stopButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
- self.stopButton.set_text("Stop update","")
- self.stopButton.connect("clicked", self.stop_running_update)
- self.mainVbox.pack_end(self.stopButton, expand=False, fill=False)
- self.window.show_all()
- self.introLabel.set_label("Update in progress, please wait.")
- gobject.timeout_add_seconds(3, self.createWindow)
- return False
- try:
- self.stopButton.destroy()
- except:
- pass
self.listing = Listing(self.config, CONFIGDIR)
self.downloadDialog = False
hildon.hildon_gtk_window_set_progress_indicator(self.window, 0)
gobject.idle_add(self.late_init)
- def job_manager_update(self, jm, old_stats, new_stats, updated_feed):
- if (not self.downloadDialog
- and new_stats['jobs-in-progress'] + new_stats['jobs-queued'] > 0):
- self.updateDbusHandler.UpdateStarted()
-
+ def update_progress(self, percent_complete,
+ completed, in_progress, queued,
+ bytes_downloaded, bytes_updated, bytes_per_second,
+ updated_feed):
+ if (in_progress or queued) and not self.downloadDialog:
self.downloadDialog = DownloadBar(self.window)
self.downloadDialog.connect("download-done", self.onDownloadDone)
self.mainVbox.pack_end(self.downloadDialog, expand=False, fill=False)
self.downloadDialog.destroy()
self.downloadDialog = False
self.displayListing()
- self.updateDbusHandler.UpdateFinished()
- self.updateDbusHandler.ArticleCountUpdated()
-
- if not self.had_interaction and 'DBUS_STARTER_ADDRESS' in environ:
- logger.info(
- "Update complete. No interaction, started by dbus: quitting.")
- self.quit()
- def stop_running_update(self, button):
- self.stopButton.set_sensitive(False)
- import dbus
- bus=dbus.SessionBus()
- remote_object = bus.get_object("org.marcoz.feedingit", # Connection name
- "/org/marcoz/feedingit/update" # Object's path
- )
- iface = dbus.Interface(remote_object, 'org.marcoz.feedingit')
- iface.StopUpdate()
-
- def increase_download_parallelism(self):
- # The system has been idle for a while. Enable parallel
- # downloads.
- JobManager().num_threads = 4
- gobject.source_remove (self.increase_download_parallelism_id)
- del self.increase_download_parallelism_id
- return False
-
- def system_inactivity_ind(self, idle):
- # The system's idle state changed.
- if (self.am_idle and idle) or (not self.am_idle and not idle):
- # No change.
- return
-
- if not idle:
- if hasattr (self, 'increase_download_parallelism_id'):
- gobject.source_remove (self.increase_download_parallelism_id)
- del self.increase_download_parallelism_id
- else:
- self.increase_download_parallelism_id = \
- gobject.timeout_add_seconds(
- 60, self.increase_download_parallelism)
-
- if not idle:
- JobManager().num_threads = 1
-
- self.am_idle = idle
def late_init(self):
self.dbusHandler = ServerObject(self)
- self.updateDbusHandler = UpdateServerObject(self)
-
- jm = JobManager()
- jm.stats_hook_register (self.job_manager_update,
- run_in_main_thread=True)
- jm.num_threads = 1
- self.am_idle = False
- JobManager(True)
-
- import dbus
- bus = dbus.SystemBus()
- proxy = bus.get_object('com.nokia.mce',
- '/com/nokia/mce/signal')
- iface = dbus.Interface(proxy, 'com.nokia.mce.signal')
- iface.connect_to_signal('system_inactivity_ind',
- self.system_inactivity_ind)
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(handler_function=self.update_progress,
+ bus_name=None,
+ signal_name='UpdateProgress',
+ dbus_interface='org.marcoz.feedingit',
+ path='/org/marcoz/feedingit/update')
def button_markAll(self, button):
- self.had_interaction = True
for key in self.listing.getListOfFeeds():
feed = self.listing.getFeed(key)
feed.markAllAsRead()
self.displayListing()
def button_about_clicked(self, button):
- self.had_interaction = True
HeAboutDialog.present(self.window, \
__appname__, \
ABOUT_ICON, \
ABOUT_DONATE)
def button_export_clicked(self, button):
- self.had_interaction = True
opml = ExportOpmlData(self.window, self.listing)
def button_import_clicked(self, button):
- self.had_interaction = True
opml = GetOpmlData(self.window)
feeds = opml.getData()
for (title, url) in feeds:
self.displayListing()
def addFeed(self, urlIn="http://"):
- self.had_interaction = True
wizard = AddWidgetWizard(self.window, self.listing, urlIn, self.listing.getListOfCategories())
ret = wizard.run()
if ret == 2:
self.displayListing()
def button_organize_clicked(self, button):
- self.had_interaction = True
def after_closing():
self.displayListing()
SortList(self.window, self.listing, self, after_closing)
- def button_update_clicked(self, button, key):
- self.had_interaction = True
+ def do_update_feeds(self):
for k in self.listing.getListOfFeeds():
self.listing.updateFeed (k)
- #self.displayListing()
+
+ def button_update_clicked(self, button, key):
+ gobject.idle_add(self.do_update_feeds)
def onDownloadsDone(self, *widget):
self.downloadDialog.destroy()
self.downloadDialog = False
self.displayListing()
- self.updateDbusHandler.UpdateFinished()
- self.updateDbusHandler.ArticleCountUpdated()
def button_preferences_clicked(self, button):
- self.had_interaction = True
dialog = self.config.createDialog()
dialog.connect("destroy", self.prefsClosed)
# pass
def on_feedList_row_activated(self, treeview, path, column):
- self.had_interaction = True
model = treeview.get_model()
iter = model.get_iter(path)
key = model.get_value(iter, COLUMN_KEY)
self.openFeed(key)
def openFeed(self, key):
- try:
- self.feed_lock
- except:
- # If feed_lock doesn't exist, we can open the feed, else we do nothing
- if key != None:
- self.feed_lock = get_lock(key)
- self.disp = DisplayFeed(self.listing, self.listing.getFeed(key), \
- self.listing.getFeedTitle(key), key, \
- self.config, self.updateDbusHandler)
- self.disp.connect("feed-closed", self.onFeedClosed)
+ if key != None:
+ self.disp = DisplayFeed(
+ self.listing, self.listing.getFeed(key),
+ self.listing.getFeedTitle(key), key,
+ self.config)
+ self.disp.connect("feed-closed", self.onFeedClosed)
def openArticle(self, key, id):
- try:
- self.feed_lock
- except:
- # If feed_lock doesn't exist, we can open the feed, else we do nothing
- if key != None:
- self.feed_lock = get_lock(key)
- self.disp = DisplayFeed(self.listing, self.listing.getFeed(key), \
- self.listing.getFeedTitle(key), key, \
- self.config, self.updateDbusHandler)
- self.disp.button_clicked(None, id)
- self.disp.connect("feed-closed", self.onFeedClosed)
-
+ if key != None:
+ self.openFeed(key)
+ self.disp.button_clicked(None, id)
def onFeedClosed(self, object, key):
- #self.listing.saveConfig()
- #del self.feed_lock
- gobject.idle_add(self.onFeedClosedTimeout)
self.displayListing()
- #self.updateDbusHandler.ArticleCountUpdated()
- def onFeedClosedTimeout(self):
- del self.feed_lock
- self.updateDbusHandler.ArticleCountUpdated()
-
def quit(self, *args):
self.window.hide()
-
- if hasattr (self, 'app_lock'):
- del self.app_lock
-
- # Wait until all slave threads have properly exited before
- # terminating the mainloop.
- jm = JobManager()
- jm.quit ()
- stats = jm.stats()
- if stats['jobs-in-progress'] == 0 and stats['jobs-queued'] == 0:
- gtk.main_quit ()
- else:
- gobject.timeout_add(500, self.quit)
-
- return False
+ gtk.main_quit ()
def run(self):
self.window.connect("destroy", self.quit)
self.button_update_clicked(None, None)
return True
- def stopUpdate(self):
- # Not implemented in the app (see update_feeds.py)
- try:
- JobManager().cancel ()
- except:
- pass
-
def getStatus(self):
status = ""
for key in self.listing.getListOfFeeds():
from BeautifulSoup import BeautifulSoup
from urlparse import urljoin
from calendar import timegm
-from updatedbus import get_lock, release_lock
import threading
import traceback
from wc import wc, wc_init, woodchuck
+import subprocess
+import dbus
+from updatedbus import update_server_object
from jobmanager import JobManager
import mainthread
return urllib2.build_opener (*openers)
+# If not None, a subprocess.Popen object corresponding to a
+# update_feeds.py process.
+update_feed_process = None
+
+update_feeds_iface = None
+
+jobs_at_start = 0
+
class Feed:
serial_execution_lock = threading.Lock()
return filename
def updateFeed(self, configdir, url, etag, modified, expiryTime=24, proxy=None, imageCache=False, priority=0, postFeedUpdateFunc=None, *postFeedUpdateFuncArgs):
- def doit():
- def it():
- self._updateFeed(configdir, url, etag, modified, expiryTime, proxy, imageCache, postFeedUpdateFunc, *postFeedUpdateFuncArgs)
- return it
- JobManager().execute(doit(), self.key, priority=priority)
+ if (os.path.basename(sys.argv[0]) == 'update_feeds.py'):
+ def doit():
+ def it():
+ self._updateFeed(configdir, url, etag, modified, expiryTime, proxy, imageCache, postFeedUpdateFunc, *postFeedUpdateFuncArgs)
+ return it
+ JobManager().execute(doit(), self.key, priority=priority)
+ else:
+ def send_update_request():
+ global update_feeds_iface
+ if update_feeds_iface is None:
+ bus=dbus.SessionBus()
+ remote_object = bus.get_object(
+ "org.marcoz.feedingit", # Connection name
+ "/org/marcoz/feedingit/update" # Object's path
+ )
+ update_feeds_iface = dbus.Interface(
+ remote_object, 'org.marcoz.feedingit')
+
+ try:
+ update_feeds_iface.Update(self.key)
+ except Exception, e:
+ logger.error("Invoking org.marcoz.feedingit.Update: %s"
+ % str(e))
+ update_feeds_iface = None
+ else:
+ return True
+
+ if send_update_request():
+ # Success! It seems we were able to start the update
+ # daemon via dbus (or, it was already running).
+ return
+
+ global update_feed_process
+ if (update_feed_process is None
+ or update_feed_process.poll() is not None):
+ # The update_feeds process is not running. Start it.
+ update_feeds = os.path.join(os.path.dirname(__file__),
+ 'update_feeds.py')
+ argv = ['/usr/bin/env', 'python', update_feeds, '--daemon' ]
+ logger.debug("Starting update_feeds: running %s"
+ % (str(argv),))
+ update_feed_process = subprocess.Popen(argv)
+ # Make sure the dbus calls go to the right process:
+ # rebind.
+ update_feeds_iface = None
+
+ for _ in xrange(5):
+ if send_update_request():
+ break
+ time.sleep(1)
def _updateFeed(self, configdir, url, etag, modified, expiryTime=24, proxy=None, imageCache=False, postFeedUpdateFunc=None, *postFeedUpdateFuncArgs):
success = False
have_serial_execution_lock = False
try:
- update_lock = None
- update_lock = get_lock("key")
- if not update_lock:
- # Someone else is doing an update.
- return
-
download_start = time.time ()
progress_handler = HTTPProgressHandler(download_callback)
if have_serial_execution_lock:
self.serial_execution_lock.release ()
- if update_lock is not None:
- release_lock (update_lock)
-
updateTime = 0
try:
rows = self.db.execute("SELECT MAX(date) FROM feed;")
# Check that Woodchuck's state is up to date with respect our
# state.
- wc_init (self)
- if wc().available():
+ updater = os.path.basename(sys.argv[0]) == 'update_feeds.py'
+ wc_init (self, True if updater else False)
+ if wc().available() and updater:
# The list of known streams.
streams = wc().streams_list ()
stream_ids = [s.identifier for s in streams]
(title, key))
self.db.commit()
self.updateUnread(key)
+
+ update_server_object().ArticleCountUpdated()
+
+ stats = JobManager().stats()
+ global jobs_at_start
+ completed = stats['jobs-completed'] - jobs_at_start
+ in_progress = stats['jobs-in-progress']
+ queued = stats['jobs-queued']
+
+ percent = (100 * ((completed + in_progress / 2.))
+ / (completed + in_progress + queued))
+
+ update_server_object().UpdateProgress(
+ percent, completed, in_progress, queued, 0, 0, 0, key)
+
+ if in_progress == 0 and queued == 0:
+ jobs_at_start = stats['jobs-completed']
def getFeed(self, key):
if key == "ArchivedArticles":
from rss_sqlite import Listing
from config import Config
-from updatedbus import get_lock, UpdateServerObject
+from updatedbus import UpdateServerObject
import os
-import gobject
import traceback
+import sys
+import dbus
from jobmanager import JobManager
import mainthread
+import gobject
+gobject.threads_init()
+
import logging
logger = logging.getLogger(__name__)
import debugging
CONFIGDIR="/home/user/.feedingit/"
#DESKTOP_FILE = "/usr/share/applications/hildon-status-menu/feedingit_status.desktop"
-dbug = False
from socket import setdefaulttimeout
timeout = 5
setdefaulttimeout(timeout)
del timeout
-from updatedbus import UpdateServerObject
+class FeedUpdate(UpdateServerObject):
+ def __init__(self, bus_name):
+ UpdateServerObject.__init__(self, bus_name)
-class FeedUpdate():
- def __init__(self):
self.config = Config(self, CONFIGDIR+"config.ini")
- self.dbusHandler = UpdateServerObject(self)
self.listing = Listing(self.config, CONFIGDIR)
- self.done = False
jm = JobManager(True)
jm.stats_hook_register (self.job_manager_update,
run_in_main_thread=True)
- self.dbusHandler.UpdateStarted()
- for k in self.listing.getListOfFeeds():
- self.listing.updateFeed (k)
-
+ # Whether or no an update is in progress.
+ self.am_updating = False
+
+ # After an update an finished, we start the inactivity timer.
+ # If this fires before a new job arrives, we quit.
+ self.inactivity_timer = 0
+
+ # Whether we started in daemon mode, or not.
+ self.daemon = '--daemon' in sys.argv
+
+ if self.daemon:
+ logger.debug("Running in daemon mode: waiting for commands.")
+ self.inactivity_timer = gobject.timeout_add(
+ 5 * 60 * 1000, self.inactivity_cb)
+ else:
+ # Update all feeds.
+ logger.debug("Not running in daemon mode: updating all feeds.")
+ gobject.idle_add(self.UpdateAll)
+
+ # If the system becomes idle
+ bus = dbus.SystemBus()
+
+ mce_request_proxy = bus.get_object(
+ 'com.nokia.mce', '/com/nokia/mce/request')
+ mce_request_iface = dbus.Interface(
+ mce_request_proxy, 'com.nokia.mce.request')
+ system_idle = mce_request_iface.get_inactivity_status()
+ # Force self.system_inactivity_ind to run: ensure that a state
+ # change occurs.
+ self.system_idle = not system_idle
+ self.system_inactivity_ind(system_idle)
+
+ mce_signal_proxy = bus.get_object(
+ 'com.nokia.mce', '/com/nokia/mce/signal')
+ mce_signal_iface = dbus.Interface(
+ mce_signal_proxy, 'com.nokia.mce.signal')
+ mce_signal_iface.connect_to_signal(
+ 'system_inactivity_ind', self.system_inactivity_ind)
+
+ def increase_download_parallelism(self):
+ # The system has been idle for a while. Enable parallel
+ # downloads.
+ logger.debug("Increasing parallelism to 4 workers.")
+ JobManager().num_threads = 4
+ gobject.source_remove (self.increase_download_parallelism_id)
+ del self.increase_download_parallelism_id
+ return False
+
+ def system_inactivity_ind(self, idle):
+ # The system's idle state changed.
+ if (self.system_idle and idle) or (not self.system_idle and not idle):
+ # No change.
+ return
+
+ if not idle:
+ if hasattr (self, 'increase_download_parallelism_id'):
+ gobject.source_remove (self.increase_download_parallelism_id)
+ del self.increase_download_parallelism_id
+ else:
+ self.increase_download_parallelism_id = \
+ gobject.timeout_add_seconds(
+ 60, self.increase_download_parallelism)
+
+ if not idle:
+ logger.debug("Reducing parallelism to 1 worker.")
+ JobManager().num_threads = 1
+
+ self.system_idle = idle
+
def job_manager_update(self, jm, old_stats, new_stats, updated_feed):
- if not new_stats['jobs-queued'] and not new_stats['jobs-in-progress']:
- self.dbusHandler.UpdateFinished()
- self.dbusHandler.ArticleCountUpdated()
- self.done = True
+ queued = new_stats['jobs-queued']
+ in_progress = new_stats['jobs-in-progress']
+
+ if (queued or in_progress) and not self.am_updating:
+ logger.debug("new update started")
+ self.am_updating = True
+ self.UpdateStarted()
+ self.UpdateProgress(0, 0, in_progress, queued, 0, 0, 0, "")
+
+ if not queued and not in_progress:
+ logger.debug("update finished!")
+ self.am_updating = False
+ self.UpdateFinished()
+ self.ArticleCountUpdated()
+
+ if self.daemon:
+ self.inactivity_timer = gobject.timeout_add(
+ 60 * 1000, self.inactivity_cb)
+ else:
+ logger.debug("update finished, not running in daemon mode: "
+ "quitting")
+ mainloop.quit()
+
+ if (queued or in_progress) and self.inactivity_timer:
+ gobject.source_remove(self.inactivity_timer)
+ self.inactivity_timer = 0
+
+ def inactivity_cb(self):
+ """
+ The updater has been inactive for a while. Quit.
+ """
+ assert self.inactivity_timer
+ self.inactivity_timer = 0
+
+ if not self.am_updating:
+ logger.info("Nothing to do for a while. Quitting.")
mainloop.quit()
- def stopUpdate(self):
- logger.info("Stop update called.")
+ def StopUpdate(self):
+ """
+ Stop updating.
+ """
+ super(FeedUpdate, self).stopUpdate()
+
JobManager().quit()
+ def UpdateAll(self):
+ """
+ Update all feeds.
+ """
+ super(FeedUpdate, self).UpdateAll()
+
+ feeds = self.listing.getListOfFeeds()
+ for k in feeds:
+ self.listing.updateFeed(k)
+ logger.debug("Queued all feeds (%d) for update." % len(feeds))
+
+ def Update(self, feed):
+ """
+ Update a particular feed.
+ """
+ super(FeedUpdate, self).Update(feed)
+
+ # We got a request via dbus. If we weren't in daemon mode
+ # before, enter it now.
+ self.daemon = True
+
+ self.listing.updateFeed(feed)
+
+
import dbus.mainloop.glib
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-gobject.threads_init()
-mainthread.init()
mainloop = gobject.MainLoop()
+mainthread.init()
+
+# Acquire our name on the session bus. If this doesn't work, most
+# likely another update_feeds instance is already running. In this
+# case, just quit.
+try:
+ bus_name = dbus.service.BusName('org.marcoz.feedingit',
+ bus=dbus.SessionBus(),
+ do_not_queue=True)
+except Exception:
+ # We failed to acquire our bus name. Die.
+ try:
+ dbus_proxy = dbus.SessionBus().get_object(
+ 'org.freedesktop.DBus', '/org/freedesktop/DBus')
+ dbus_iface = dbus.Interface(dbus_proxy, 'org.freedesktop.DBus')
+ pid = dbus_iface.GetConnectionUnixProcessID('org.marcoz.feedingit')
+ logger.error("update_feeds already running: pid %d." % pid)
+ except Exception, e:
+ logger.error("Getting pid associated with org.marcoz.feedingit: %s"
+ % str(e))
+ logger.error("update_feeds already running.")
+
+ sys.exit(1)
+
+# Run the updater. Note: we run this until feed.am_updating is false.
+# Only is this case have all worker threads exited. If the main
+# thread exits before all threads have exited and the process gets a
+# signal, the Python interpreter is unable to handle the signal and it
+# runs really slow (rescheduling after ever single instruction instead
+# of every few thousand).
+feed = FeedUpdate(bus_name)
+while True:
+ try:
+ mainloop.run()
+ except KeyboardInterrupt:
+ logger.error("Interrupted. Quitting.")
+ JobManager().quit()
+
+ if not feed.am_updating:
+ break
-app_lock = get_lock("app_lock")
-
-if app_lock != None:
- feed = FeedUpdate()
- while not feed.done:
- try:
- mainloop.run()
- except KeyboardInterrupt:
- logger.error("Interrupted. Quitting.")
- JobManager().quit()
- del app_lock
-else:
- logger.error("Update in progress")
#
# Copyright (c) 2007-2008 INdT.
+# Copyright (c) 2011 Neal H. Walfield
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# Description : Simple RSS Reader
# ============================================================================
-import dbus
import dbus.service
-import mainthread
+import logging
+logger = logging.getLogger(__name__)
-@mainthread.mainthread
-def get_lock(key):
- try:
- bus_name = dbus.service.BusName('org.marcoz.feedingit.lock_%s' %key,bus=dbus.SessionBus(), do_not_queue=True)
- except:
- bus_name = None
- return bus_name
-
-def release_lock(lock):
- assert lock is not None
- mainthread.execute(lock.__del__, async=True)
+_update_server_object = None
+def update_server_object():
+ global _update_server_object
+ assert _update_server_object is not None, \
+ "No UpdateServerObject instantiated!"
+ return _update_server_object
class UpdateServerObject(dbus.service.Object):
- def __init__(self, app):
- # Here the service name
- bus_name = dbus.service.BusName('org.marcoz.feedingit',bus=dbus.SessionBus())
- # Here the object path
- dbus.service.Object.__init__(self, bus_name, '/org/marcoz/feedingit/update')
- self.app = app
+ def __init__(self, bus_name):
+ """
+ Start listening for requests.
+ """
+ global _update_server_object
+ assert _update_server_object is None, \
+ "Attempt to instantiate multiple UpdateServerObject objects."
+ _update_server_object = self
+
+ dbus.service.Object.__init__(self, bus_name,
+ '/org/marcoz/feedingit/update')
- @dbus.service.method('org.marcoz.feedingit')
- def UpdateAll(self):
- self.app.automaticUpdate()
- return "Done"
-
@dbus.service.method('org.marcoz.feedingit')
def StopUpdate(self):
- self.app.stopUpdate()
- return "Done"
+ logger.debug("Stop update called.")
+
+ @dbus.service.method('org.marcoz.feedingit')
+ def UpdateAll(self):
+ logger.debug("UpdateAll called.")
+
+ @dbus.service.method('org.marcoz.feedingit', in_signature='s')
+ def Update(self, feed):
+ logger.debug("Update(%s) called." % feed)
# A signal that will be exported to dbus
@dbus.service.signal('org.marcoz.feedingit', signature='')
pass
# A signal that will be exported to dbus
+ @dbus.service.signal('org.marcoz.feedingit', signature='uuuuttus')
+ def UpdateProgress(self, percent_complete,
+ feeds_downloaded, feeds_downloading, feeds_pending,
+ bytes_downloaded, bytes_uploaded, bytes_per_second,
+ updated_feed):
+ pass
+
+ # A signal that will be exported to dbus
@dbus.service.signal('org.marcoz.feedingit', signature='')
def UpdateStarted(self):
pass
# A signal that will be exported to dbus
@dbus.service.signal('org.marcoz.feedingit', signature='')
def UpdateFinished(self):
- pass
\ No newline at end of file
+ pass
+
+
refresh_interval = 6 * 60 * 60
class mywoodchuck (PyWoodchuck):
- def __init__(self, listing, *args):
- PyWoodchuck.__init__ (self, *args)
+ def __init__(self, listing, human_readable_name, identifier,
+ request_feedback):
+ PyWoodchuck.__init__ (self, human_readable_name, identifier,
+ request_feedback)
self.listing = listing
stream.human_readable_name, stream.identifier))
_w = None
-def wc_init(listing):
+def wc_init(listing, request_feedback=False):
global _w
assert _w is None
- _w = mywoodchuck (listing, "FeedingIt", "org.maemo.feedingit")
+ _w = mywoodchuck (listing, "FeedingIt", "org.marcoz.feedingit",
+ request_feedback)
if not woodchuck_imported or not _w.available ():
logger.info("Unable to contact Woodchuck server.")