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 #CONFIGDIR="/home/user/.feedingit/"
48 CONFIGDIR = os.environ.get("HOME", "/home/user") + "/.feedingit/"
49 #DESKTOP_FILE = "/usr/share/applications/hildon-status-menu/feedingit_status.desktop"
51 from socket import setdefaulttimeout
53 setdefaulttimeout(timeout)
56 class FeedUpdate(UpdateServerObject):
57 def __init__(self, bus_name):
58 UpdateServerObject.__init__(self, bus_name)
60 self.config = Config(self, CONFIGDIR+"config.ini")
61 self.listing = Listing(self.config, CONFIGDIR)
64 jm.stats_hook_register (self.job_manager_update,
65 run_in_main_thread=True)
67 # Whether or no an update is in progress.
68 self.am_updating = False
70 # After an update an finished, we start the inactivity timer.
71 # If this fires before a new job arrives, we quit.
72 self.inactivity_timer = 0
74 # Whether we started in daemon mode, or not.
75 self.daemon = '--daemon' in sys.argv
78 logger.debug("Running in daemon mode: waiting for commands.")
79 self.inactivity_timer = gobject.timeout_add(
80 5 * 60 * 1000, self.inactivity_cb)
83 logger.debug("Not running in daemon mode: updating all feeds.")
84 gobject.idle_add(self.UpdateAll)
86 # # If the system becomes idle
87 # bus = dbus.SystemBus()
89 # mce_request_proxy = bus.get_object(
90 # 'com.nokia.mce', '/com/nokia/mce/request')
91 # mce_request_iface = dbus.Interface(
92 # mce_request_proxy, 'com.nokia.mce.request')
93 # system_idle = mce_request_iface.get_inactivity_status()
94 # # Force self.system_inactivity_ind to run: ensure that a state
96 # self.system_idle = not system_idle
97 # self.system_inactivity_ind(system_idle)
99 # mce_signal_proxy = bus.get_object(
100 # 'com.nokia.mce', '/com/nokia/mce/signal')
101 # mce_signal_iface = dbus.Interface(
102 # mce_signal_proxy, 'com.nokia.mce.signal')
103 # mce_signal_iface.connect_to_signal(
104 # 'system_inactivity_ind', self.system_inactivity_ind)
106 def increase_download_parallelism(self):
107 # The system has been idle for a while. Enable parallel
109 logger.debug("Increasing parallelism to 4 workers.")
110 JobManager().num_threads = 4
111 gobject.source_remove (self.increase_download_parallelism_id)
112 del self.increase_download_parallelism_id
115 def system_inactivity_ind(self, idle):
116 # The system's idle state changed.
117 if (self.system_idle and idle) or (not self.system_idle and not idle):
122 if hasattr (self, 'increase_download_parallelism_id'):
123 gobject.source_remove (self.increase_download_parallelism_id)
124 del self.increase_download_parallelism_id
126 self.increase_download_parallelism_id = \
127 gobject.timeout_add_seconds(
128 60, self.increase_download_parallelism)
131 logger.debug("Reducing parallelism to 1 worker.")
132 JobManager().num_threads = 1
134 self.system_idle = idle
136 def job_manager_update(self, jm, old_stats, new_stats, updated_feed):
137 queued = new_stats['jobs-queued']
138 in_progress = new_stats['jobs-in-progress']
140 if (queued or in_progress) and not self.am_updating:
141 logger.debug("new update started")
142 self.am_updating = True
144 self.UpdateProgress(0, 0, in_progress, queued, 0, 0, 0, "")
146 if not queued and not in_progress:
147 logger.debug("update finished!")
148 self.am_updating = False
149 self.UpdateFinished()
150 self.ArticleCountUpdated()
153 self.inactivity_timer = gobject.timeout_add(
154 60 * 1000, self.inactivity_cb)
156 logger.debug("update finished, not running in daemon mode: "
160 if (queued or in_progress) and self.inactivity_timer:
161 gobject.source_remove(self.inactivity_timer)
162 self.inactivity_timer = 0
164 def inactivity_cb(self):
166 The updater has been inactive for a while. Quit.
168 assert self.inactivity_timer
169 self.inactivity_timer = 0
171 if not self.am_updating:
172 logger.info("Nothing to do for a while. Quitting.")
175 def StopUpdate(self):
179 super(FeedUpdate, self).stopUpdate()
187 logger.info("starting update.")
188 super(FeedUpdate, self).UpdateAll()
190 feeds = self.listing.getListOfFeeds()
192 self.listing.updateFeed(k)
193 logger.debug("Queued all feeds (%d) for update." % len(feeds))
195 def Update(self, feed):
197 Update a particular feed.
199 super(FeedUpdate, self).Update(feed)
201 # We got a request via dbus. If we weren't in daemon mode
202 # before, enter it now.
205 self.listing.updateFeed(feed)
208 import dbus.mainloop.glib
209 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
211 mainloop = gobject.MainLoop()
214 # Acquire our name on the session bus. If this doesn't work, most
215 # likely another update_feeds instance is already running. In this
218 bus_name = dbus.service.BusName('org.marcoz.feedingit',
219 bus=dbus.SessionBus(),
222 # We failed to acquire our bus name. Die.
224 dbus_proxy = dbus.SessionBus().get_object(
225 'org.freedesktop.DBus', '/org/freedesktop/DBus')
226 dbus_iface = dbus.Interface(dbus_proxy, 'org.freedesktop.DBus')
227 pid = dbus_iface.GetConnectionUnixProcessID('org.marcoz.feedingit')
228 logger.error("update_feeds already running: pid %d." % pid)
230 logger.error("Getting pid associated with org.marcoz.feedingit: %s"
232 logger.error("update_feeds already running.")
236 # Run the updater. Note: we run this until feed.am_updating is false.
237 # Only is this case have all worker threads exited. If the main
238 # thread exits before all threads have exited and the process gets a
239 # signal, the Python interpreter is unable to handle the signal and it
240 # runs really slow (rescheduling after ever single instruction instead
241 # of every few thousand).
242 feed = FeedUpdate(bus_name)
246 except KeyboardInterrupt:
247 logger.error("Interrupted. Quitting.")
250 if not feed.am_updating: