2 from xml.etree.cElementTree import parse as cet_parse
3 from os import path as os_path
4 from AutoTimerConfiguration import parseConfig, buildConfig
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 preferredAutoTimerComponent
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 = preferredAutoTimerComponent(
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 return buildConfig(self.defaultTimer, self.timers, webif = True)
107 file = open(XML_CONFIG, 'w')
108 file.writelines(buildConfig(self.defaultTimer, self.timers))
113 def add(self, timer):
114 self.timers.append(timer)
116 def getEnabledTimerList(self):
117 return [x for x in self.timers if x.enabled]
119 def getTimerList(self):
122 def getTupleTimerList(self):
124 return [(x,) for x in list]
126 def getSortedTupleTimerList(self):
127 list = self.timers[:]
129 return [(x,) for x in list]
131 def getUniqueId(self):
132 self.uniqueTimerId += 1
133 return self.uniqueTimerId
135 def remove(self, uniqueId):
137 for timer in self.timers:
138 if timer.id == uniqueId:
143 def set(self, timer):
145 for stimer in self.timers:
147 self.timers[idx] = timer
150 self.timers.append(timer)
154 def parseEPG(self, simulateOnly = False):
155 if NavigationInstance.instance is None:
156 print "[AutoTimer] Navigation is not available, can't parse EPG"
166 # Save Recordings in a dict to speed things up a little
167 # We include processed timers as we might search for duplicate descriptions
169 for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
170 recorddict.setdefault(str(timer.service_ref), []).append(timer)
173 for timer in self.getEnabledTimerList():
174 # Workaround to allow search for umlauts if we know the encoding
176 if timer.encoding != 'UTF-8':
178 match = match.decode('UTF-8').encode(timer.encoding)
179 except UnicodeDecodeError:
182 # Search EPG, default to empty list
183 epgcache = eEPGCache.getInstance()
184 ret = epgcache.search(('RI', 100, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or ()
186 for serviceref, eit in ret:
187 eserviceref = eServiceReference(serviceref)
189 evt = epgcache.lookupEventId(eserviceref, eit)
191 print "[AutoTimer] Could not create Event!"
194 # Try to determine real service (we always choose the last one)
195 n = evt.getNumOfLinkageServices()
197 i = evt.getLinkageService(eserviceref, n-1)
198 serviceref = i.toString()
201 name = evt.getEventName()
202 description = evt.getShortDescription()
203 begin = evt.getBeginTime()
204 duration = evt.getDuration()
205 end = begin + duration
207 # If event starts in less than 60 seconds skip it
208 if begin < time() + 60:
212 timestamp = localtime(begin)
215 timer.update(begin, timestamp)
217 # Check Duration, Timespan and Excludes
218 if timer.checkServices(serviceref) \
219 or timer.checkDuration(duration) \
220 or timer.checkTimespan(timestamp) \
221 or timer.checkFilter(name, description,
222 evt.getExtendedDescription(), str(timestamp.tm_wday)):
225 if timer.hasOffset():
226 # Apply custom Offset
227 begin, end = timer.applyOffset(begin, end)
230 begin -= config.recording.margin_before.value * 60
231 end += config.recording.margin_after.value * 60
233 # Eventually change service to alternative
234 if timer.overrideAlternatives:
235 serviceref = timer.getAlternative(serviceref)
239 # Append to timerlist and abort if simulating
240 timers.append((name, begin, end, serviceref, timer.name))
248 # Check for double Timers
249 # We first check eit and if user wants us to guess event based on time
250 # we try this as backup. The allowed diff should be configurable though.
251 for rtimer in recorddict.get(serviceref, ()):
252 if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8):
255 # Abort if we don't want to modify timers or timer is repeated
256 if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
257 print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer"
260 if hasattr(rtimer, "isAutoTimer"):
261 print "[AutoTimer] Modifying existing AutoTimer!"
263 if config.plugins.autotimer.refresh.value != "all":
264 print "[AutoTimer] Won't modify existing timer because it's no timer set by us"
267 print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us"
272 # Modify values saved in timer
274 newEntry.description = description
275 newEntry.begin = int(begin)
276 newEntry.end = int(end)
277 newEntry.service_ref = ServiceReference(serviceref)
280 elif timer.avoidDuplicateDescription == 1 and rtimer.description == description:
282 print "[AutoTimer] We found a timer with same description, skipping event"
285 # We found no timer we want to edit
287 # But there is a match
291 # We want to search for possible doubles
292 if timer.avoidDuplicateDescription == 2:
293 # I thinks thats the fastest way to do this, though it's a little ugly
295 for list in recorddict.values():
297 if rtimer.description == description:
298 raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event")
299 except AutoTimerIgnoreTimerException, etite:
303 if timer.checkCounter(timestamp):
306 print "[AutoTimer] Adding an event."
307 newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit)
309 # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
310 newEntry.isAutoTimer = True
313 if timer.hasAfterEvent():
314 afterEvent = timer.getAfterEventTimespan(localtime(end))
315 if afterEvent is None:
316 afterEvent = timer.getAfterEvent()
317 if afterEvent is not None:
318 newEntry.afterEvent = afterEvent
320 newEntry.dirname = timer.destination
321 newEntry.justplay = timer.justplay
322 newEntry.tags = timer.tags
325 # XXX: this won't perform a sanity check, but do we actually want to do so?
326 NavigationInstance.instance.RecordTimer.timeChanged(newEntry)
328 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
329 if conflicts and config.plugins.autotimer.disabled_on_conflict.value:
330 newEntry.disabled = True
331 # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
332 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
333 if conflicts is None:
334 timer.decrementCounter()
336 recorddict.setdefault(serviceref, []).append(newEntry)
338 return (total, new, modified, timers)