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 recorddict.setdefault(str(timer.service_ref), []).append(timer)
168 for timer in self.getEnabledTimerList():
169 # Workaround to allow search for umlauts if we know the encoding
171 if timer.encoding != 'UTF-8':
173 match = match.decode('UTF-8').encode(timer.encoding)
174 except UnicodeDecodeError:
177 # Search EPG, default to empty list
178 epgcache = eEPGCache.getInstance()
179 ret = epgcache.search(('RI', 100, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or ()
181 for serviceref, eit in ret:
182 eserviceref = eServiceReference(serviceref)
184 evt = epgcache.lookupEventId(eserviceref, eit)
186 print "[AutoTimer] Could not create Event!"
189 # Try to determine real service (we always choose the last one)
190 n = evt.getNumOfLinkageServices()
192 i = evt.getLinkageService(eserviceref, n-1)
193 serviceref = i.toString()
196 name = evt.getEventName()
197 description = evt.getShortDescription()
198 begin = evt.getBeginTime()
199 duration = evt.getDuration()
200 end = begin + duration
202 # If event starts in less than 60 seconds skip it
203 if begin < time() + 60:
207 timestamp = localtime(begin)
210 timer.update(begin, timestamp)
212 # Check Duration, Timespan and Excludes
213 if timer.checkServices(serviceref) \
214 or timer.checkDuration(duration) \
215 or timer.checkTimespan(timestamp) \
216 or timer.checkFilter(name, description,
217 evt.getExtendedDescription(), str(timestamp.tm_wday)):
220 if timer.hasOffset():
221 # Apply custom Offset
222 begin, end = timer.applyOffset(begin, end)
225 begin -= config.recording.margin_before.value * 60
226 end += config.recording.margin_after.value * 60
230 # Append to timerlist and abort if simulating
231 timers.append((name, begin, end, serviceref, timer.name))
239 # Check for double Timers
240 # We first check eit and if user wants us to guess event based on time
241 # we try this as backup. The allowed diff should be configurable though.
242 for rtimer in recorddict.get(serviceref, ()):
243 if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8):
246 # Abort if we don't want to modify timers or timer is repeated
247 if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
248 print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer"
251 if hasattr(rtimer, "isAutoTimer"):
252 print "[AutoTimer] Modifying existing AutoTimer!"
254 if config.plugins.autotimer.refresh.value != "all":
255 print "[AutoTimer] Won't modify existing timer because it's no timer set by us"
258 print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us"
263 # Modify values saved in timer
265 newEntry.description = description
266 newEntry.begin = int(begin)
267 newEntry.end = int(end)
268 newEntry.service_ref = ServiceReference(serviceref)
271 elif timer.avoidDuplicateDescription == 1 and rtimer.description == description:
273 print "[AutoTimer] We found a timer with same description, skipping event"
276 # We found no timer we want to edit
278 # But there is a match
282 # We want to search for possible doubles
283 if timer.avoidDuplicateDescription == 2:
284 # I thinks thats the fastest way to do this, though it's a little ugly
286 for list in recorddict.values():
288 if rtimer.description == description:
289 raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event")
290 except AutoTimerIgnoreTimerException, etite:
294 if timer.checkCounter(timestamp):
297 print "[AutoTimer] Adding an event."
298 newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit)
300 # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
301 newEntry.isAutoTimer = True
304 if timer.hasAfterEvent():
305 afterEvent = timer.getAfterEventTimespan(localtime(end))
306 if afterEvent is None:
307 afterEvent = timer.getAfterEvent()
308 if afterEvent is not None:
309 newEntry.afterEvent = afterEvent
311 newEntry.dirname = timer.destination
312 newEntry.justplay = timer.justplay
313 newEntry.tags = timer.tags
316 # XXX: this won't perform a sanity check, but do we actually want to do so?
317 NavigationInstance.instance.RecordTimer.timeChanged(newEntry)
319 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
320 if conflicts and config.plugins.autotimer.disabled_on_conflict.value:
321 newEntry.disabled = True
322 # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
323 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
324 if conflicts is None:
325 timer.decrementCounter()
327 recorddict.setdefault(serviceref, []).append(newEntry)
329 return (total, new, modified, timers)