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
228 # Eventually change service to alternative
229 if timer.overrideAlternatives:
230 serviceref = timer.getAlternative(serviceref)
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.avoidDuplicateDescription == 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.avoidDuplicateDescription == 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 recorddict.setdefault(serviceref, []).append(newEntry)
333 return (total, new, modified, timers)