2 from xml.etree.cElementTree import parse as cet_parse
3 from os import path as os_path
4 from AutoTimerConfiguration import parseConfig, writeConfig
6 # Navigation (RecordTimer)
7 import NavigationInstance
10 from ServiceReference import ServiceReference
11 from RecordTimer import RecordTimerEntry
12 from Components.TimerSanityCheck import TimerSanityCheck
15 from time import localtime, time
18 from enigma import eEPGCache, eServiceReference
21 from Components.config import config
24 from AutoTimerComponent import AutoTimerComponent
26 XML_CONFIG = "/etc/enigma2/autotimer.xml"
28 def getTimeDiff(timer, begin, end):
29 if begin <= timer.begin <= end:
30 return end - timer.begin
31 elif timer.begin <= begin <= timer.end:
32 return timer.end - begin
36 "exact": eEPGCache.EXAKT_TITLE_SEARCH,
37 "partial": eEPGCache.PARTIAL_TITLE_SEARCH
41 "sensitive": eEPGCache.CASE_CHECK,
42 "insensitive": eEPGCache.NO_CASE_CHECK
45 class AutoTimerIgnoreTimerException(Exception):
46 def __init__(self, cause):
50 return "[AutoTimer] " + str(self.cause)
53 return str(type(self))
56 """Read and save xml configuration, query EPGCache"""
62 self.uniqueTimerId = 0
63 self.defaultTimer = AutoTimerComponent(
73 # Abort if no config found
74 if not os_path.exists(XML_CONFIG):
75 print "[AutoTimer] No configuration file present"
78 # Parse if mtime differs from whats saved
79 mtime = os_path.getmtime(XML_CONFIG)
80 if mtime == self.configMtime:
81 print "[AutoTimer] No changes in configuration, won't parse"
85 self.configMtime = mtime
88 configuration = cet_parse(XML_CONFIG).getroot()
90 # Empty out timers and reset Ids
92 self.defaultTimer.clear(-1, True)
97 configuration.get("version"),
101 self.uniqueTimerId = len(self.timers)
104 writeConfig(XML_CONFIG, self.defaultTimer, self.timers)
108 def add(self, timer):
109 self.timers.append(timer)
111 def getEnabledTimerList(self):
112 return [x for x in self.timers if x.enabled]
114 def getTimerList(self):
117 def getTupleTimerList(self):
119 return [(x,) for x in list]
121 def getSortedTupleTimerList(self):
122 list = self.timers[:]
124 return [(x,) for x in list]
126 def getUniqueId(self):
127 self.uniqueTimerId += 1
128 return self.uniqueTimerId
130 def remove(self, uniqueId):
132 for timer in self.timers:
133 if timer.id == uniqueId:
138 def set(self, timer):
140 for stimer in self.timers:
142 self.timers[idx] = timer
145 self.timers.append(timer)
149 def parseEPG(self, simulateOnly = False):
150 if NavigationInstance.instance is None:
151 print "[AutoTimer] Navigation is not available, can't parse EPG"
161 # Save Recordings in a dict to speed things up a little
162 # We include processed timers as we might search for duplicate descriptions
164 for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
165 if not recorddict.has_key(str(timer.service_ref)):
166 recorddict[str(timer.service_ref)] = [timer]
168 recorddict[str(timer.service_ref)].append(timer)
171 for timer in self.getEnabledTimerList():
172 # Workaround to allow search for umlauts if we know the encoding
174 if timer.encoding != 'UTF-8':
176 match = match.decode('UTF-8').encode(timer.encoding)
177 except UnicodeDecodeError:
180 # Search EPG, default to empty list
181 epgcache = eEPGCache.getInstance()
182 ret = epgcache.search(('RI', 100, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or []
184 for serviceref, eit in ret:
185 eserviceref = eServiceReference(serviceref)
187 evt = epgcache.lookupEventId(eserviceref, eit)
189 print "[AutoTimer] Could not create Event!"
192 # Try to determine real service (we always choose the last one)
193 n = evt.getNumOfLinkageServices()
195 i = evt.getLinkageService(eserviceref, n-1)
196 serviceref = i.toString()
199 name = evt.getEventName()
200 description = evt.getShortDescription()
201 begin = evt.getBeginTime()
202 duration = evt.getDuration()
203 end = begin + duration
205 # If event starts in less than 60 seconds skip it
206 if begin < time() + 60:
210 timestamp = localtime(begin)
213 timer.update(begin, timestamp)
215 # Check Duration, Timespan and Excludes
216 if timer.checkServices(serviceref) \
217 or timer.checkDuration(duration) \
218 or timer.checkTimespan(timestamp) \
219 or timer.checkFilter(name, description,
220 evt.getExtendedDescription(), str(timestamp.tm_wday)):
223 if timer.hasOffset():
224 # Apply custom Offset
225 begin, end = timer.applyOffset(begin, end)
228 begin -= config.recording.margin_before.value * 60
229 end += config.recording.margin_after.value * 60
234 # Append to timerlist and abort if simulating
235 timers.append((name, begin, end, serviceref, timer.name))
243 # Check for double Timers
244 # We first check eit and if user wants us to guess event based on time
245 # we try this as backup. The allowed diff should be configurable though.
246 for rtimer in recorddict.get(serviceref, []):
247 if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8):
250 # Abort if we don't want to modify timers or timer is repeated
251 if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
252 print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer"
255 if hasattr(rtimer, "isAutoTimer"):
256 print "[AutoTimer] Modifying existing AutoTimer!"
258 if config.plugins.autotimer.refresh.value != "all":
259 print "[AutoTimer] Won't modify existing timer because it's no timer set by us"
262 print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us"
267 # Modify values saved in timer
269 newEntry.description = description
270 newEntry.begin = int(begin)
271 newEntry.end = int(end)
272 newEntry.service_ref = ServiceReference(serviceref)
275 elif timer.getAvoidDuplicateDescription() == 1 and rtimer.description == description:
277 print "[AutoTimer] We found a timer with same description, skipping event"
280 # We found no timer we want to edit
282 # But there is a match
286 # We want to search for possible doubles
287 if timer.getAvoidDuplicateDescription() == 2:
288 # I thinks thats the fastest way to do this, though it's a little ugly
290 for list in recorddict.values():
292 if rtimer.description == description:
293 raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event")
294 except AutoTimerIgnoreTimerException, etite:
298 if timer.checkCounter(timestamp):
301 print "[AutoTimer] Adding an event."
302 newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit)
304 # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
305 newEntry.isAutoTimer = True
308 if timer.hasAfterEvent():
309 afterEvent = timer.getAfterEventTimespan(localtime(end))
310 if afterEvent is None:
311 afterEvent = timer.getAfterEvent()
312 if afterEvent is not None:
313 newEntry.afterEvent = afterEvent
315 newEntry.dirname = timer.destination
316 newEntry.justplay = timer.justplay
317 newEntry.tags = timer.tags
320 # XXX: this won't perform a sanity check, but do we actually want to do so?
321 NavigationInstance.instance.RecordTimer.timeChanged(newEntry)
323 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
324 if conflicts and config.plugins.autotimer.disabled_on_conflict.value:
325 newEntry.disabled = True
326 # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
327 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
328 if conflicts is None:
329 timer.decrementCounter()
331 if recorddict.has_key(serviceref):
332 recorddict[serviceref].append(newEntry)
334 recorddict[serviceref] = [newEntry]
336 return (total, new, modified, timers)