updated image references and straw url
[straw.git] / src / lib / ImageCache.py
blobc02220b1e7e4c9a0530aa613640432ed9f5b3230
1 """ ImageCache.py
3 Module for handling images
4 """
5 __copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
6 __license__ = """
7 Straw is free software; you can redistribute it and/or modify it under the
8 terms of the GNU General Public License as published by the Free Software
9 Foundation; either version 2 of the License, or (at your option) any later
10 version.
12 Straw is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License along with
17 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 Place - Suite 330, Boston, MA 02111-1307, USA. """
20 import NetworkConstants
21 import Event
22 import URLFetch
23 import PollManager
24 import ItemStore
25 import utils
26 import error
27 import Config
29 STATUS_IMAGE_WAITING = None
30 STATUS_IMAGE_BROKEN = None
32 class CacheEntry(object):
33 def __init__(self, image, count, pollstopper = None, restore = False):
34 self._image = image
35 self._count = count
36 self._pollstopper = pollstopper
37 if not restore:
38 self._save_count()
40 @apply
41 def image():
42 doc="The image object that is associated with this class"
43 def fget(self):
44 return self._image
45 def fset(self, image):
46 self._image = image
47 return property(**locals())
49 @apply
50 def count():
51 doc="The number of instances of this image"
52 def fget(self):
53 return self._count
54 def fset(self, count):
55 self._count = count
56 self._save_count()
57 return property(**locals())
59 @apply
60 def pollstopper():
61 doc="the stopper object for this cache entry"
62 def fget(self):
63 return self._pollstopper
64 def fset(self, pollstopper):
65 self._pollstopper = pollstopper
66 if pollstopper:
67 pollstopper.signal_connect(Event.PollingStoppedSignal,
68 self._polling_stopped)
69 return property(**locals())
71 def incr_count(self): self._count += 1
72 def decr_count(self): self._count -= 1
74 def _polling_stopped(self):
75 self._pollstopper = None
77 def _save_count(self):
78 ItemStore.get_instance().set_image_count(self._image.url, self._count)
81 class Cache(Event.SignalEmitter):
82 """Image cache with explicit reference counting."""
83 def __init__(self):
84 Event.SignalEmitter.__init__(self)
85 self.initialize_slots(Event.ImageUpdatedSignal)
86 # self.__cache contains items of the form [Image, refcount]
87 self.__cache = dict()
89 def __getitem__(self, key):
90 return self.__cache[key].image
92 def add_refer(self, key, restore = False, item = None):
93 if key not in self.__cache:
94 if not restore:
95 # fetch image
96 image = Image(key, Image.WAITING)
97 ic = ImageConsumer(image)
98 headers = {}
99 stopper = None
100 if item and item.feed:
101 headers['Referer'] = item.feed.location
102 try:
103 stopper = URLFetch.get_instance().request(
104 key, ic,
105 priority=NetworkConstants.PRIORITY_IMAGE,
106 headers=headers)
107 except URLFetch.RequestSchemeException, e:
108 ic.http_failed(e)
109 except Exception, e:
110 error.logtb("ImageCache.add_refer: ", str(e))
111 ic.http_failed(e)
112 self.__cache[key] = CacheEntry(
113 image, 1, PollManager.PollStopper(stopper, image), restore=restore)
114 else:
115 image = Image(key, Image.DATA_IN_DB)
116 self.__cache[key] = CacheEntry(image, 1, pollstopper=None, restore=restore)
117 elif key in self.__cache and not restore:
118 self.__cache[key].incr_count()
120 def remove_refer(self, key):
121 if self.__cache.has_key(key):
122 entry = self.__cache[key]
123 entry.decr_count()
124 if entry.count == 0:
125 del self.__cache[key]
126 self.emit_signal(Event.ImageUpdatedSignal(self, key, None))
128 def image_updated(self, url, data):
129 self.__cache[url].pollstopper = None
130 self.emit_signal(Event.ImageUpdatedSignal(self, url, data))
132 def set_image(self, url, image):
133 if self.__cache.has_key(url):
134 self.__cache[url].incr_count()
135 else:
136 self.__cache[url] = CacheEntry(image, 1)
138 def set_image_with_count(self, url, image, count, restore=False):
139 self.__cache[url] = CacheEntry(image, count, restore=restore)
141 def stop_transfer(self, url):
142 image = self.__cache.get(url, None)
143 if image and image.pollstopper:
144 self.__cache[url].pollstopper.stop()
145 self.__cache[url].pollstopper = None
147 class Image:
148 WAITING = 1
149 DATA_IN_DB = 2
150 FAILED = 3
152 def __init__(self, url, status = DATA_IN_DB):
153 self.url = url
154 self.status = status
156 def get_data(self):
157 if self.status == self.WAITING:
158 return STATUS_IMAGE_WAITING
159 elif self.status == self.DATA_IN_DB:
160 data = self.read_data()
161 if data is None:
162 self.status = self.FAILED
163 return STATUS_IMAGE_BROKEN
164 return data
165 else:
166 return STATUS_IMAGE_BROKEN
168 def set_data(self, data):
169 cache.image_updated(self.url, data)
170 self.status = self.DATA_IN_DB
172 def set_failed(self):
173 self.status = self.FAILED
175 def read_data(self):
176 istore = ItemStore.get_instance()
177 return istore.read_image(self.url)
179 def _return_id(self):
180 return "%d %s" % (id(self), self.url)
182 cache = Cache()
184 class ImageConsumer:
185 def __init__(self, imobj):
186 self._imobj = imobj
188 def http_results(self, status, headers, data):
189 if status[1] == 200:
190 self._imobj.set_data(data)
191 else:
192 self._imobj.set_failed()
194 def http_failed(self, exception):
195 self._imobj.set_failed()
197 def operation_stopped(self):
198 self._imobj.set_failed()
200 def initialize():
201 global STATUS_IMAGE_WAITING
202 global STATUS_IMAGE_BROKEN
203 libdir = utils.find_image_dir()
204 STATUS_IMAGE_WAITING = open(
205 libdir + "/image-loading.svg").read()
206 STATUS_IMAGE_BROKEN = open(
207 libdir + "/image-missing.svg").read()