1 #!/usr/bin/env python2.5
4 # Copyright (c) 2007-2008 INdT.
5 # Copyright (c) 2011 Neal H. Walfield
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # ============================================================================
21 # Name : update_feeds.py
22 # Author : Yves Marcoz
24 # Description : Simple RSS Reader
25 # ============================================================================
27 from rss_sqlite import Listing
28 from config import Config
29 from updatedbus import UpdateServerObject
36 from jobmanager import JobManager
40 gobject.threads_init()
43 logger = logging.getLogger(__name__)
45 debugging.init(dot_directory=".feedingit", program_name="update_feeds")
47 from updatedbus import update_server_object
49 CONFIGDIR = os.environ.get("HOME", "/home/user") + "/.feedingit/"
50 #DESKTOP_FILE = "/usr/share/applications/hildon-status-menu/feedingit_status.desktop"
52 from socket import setdefaulttimeout
54 setdefaulttimeout(timeout)
57 class FeedUpdate(UpdateServerObject):
58 def __init__(self, bus_name):
59 UpdateServerObject.__init__(self, bus_name)
61 self.config = Config(self, CONFIGDIR+"config.ini")
62 self.listing = Listing(self.config, CONFIGDIR)
65 jm.stats_hook_register (self.job_manager_update,
66 run_in_main_thread=True)
68 # Whether or no an update is in progress.
69 self.am_updating = False
71 # After an update an finished, we start the inactivity timer.
72 # If this fires before a new job arrives, we quit.
73 self.inactivity_timer = 0
75 # Whether we started in daemon mode, or not.
76 self.daemon = '--daemon' in sys.argv
79 logger.debug("Running in daemon mode: waiting for commands.")
80 self.inactivity_timer = gobject.timeout_add(
81 5 * 60 * 1000, self.inactivity_cb)
84 logger.debug("Not running in daemon mode: updating all feeds.")
85 gobject.idle_add(self.UpdateAll)
87 # If the system becomes idle
88 bus = dbus.SystemBus()
90 mce_request_proxy = bus.get_object(
91 'com.nokia.mce', '/com/nokia/mce/request')
92 mce_request_iface = dbus.Interface(
93 mce_request_proxy, 'com.nokia.mce.request')
94 system_idle = mce_request_iface.get_inactivity_status()
95 # Force self.system_inactivity_ind to run: ensure that a state
97 self.system_idle = not system_idle
98 self.system_inactivity_ind(system_idle)
100 mce_signal_proxy = bus.get_object(
101 'com.nokia.mce', '/com/nokia/mce/signal')
102 mce_signal_iface = dbus.Interface(
103 mce_signal_proxy, 'com.nokia.mce.signal')
104 mce_signal_iface.connect_to_signal(
105 'system_inactivity_ind', self.system_inactivity_ind)
107 def increase_download_parallelism(self):
108 # The system has been idle for a while. Enable parallel
110 logger.debug("Increasing parallelism to 4 workers.")
111 JobManager().num_threads = 4
112 gobject.source_remove (self.increase_download_parallelism_id)
113 del self.increase_download_parallelism_id
116 def system_inactivity_ind(self, idle):
117 # The system's idle state changed.
118 if (self.system_idle and idle) or (not self.system_idle and not idle):
123 if hasattr (self, 'increase_download_parallelism_id'):
124 gobject.source_remove (self.increase_download_parallelism_id)
125 del self.increase_download_parallelism_id
127 self.increase_download_parallelism_id = \
128 gobject.timeout_add_seconds(
129 60, self.increase_download_parallelism)
132 logger.debug("Reducing parallelism to 1 worker.")
133 JobManager().num_threads = 1
135 self.system_idle = idle
137 def job_manager_update(self, jm, old_stats, new_stats, updated_feed):
138 queued = new_stats['jobs-queued']
139 in_progress = new_stats['jobs-in-progress']
141 if (queued or in_progress) and not self.am_updating:
142 logger.debug("new update started")
143 self.am_updating = True
145 self.UpdateProgress(0, 0, in_progress, queued, 0, 0, 0, "")
147 if not queued and not in_progress:
148 logger.debug("update finished!")
149 self.am_updating = False
150 self.UpdateFinished()
151 self.ArticleCountUpdated()
154 self.inactivity_timer = gobject.timeout_add(
155 60 * 1000, self.inactivity_cb)
157 logger.debug("update finished, not running in daemon mode: "
161 if (queued or in_progress) and self.inactivity_timer:
162 gobject.source_remove(self.inactivity_timer)
163 self.inactivity_timer = 0
165 def inactivity_cb(self):
167 The updater has been inactive for a while. Quit.
169 assert self.inactivity_timer
170 self.inactivity_timer = 0
172 if not self.am_updating:
173 logger.info("Nothing to do for a while. Quitting.")
175 # Make any progress bar go away.
177 update_server_object().UpdateProgress(
178 100, 0, 0, 0, 0, 0, 0, "")
180 logger.exception("Sending final progress update")
184 def StopUpdate(self):
188 super(FeedUpdate, self).stopUpdate()
196 super(FeedUpdate, self).UpdateAll()
198 feeds = self.listing.getListOfFeeds()
200 self.listing.updateFeed(k)
201 logger.debug("Queued all feeds (%d) for update." % len(feeds))
203 def Update(self, feed):
205 Update a particular feed.
207 super(FeedUpdate, self).Update(feed)
209 # We got a request via dbus. If we weren't in daemon mode
210 # before, enter it now.
213 self.listing.updateFeed(feed)
216 import dbus.mainloop.glib
217 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
219 mainloop = gobject.MainLoop()
222 # Acquire our name on the session bus. If this doesn't work, most
223 # likely another update_feeds instance is already running. In this
226 bus_name = dbus.service.BusName('org.marcoz.feedingit',
227 bus=dbus.SessionBus(),
230 # We failed to acquire our bus name. Die.
232 dbus_proxy = dbus.SessionBus().get_object(
233 'org.freedesktop.DBus', '/org/freedesktop/DBus')
234 dbus_iface = dbus.Interface(dbus_proxy, 'org.freedesktop.DBus')
235 pid = dbus_iface.GetConnectionUnixProcessID('org.marcoz.feedingit')
236 logger.error("update_feeds already running: pid %d." % pid)
238 logger.error("Getting pid associated with org.marcoz.feedingit: %s"
240 logger.error("update_feeds already running.")
244 # Run the updater. Note: we run this until feed.am_updating is false.
245 # Only is this case have all worker threads exited. If the main
246 # thread exits before all threads have exited and the process gets a
247 # signal, the Python interpreter is unable to handle the signal and it
248 # runs really slow (rescheduling after ever single instruction instead
249 # of every few thousand).
250 feed = FeedUpdate(bus_name)
254 except KeyboardInterrupt:
255 logger.error("Interrupted. Quitting.")
258 if not feed.am_updating: