| |
1 #!/usr/bin/env python |
| |
2 |
| |
3 # A very simple and stupid RSS reader |
| |
4 # |
| |
5 # Uses the Universal Feed Parser |
| |
6 # |
| |
7 |
| |
8 import threading |
| |
9 import feedparser |
| |
10 import gobject |
| |
11 import sys |
| |
12 import time |
| |
13 |
| |
14 ## |
| |
15 # The FeedItem class. It will update emit 'delete' signal when it's |
| |
16 # destroyed. |
| |
17 ## |
| |
18 class FeedItem(gobject.GObject): |
| |
19 __gproperties__ = { |
| |
20 'unread' : (gobject.TYPE_BOOLEAN, 'read', |
| |
21 'The unread state of the item.', |
| |
22 False, gobject.PARAM_READWRITE) |
| |
23 } |
| |
24 __gsignals__ = { |
| |
25 'delete' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)) |
| |
26 } |
| |
27 def __init__(self, item, parent): |
| |
28 self.__gobject_init__() |
| |
29 self.date = item['date'] |
| |
30 self.date_parsed = item['date_parsed'] |
| |
31 self.title = item['title'] |
| |
32 self.summary = item['summary'] |
| |
33 self.link = item['link'] |
| |
34 self.parent = parent |
| |
35 self.unread = True |
| |
36 |
| |
37 def remove(self): |
| |
38 self.emit('delete', self.parent) |
| |
39 |
| |
40 def do_set_property(self, property, value): |
| |
41 if property.name == 'unread': |
| |
42 self.unread = value |
| |
43 |
| |
44 gobject.type_register(FeedItem) |
| |
45 |
| |
46 def item_hash(item): |
| |
47 return str(item['date'] + item['title']) |
| |
48 |
| |
49 ## |
| |
50 # The Feed class. It will update the 'link', 'title', 'desc' and 'items' |
| |
51 # attributes if/when they are updated (triggering 'notify::<attr>' signal) |
| |
52 ## |
| |
53 class Feed(gobject.GObject): |
| |
54 __gproperties__ = { |
| |
55 'link' : (gobject.TYPE_STRING, 'link', |
| |
56 'The web page this feed is associated with.', |
| |
57 '...', gobject.PARAM_READWRITE), |
| |
58 'title' : (gobject.TYPE_STRING, 'title', |
| |
59 'The title of the feed.', |
| |
60 '...', gobject.PARAM_READWRITE), |
| |
61 'desc' : (gobject.TYPE_STRING, 'description', |
| |
62 'The description for the feed.', |
| |
63 '...', gobject.PARAM_READWRITE), |
| |
64 'items' : (gobject.TYPE_POINTER, 'items', |
| |
65 'The items in the feed.', gobject.PARAM_READWRITE), |
| |
66 'unread' : (gobject.TYPE_INT, 'unread', |
| |
67 'Number of unread items in the feed.', 0, 10000, 0, gobject.PARAM_READWRITE) |
| |
68 } |
| |
69 __gsignals__ = { |
| |
70 'added' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)) |
| |
71 } |
| |
72 |
| |
73 def __init__(self, url): |
| |
74 self.__gobject_init__() |
| |
75 self.url = url # The url of the feed itself |
| |
76 self.link = url # The web page associated with the feed |
| |
77 self.desc = url |
| |
78 self.title = url |
| |
79 self.unread = 0 |
| |
80 self.timer = 0 |
| |
81 self.items = [] |
| |
82 self.hash = {} |
| |
83 |
| |
84 def do_set_property(self, property, value): |
| |
85 if property.name == 'link': |
| |
86 self.link = value |
| |
87 elif property.name == 'desc': |
| |
88 self.desc = value |
| |
89 elif property.name == 'title': |
| |
90 self.title = value |
| |
91 elif property.name == 'unread': |
| |
92 self.unread = value |
| |
93 pass |
| |
94 |
| |
95 def check_thread_for_death(self): |
| |
96 #if self.thread.running: |
| |
97 # sys.stderr.write(time.ctime() + "continue") |
| |
98 # return True |
| |
99 # The thread has ended!! |
| |
100 #result = self.thread.result |
| |
101 #self.thread = None |
| |
102 result = feedparser.parse(self.url) |
| |
103 channel = result['channel'] |
| |
104 self.set_property('link', channel['link']) |
| |
105 self.set_property('desc', channel['description']) |
| |
106 self.set_property('title', channel['title']) |
| |
107 self.set_property('unread', len(result['items'])) |
| |
108 self.timer = 0 |
| |
109 items = result['items'] |
| |
110 tmp = {} |
| |
111 for item in self.items: |
| |
112 tmp[hash(item)] = item |
| |
113 |
| |
114 for item in items: |
| |
115 try: |
| |
116 exist = self.hash[item_hash(item)] |
| |
117 del tmp[hash(exist)] |
| |
118 except: |
| |
119 itm = FeedItem(item, self) |
| |
120 self.items.append(itm) |
| |
121 self.emit('added', itm) |
| |
122 self.hash[item_hash(item)] = itm |
| |
123 for hv in tmp: |
| |
124 tmp[hv].remove() |
| |
125 |
| |
126 return False |
| |
127 |
| |
128 def refresh(self): |
| |
129 #if self.thread == 0: |
| |
130 # self.thread = FeedReader(self) |
| |
131 # self.thread.start() |
| |
132 if self.timer == 0: |
| |
133 self.timer = gobject.timeout_add(1000, self.check_thread_for_death) |
| |
134 |
| |
135 gobject.type_register(Feed) |
| |
136 |
| |
137 ## |
| |
138 # A FeedReader class, which is threaded to make sure it doesn't freeze the ui |
| |
139 # (this thing doesn't quite work ... yet) |
| |
140 ## |
| |
141 class FeedReader(threading.Thread): |
| |
142 def __init__(self, feed): |
| |
143 self.feed = feed |
| |
144 self.running = True |
| |
145 threading.Thread.__init__(self) |
| |
146 |
| |
147 def run(self): |
| |
148 sys.stderr.write(str(time.ctime()) + " STARTED!!!\n\n\n") |
| |
149 self.running = True |
| |
150 self.result = feedparser.parse(self.feed.url) |
| |
151 self.running = False |
| |
152 sys.stderr.write(str(time.ctime()) + " DONE!!!\n\n\n") |
| |
153 |
| |
154 feeds = [] |
| |
155 urls = ("http://rss.slashdot.org/Slashdot/slashdot", |
| |
156 "http://www.python.org/channews.rdf", |
| |
157 "http://pidgin.im/rss.php", |
| |
158 "./rss.php" |
| |
159 ) |
| |
160 |
| |
161 for url in urls: |
| |
162 feed = Feed(url) |
| |
163 feeds.append(feed) |
| |
164 |