install src/portrait.py ${DESTDIR}/opt/FeedingIt
install src/rss.py ${DESTDIR}/opt/FeedingIt
install src/opml.py ${DESTDIR}/opt/FeedingIt
+ install src/config.py ${DESTDIR}/opt/FeedingIt
install src/feedingitdbus.py ${DESTDIR}/opt/FeedingIt
install -d ${DESTDIR}/usr/share/applications/hildon
install src/FeedingIt.desktop ${DESTDIR}/usr/share/applications/hildon
install -d ${DESTDIR}/usr/share/icons/hicolor/40x40/apps/
- install data/40x40/feedingit.png ${DESTDIR}/usr/share/icons/hicolor/40x40/apps/
+ install data/40px.png ${DESTDIR}/usr/share/icons/hicolor/40x40/apps/feedingit.png
install -d ${DESTDIR}/usr/share/icons/hicolor/26x26/apps/
- install data/26x26/feedingit.png ${DESTDIR}/usr/share/icons/hicolor/26x26/apps/
+ install data/26px.png ${DESTDIR}/usr/share/icons/hicolor/26x26/apps/feedingit.png
install -d ${DESTDIR}/usr/share/dbus-1/services/
install src/feedingit.service ${DESTDIR}/usr/share/dbus-1/services/
\ No newline at end of file
+feedingit (0.3.1-1) unstable; urgency=low
+
+ * Fixed socket timeouts
+
+ -- Yves <yves@marcoz.org> Sat, 23 Jan 2010 13:31:19 -0800
+
feedingit (0.2.3-2) unstable; urgency=low
* Fixed missing python-osso dependency
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, python-gtkhtml2, python, python-hildon, libgtkhtml2-0, python-dbus, python-osso
Description: Simple RSS Reader
- Simple RSS Reader, with portrait mode support, and swipe gestures in articles
+ Simple RSS Reader
+ Its main features are:
+ - Portrait mode support
+ - Auto-update
+ - Swipe left or right while in articles to move to the previous or next article
+ - Import/Export of OPML files
XB-Maemo-Icon-26:
- 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
\ No newline at end of file
+ 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
+ WUsq//fg/wAnQYd0SCjJNQAAAABJRU5ErkJggg
\ No newline at end of file
import threading
import thread
from feedingitdbus import ServerObject
+from config import Config
from rss import *
from opml import GetOpmlData, ExportOpmlData
+import socket
+timeout = 5
+socket.setdefaulttimeout(timeout)
+
+CONFIGDIR="/home/user/.feedingit/"
+
class AddWidgetWizard(hildon.WizardDialog):
def __init__(self, parent, urlIn):
return True
class GetImage(threading.Thread):
- def __init__(self, url):
+ def __init__(self, url, stream):
threading.Thread.__init__(self)
self.url = url
+ self.stream = stream
def run(self):
f = urllib2.urlopen(self.url)
- self.data = f.read()
+ data = f.read()
f.close()
+ self.stream.write(data)
+ self.stream.close()
+class ImageDownloader():
+ def __init__(self):
+ self.images = []
+ self.downloading = False
+
+ def queueImage(self, url, stream):
+ self.images.append((url, stream))
+ if not self.downloading:
+ self.downloading = True
+ gobject.timeout_add(50, self.checkQueue)
+
+ def checkQueue(self):
+ for i in range(4-threading.activeCount()):
+ if len(self.images) > 0:
+ (url, stream) = self.images.pop()
+ GetImage(url, stream).start()
+ if len(self.images)>0:
+ gobject.timeout_add(200, self.checkQueue)
+ else:
+ self.downloading=False
+
+ def stopAll(self):
+ self.images = []
+
class Download(threading.Thread):
- def __init__(self, listing, key):
+ def __init__(self, listing, key, config):
threading.Thread.__init__(self)
self.listing = listing
self.key = key
+ self.config = config
def run (self):
- self.listing.updateFeed(self.key)
+ self.listing.updateFeed(self.key, self.config.getExpiry())
class DownloadDialog():
- def __init__(self, parent, listing, listOfKeys):
+ def __init__(self, parent, listing, listOfKeys, config):
self.listOfKeys = listOfKeys[:]
self.listing = listing
self.total = len(self.listOfKeys)
- self.current = 0
+ self.config = config
+ self.current = 0
if self.total>0:
self.progress = gtk.ProgressBar()
self.listOfKeys = []
while threading.activeCount() > 1:
# Wait for current downloads to finish
- time.sleep(0.5)
+ time.sleep(0.1)
self.waitingWindow.destroy()
def update_progress_bar(self):
if len(self.listOfKeys)>0:
self.current = self.current+1
key = self.listOfKeys.pop()
- download = Download(self.listing, key)
+ download = Download(self.listing, key, self.config)
download.start()
return True
elif threading.activeCount() > 1:
class DisplayArticle(hildon.StackableWindow):
- def __init__(self, title, text, index):
+ def __init__(self, title, text, link, index, key, listing):
hildon.StackableWindow.__init__(self)
+ self.imageDownloader = ImageDownloader()
+ self.listing=listing
+ self.key = key
self.index = index
self.text = text
+ self.link = link
self.set_title(title)
self.images = []
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
button.set_label("Allow Horizontal Scrolling")
button.connect("clicked", self.horiz_scrolling_button)
+ menu.append(button)
+ button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ button.set_label("Open in Browser")
+ button.connect("clicked", self._signal_link_clicked, self.link)
+ menu.append(button)
+
+ button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
+ button.set_label("Add to Archived Articles")
+ button.connect("clicked", self.archive_button)
menu.append(button)
+
self.set_app_menu(menu)
menu.show_all()
self.add(self.pannable_article)
- self.show_all()
+ self.pannable_article.show_all()
self.destroyId = self.connect("destroy", self.destroyWindow)
- self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
+ #self.timeout_handler_id = gobject.timeout_add(300, self.reloadArticle)
def gesture(self, widget, direction, startx, starty):
if (direction == 3):
self.emit("article-next", self.index)
if (direction == 2):
self.emit("article-previous", self.index)
- self.timeout_handler_id = gobject.timeout_add(200, self.destroyWindow)
+ #self.timeout_handler_id = gobject.timeout_add(200, self.destroyWindow)
def destroyWindow(self, *args):
+ self.disconnect(self.destroyId)
self.emit("article-closed", self.index)
+ self.imageDownloader.stopAll()
self.destroy()
def horiz_scrolling_button(self, *widget):
self.pannable_article.disconnect(self.gestureId)
self.pannable_article.set_property("mov-mode", hildon.MOVEMENT_MODE_BOTH)
- def reloadArticle(self, *widget):
- if threading.activeCount() > 1:
+ def archive_button(self, *widget):
+ # Call the listing.addArchivedArticle
+ self.listing.addArchivedArticle(self.key, self.index)
+
+ #def reloadArticle(self, *widget):
+ # if threading.activeCount() > 1:
# Image thread are still running, come back in a bit
- return True
- else:
- for (stream, imageThread) in self.images:
- imageThread.join()
- stream.write(imageThread.data)
- stream.close()
- return False
- self.show_all()
+ # return True
+ # else:
+ # for (stream, imageThread) in self.images:
+ # imageThread.join()
+ # stream.write(imageThread.data)
+ # stream.close()
+ # return False
+ # self.show_all()
def _signal_link_clicked(self, object, link):
bus = dbus.SystemBus()
iface.open_new_window(link)
def _signal_request_url(self, object, url, stream):
- imageThread = GetImage(url)
- imageThread.start()
- self.images.append((stream, imageThread))
+ #print url
+ self.imageDownloader.queueImage(url, stream)
+ #imageThread = GetImage(url)
+ #imageThread.start()
+ #self.images.append((stream, imageThread))
class DisplayFeed(hildon.StackableWindow):
- def __init__(self, listing, feed, title, key):
+ def __init__(self, listing, feed, title, key, config):
hildon.StackableWindow.__init__(self)
self.listing = listing
self.feed = feed
self.feedTitle = title
self.set_title(title)
self.key=key
+ self.config = config
+
+ self.disp = False
menu = hildon.AppMenu()
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
button.set_label("Update Feed")
button.connect("clicked", self.button_update_clicked)
menu.append(button)
+
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
button.set_label("Mark All As Read")
button.connect("clicked", self.buttonReadAllClicked)
def destroyWindow(self, *args):
self.emit("feed-closed", self.key)
self.destroy()
- self.feed.saveFeed()
+ self.feed.saveFeed(CONFIGDIR)
def displayFeed(self):
self.vboxFeed = gtk.VBox(False, 10)
label = button.child
if self.feed.isEntryRead(index):
#label.modify_font(pango.FontDescription("sans 16"))
- label.modify_font(pango.FontDescription(self.listing.getReadFont()))
+ label.modify_font(pango.FontDescription(self.config.getReadFont()))
else:
#print self.listing.getFont() + " bold"
- label.modify_font(pango.FontDescription(self.listing.getUnreadFont()))
+ label.modify_font(pango.FontDescription(self.config.getUnreadFont()))
#label.modify_font(pango.FontDescription("sans bold 23"))
#"sans bold 16"
label.set_line_wrap(True)
def clear(self):
self.remove(self.pannableFeed)
-
- def button_clicked(self, button, index):
- self.disp = DisplayArticle(self.feedTitle, self.feed.getArticle(index), index)
+
+ def button_clicked(self, button, index, previous=False):
+ newDisp = DisplayArticle(self.feedTitle, self.feed.getArticle(index), self.feed.getLink(index), index, self.key, self.listing)
self.ids = []
- self.ids.append(self.disp.connect("article-closed", self.onArticleClosed))
- self.ids.append(self.disp.connect("article-next", self.nextArticle))
- self.ids.append(self.disp.connect("article-previous", self.previousArticle))
+ self.ids.append(newDisp.connect("article-closed", self.onArticleClosed))
+ self.ids.append(newDisp.connect("article-next", self.nextArticle))
+ self.ids.append(newDisp.connect("article-previous", self.previousArticle))
+ stack = hildon.WindowStack.get_default()
+ if previous:
+ tmp = stack.pop(1)
+ stack.push(newDisp)
+ del tmp
+ newDisp.show_all()
+ #stack.push(tmp)
+ #if not self.disp == False:
+ # self.disp.destroyWindow()
+ else:
+ if not self.disp == False:
+ #stack.pop(1)
+ stack.pop_and_push(1,newDisp)
+ else:
+ stack.push(newDisp)
+ #newDisp.show_all()
+ #if not self.disp == False:
+ #self.disp.destroyWindow()
+ self.disp = newDisp
def nextArticle(self, object, index):
label = self.buttons[index].child
- label.modify_font(pango.FontDescription(self.listing.getReadFont()))
+ label.modify_font(pango.FontDescription(self.config.getReadFont()))
index = (index+1) % self.feed.getNumberOfEntries()
self.button_clicked(object, index)
def previousArticle(self, object, index):
label = self.buttons[index].child
- label.modify_font(pango.FontDescription(self.listing.getReadFont()))
+ label.modify_font(pango.FontDescription(self.config.getReadFont()))
index = (index-1) % self.feed.getNumberOfEntries()
- self.button_clicked(object, index)
+ self.button_clicked(object, index, True)
def onArticleClosed(self, object, index):
label = self.buttons[index].child
- label.modify_font(pango.FontDescription(self.listing.getReadFont()))
+ label.modify_font(pango.FontDescription(self.config.getReadFont()))
self.buttons[index].show()
def button_update_clicked(self, button):
- disp = DownloadDialog(self, self.listing, [self.key,] )
+ disp = DownloadDialog(self, self.listing, [self.key,], self.config )
#self.feed.updateFeed()
self.clear()
self.displayFeed()
for index in range(self.feed.getNumberOfEntries()):
self.feed.setEntryRead(index)
label = self.buttons[index].child
- label.modify_font(pango.FontDescription(self.listing.getReadFont()))
+ label.modify_font(pango.FontDescription(self.config.getReadFont()))
self.buttons[index].show()
class FeedingIt:
def __init__(self):
- self.listing = Listing()
+ self.listing = Listing(CONFIGDIR)
# Init the windows
self.window = hildon.StackableWindow()
+ self.config = Config(self.window, CONFIGDIR+"config.ini")
self.window.set_title("FeedingIt")
FremantleRotation("FeedingIt", main_window=self.window)
menu = hildon.AppMenu()
button.set_label("Update All Feeds")
button.connect("clicked", self.button_update_clicked, "All")
menu.append(button)
+
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
button.set_label("Add Feed")
button.connect("clicked", self.button_add_clicked)
menu.append(button)
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
- button.set_label("Listing Font Size")
- button.connect("clicked", self.button_font_clicked)
+ button.set_label("Preferences")
+ button.connect("clicked", self.button_preferences_clicked)
menu.append(button)
button = hildon.GtkButton(gtk.HILDON_SIZE_AUTO)
self.feedWindow = hildon.StackableWindow()
self.articleWindow = hildon.StackableWindow()
- self.displayListing()
+ self.displayListing()
+ self.autoupdate = False
+ self.checkAutoUpdate()
+
def button_export_clicked(self, button):
opml = ExportOpmlData(self.window, self.listing)
self.displayListing()
def button_update_clicked(self, button, key):
- disp = DownloadDialog(self.window, self.listing, self.listing.getListOfFeeds() )
+ disp = DownloadDialog(self.window, self.listing, self.listing.getListOfFeeds(), self.config )
self.displayListing()
- def button_font_clicked(self, button):
- self.pickerDialog = hildon.PickerDialog(self.window)
- #HildonPickerDialog
- selector = self.create_selector()
- self.pickerDialog.set_selector(selector)
- self.pickerDialog.show_all()
-
- def create_selector(self):
- selector = hildon.TouchSelector(text=True)
- # Selection multiple
- #selector.set_column_selection_mode(hildon.TOUCH_SELECTOR_SELECTION_MODE_MULTIPLE)
- self.mapping = {}
-
- current_size = self.listing.getFontSize()
- index = 0
- for size in range(12,24):
- iter = selector.append_text(str(size))
- if str(size) == current_size:
- selector.set_active(0, index)
- index += 1
- selector.connect("changed", self.selection_changed)
- return selector
-
- def selection_changed(self, widget, data):
- current_selection = widget.get_current_text()
- if current_selection:
- self.listing.setFont(current_selection)
- self.pickerDialog.destroy()
+ def button_preferences_clicked(self, button):
+ dialog = self.config.createDialog()
+ dialog.connect("destroy", self.checkAutoUpdate)
def show_confirmation_note(self, parent, title):
note = hildon.Note("confirmation", parent, "Are you sure you want to delete " + title +"?")
self.window.show_all()
def buttonFeedClicked(widget, button, self, window, key):
- disp = DisplayFeed(self.listing, self.listing.getFeed(key), self.listing.getFeedTitle(key), key)
+ disp = DisplayFeed(self.listing, self.listing.getFeed(key), self.listing.getFeedTitle(key), key, self.config)
disp.connect("feed-closed", self.onFeedClosed)
def onFeedClosed(self, object, key):
- self.buttons[key].set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
- + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
- self.buttons[key].show()
+ self.displayListing()
+ #self.buttons[key].set_text(self.listing.getFeedTitle(key), self.listing.getFeedUpdateTime(key) + " / "
+ # + str(self.listing.getFeedNumberOfUnreadItems(key)) + " Unread Items")
+ #self.buttons[key].show()
def run(self):
self.window.connect("destroy", gtk.main_quit)
gtk.main()
self.listing.saveConfig()
+ def checkAutoUpdate(self, *widget):
+ if self.config.isAutoUpdateEnabled():
+ if not self.autoupdate:
+ self.autoupdateId = gobject.timeout_add(int(self.config.getUpdateInterval()*3600000), self.automaticUpdate)
+ self.autoupdate = True
+ else:
+ if self.autoupdate:
+ gobject.source_remove(self.autoupdateId)
+ self.autoupdate = False
+
+ def automaticUpdate(self, *widget):
+ # Need to check for internet connection
+ # If no internet connection, try again in 10 minutes:
+ # gobject.timeout_add(int(5*3600000), self.automaticUpdate)
+ self.button_update_clicked(None, None)
+ return True
if __name__ == "__main__":
gobject.signal_new("feed-closed", DisplayFeed, gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))
--- /dev/null
+#!/usr/bin/env python2.5
+
+#
+# Copyright (c) 2007-2008 INdT.
+# 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
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# ============================================================================
+# Name : FeedingIt.py
+# Author : Yves Marcoz
+# Version : 0.2.2
+# Description : Simple RSS Reader
+# ============================================================================
+
+import gtk
+import hildon
+import ConfigParser
+import gobject
+
+section = "FeedingIt"
+ranges = { "updateInterval":[0.02, 0.5, 1, 2, 4, 12, 24], "expiry":[24, 48, 72], "fontSize":range(12,24) }
+titles = {"updateInterval":"Auto-update Interval", "expiry":"Expiry For Articles", "fontSize":"Font Size For Article Listing"}
+subtitles = {"updateInterval":"Update every %s hours", "expiry":"Delete articles after %s hours", "fontSize":"%s pixels"}
+
+class Config():
+ def __init__(self, parent, configFilename):
+ self.configFilename = configFilename
+ self.parent = parent
+ # Load config
+ self.loadConfig()
+
+ def createDialog(self):
+
+ self.window = gtk.Dialog("Preferences", self.parent)
+ #self.vbox = gtk.VBox(False, 10)
+ self.buttons = {}
+ for setting in ["fontSize", "expiry", "updateInterval"]:
+ picker = hildon.PickerButton(gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL)
+ selector = self.create_selector(ranges[setting], setting)
+ picker.set_selector(selector)
+ picker.set_title(titles[setting])
+ picker.set_text(titles[setting], subtitles[setting] % self.config[setting])
+ picker.set_name('HildonButton-finger')
+ picker.set_alignment(0,0,1,1)
+ self.buttons[setting] = picker
+ self.window.vbox.pack_start(picker)
+
+ button = hildon.CheckButton(gtk.HILDON_SIZE_FINGER_HEIGHT)
+ button.set_label("Auto-update Enabled")
+ button.set_active(self.config["autoupdate"])
+ button.connect("toggled", self.button_toggled)
+
+ self.window.vbox.pack_start(button)
+
+ self.window.connect("destroy", self.onExit)
+ #self.window.add(self.vbox)
+ self.window.show_all()
+ return self.window
+
+ def onExit(self, *widget):
+ self.saveConfig()
+ self.window.destroy()
+
+ def button_toggled(self, widget):
+ #print "widget", widget.get_active()
+ if (widget.get_active()):
+ self.config["autoupdate"] = True
+ else:
+ self.config["autoupdate"] = False
+ #print "autoup", self.autoupdate
+ self.saveConfig()
+
+ def selection_changed(self, selector, button, setting):
+ current_selection = selector.get_current_text()
+ if current_selection:
+ self.config[setting] = current_selection
+ gobject.idle_add(self.updateButton, setting)
+ self.saveConfig()
+
+ def updateButton(self, setting):
+ self.buttons[setting].set_text(titles[setting], subtitles[setting] % self.config[setting])
+
+ def loadConfig(self):
+ self.config = {}
+ try:
+ configParser = ConfigParser.RawConfigParser()
+ configParser.read(self.configFilename)
+ self.config["fontSize"] = configParser.getint(section, "fontSize")
+ self.config["expiry"] = configParser.getint(section, "expiry")
+ self.config["autoupdate"] = configParser.getboolean(section, "autoupdate")
+ self.config["updateInterval"] = configParser.getfloat(section, "updateInterval")
+ except:
+ self.config["fontSize"] = 16
+ self.config["expiry"] = 24
+ self.config["autoupdate"] = False
+ self.config["updateInterval"] = 4
+
+ def saveConfig(self):
+ configParser = ConfigParser.RawConfigParser()
+ configParser.add_section(section)
+ configParser.set(section, 'fontSize', str(self.config["fontSize"]))
+ configParser.set(section, 'expiry', str(self.config["expiry"]))
+ configParser.set(section, 'autoupdate', str(self.config["autoupdate"]))
+ configParser.set(section, 'updateInterval', str(self.config["updateInterval"]))
+
+ # Writing our configuration file
+ file = open(self.configFilename, 'wb')
+ configParser.write(file)
+ file.close()
+
+ def create_selector(self, choices, setting):
+ #self.pickerDialog = hildon.PickerDialog(self.parent)
+ selector = hildon.TouchSelector(text=True)
+ index = 0
+ for item in choices:
+ iter = selector.append_text(str(item))
+ if str(self.config[setting]) == str(item):
+ selector.set_active(0, index)
+ index += 1
+ selector.connect("changed", self.selection_changed, setting)
+ #self.pickerDialog.set_selector(selector)
+ return selector
+ #self.pickerDialog.show_all()
+
+ def getFontSize(self):
+ return self.config["fontSize"]
+ def getExpiry(self):
+ return self.config["expiry"]
+ def isAutoUpdateEnabled(self):
+ return self.config["autoupdate"]
+ def getUpdateInterval(self):
+ return float(self.config["updateInterval"])
+ def getReadFont(self):
+ return "sans %s" % self.config["fontSize"]
+ def getUnreadFont(self):
+ return "sans bold %s" % self.config["fontSize"]
\ No newline at end of file
for key in listing.getListOfFeeds():
title = listing.getFeedTitle(key)
url = listing.getFeedUrl(key)
- opml_text += """\n\t\t<outline type="rss" text="%s" title="%s" xmlUrl="%s"/>""" % (title, title, url)
+ if not title == "Archived Articles":
+ opml_text += """\n\t\t<outline type="rss" text="%s" title="%s" xmlUrl="%s"/>""" % (title, title, url)
opml_text += """\n</body>\n</opml>\n"""
return opml_text
import md5
import feedparser
import time
+import urllib2
-CONFIGDIR="/home/user/.feedingit/"
+#CONFIGDIR="/home/user/.feedingit/"
def getId(string):
return md5.new(string).hexdigest()
# Contains all the info about a single feed (articles, ...), and expose the data
def __init__(self, name, url):
self.entries = []
- self.fontSize = "+0"
self.readItems = {}
self.countUnread = 0
self.name = name
self.url = url
self.updateTime = "Never"
- def getFontSize(self):
- try:
- return self.fontSize
- except:
- self.fontSize = "+0"
- return self.fontSize
-
- def saveFeed(self):
- file = open(CONFIGDIR+getId(self.name), "w")
+ def saveFeed(self, configdir):
+ file = open(configdir+getId(self.name), "w")
pickle.dump(self, file )
file.close()
- def updateFeed(self):
+ def updateFeed(self, configdir, expiryTime=24):
+ # Expiry time is in hours
tmp=feedparser.parse(self.url)
# Check if the parse was succesful (number of entries > 0, else do nothing)
if len(tmp["entries"])>0:
- self.tmpReadItems = self.readItems
- self.readItems = {}
- self.updateTime = time.asctime()
+ #reversedEntries = self.getEntries()
+ #reversedEntries.reverse()
+ tmpIds = []
+ for entry in tmp["entries"]:
+ tmpIds.append(self.getUniqueId(-1, entry))
+ for entry in self.getEntries():
+ currentTime = time.time()
+ expiry = float(expiryTime) * 3600.
+ if entry.has_key("updated_parsed"):
+ articleTime = time.mktime(entry["updated_parsed"])
+ if currentTime - articleTime < expiry:
+ id = self.getUniqueId(-1, entry)
+ if not id in tmpIds:
+ tmp["entries"].append(entry)
+
self.entries = tmp["entries"]
self.countUnread = 0
# Initialize the new articles to unread
+ tmpReadItems = self.readItems
+ self.readItems = {}
for index in range(self.getNumberOfEntries()):
- if not self.tmpReadItems.has_key(self.getUniqueId(index)):
+ if not tmpReadItems.has_key(self.getUniqueId(index)):
self.readItems[self.getUniqueId(index)] = False
else:
- self.readItems[self.getUniqueId(index)] = self.tmpReadItems[self.getUniqueId(index)]
+ self.readItems[self.getUniqueId(index)] = tmpReadItems[self.getUniqueId(index)]
if self.readItems[self.getUniqueId(index)]==False:
self.countUnread = self.countUnread + 1
del tmp
- self.saveFeed()
+ self.updateTime = time.asctime()
+ self.saveFeed(configdir)
def setEntryRead(self, index):
if self.readItems[self.getUniqueId(index)]==False:
def getTitle(self, index):
return self.entries[index]["title"]
- def getUniqueId(self,index):
- entry = self.entries[index]
+ def getLink(self, index):
+ return self.entries[index]["link"]
+
+ def getDate(self, index):
+ try:
+ return self.entries[index]["updated_parsed"]
+ except:
+ return time.localtime()
+
+ def getUniqueId(self, index, entry=None):
+ if index >=0:
+ entry = self.entries[index]
if entry.has_key("updated_parsed"):
return getId(time.strftime("%a, %d %b %Y %H:%M:%S",entry["updated_parsed"]) + entry["title"])
elif entry.has_key("link"):
text += content
return text
+class ArchivedArticles(Feed):
+ def addArchivedArticle(self, title, link, updated_parsed, configdir):
+ entry = {}
+ entry["title"] = title
+ entry["link"] = link
+ entry["downloaded"] = False
+ entry["summary"] = '<a href=\"' + link + '\">' + title + "</a>"
+ entry["updated_parsed"] = updated_parsed
+ self.entries.append(entry)
+ self.readItems[self.getUniqueId(len(self.entries)-1)] = False
+ self.countUnread = self.countUnread + 1
+ self.saveFeed(configdir)
+ #print entry
+
+ def updateFeed(self, configdir, expiryTime=24):
+ for entry in self.getEntries():
+ if not entry["downloaded"]:
+ try:
+ f = urllib2.urlopen(entry["link"])
+ entry["summary"] = f.read()
+ f.close()
+ entry["downloaded"] = True
+ entry["time"] = time.time()
+ except:
+ pass
+ currentTime = time.time()
+ expiry = float(expiryTime) * 3600
+ if currentTime - entry["time"] > expiry:
+ self.entries.remove(entry)
+ self.saveFeed(configdir)
+
+ def getArticle(self, index):
+ self.setEntryRead(index)
+ content = self.getContent(index)
+ return content
+
class Listing:
# Lists all the feeds in a dictionary, and expose the data
- def __init__(self):
+ def __init__(self, configdir):
+ self.configdir = configdir
self.feeds = {}
- if isfile(CONFIGDIR+"feeds.pickle"):
- file = open(CONFIGDIR+"feeds.pickle")
+ if isfile(self.configdir+"feeds.pickle"):
+ file = open(self.configdir+"feeds.pickle")
self.listOfFeeds = pickle.load(file)
file.close()
else:
self.listOfFeeds = {getId("Slashdot"):{"title":"Slashdot", "url":"http://rss.slashdot.org/Slashdot/slashdot"}, }
- if not self.listOfFeeds.has_key("font"):
- self.setFont("16")
+ if self.listOfFeeds.has_key("font"):
+ del self.listOfFeeds["font"]
if self.listOfFeeds.has_key("feedingit-order"):
self.sortedKeys = self.listOfFeeds["feedingit-order"]
else:
except:
self.sortedKeys.remove(key)
#self.saveConfig()
-
- def getFontSize(self):
- return self.listOfFeeds["font"]
-
- def getReadFont(self):
- return "sans %s" % self.listOfFeeds["font"]
-
- def getUnreadFont(self):
- return "sans bold %s" % self.listOfFeeds["font"]
-
- def setFont(self, fontname):
- self.listOfFeeds["font"] = fontname
+
+ def addArchivedArticle(self, key, index):
+ title = self.getFeed(key).getTitle(index)
+ link = self.getFeed(key).getLink(index)
+ date = self.getFeed(key).getDate(index)
+ if not self.listOfFeeds.has_key(getId("Archived Articles")):
+ self.listOfFeeds[getId("Archived Articles")] = {"title":"Archived Articles", "url":""}
+ self.sortedKeys.append(getId("Archived Articles"))
+ self.feeds[getId("Archived Articles")] = ArchivedArticles("Archived Articles", "")
+ self.saveConfig()
+
+ self.getFeed(getId("Archived Articles")).addArchivedArticle(title, link, date, self.configdir)
def loadFeed(self, key):
- if isfile(CONFIGDIR+key):
- file = open(CONFIGDIR+key)
+ if isfile(self.configdir+key):
+ file = open(self.configdir+key)
self.feeds[key] = pickle.load(file)
file.close()
else:
url = self.listOfFeeds[key]["url"]
self.feeds[key] = Feed(title, url)
- def updateFeeds(self):
+ def updateFeeds(self, expiryTime=24):
for key in self.getListOfFeeds():
- self.feeds[key].updateFeed()
+ self.feeds[key].updateFeed(self.configdir, expiryTime)
- def updateFeed(self, key):
- self.feeds[key].updateFeed()
+ def updateFeed(self, key, expiryTime=24):
+ self.feeds[key].updateFeed(self.configdir, expiryTime)
def getFeed(self, key):
return self.feeds[key]
del self.listOfFeeds[key]
self.sortedKeys.remove(key)
del self.feeds[key]
- if isfile(CONFIGDIR+key):
- remove(CONFIGDIR+key)
+ if isfile(self.configdir+key):
+ remove(self.configdir+key)
def saveConfig(self):
self.listOfFeeds["feedingit-order"] = self.sortedKeys
- file = open(CONFIGDIR+"feeds.pickle", "w")
+ file = open(self.configdir+"feeds.pickle", "w")
pickle.dump(self.listOfFeeds, file)
file.close()