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"
157 return (0, 0, 0, [], [])
167 # Save Recordings in a dict to speed things up a little
168 # We include processed timers as we might search for duplicate descriptions
170 for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
171 recorddict.setdefault(str(timer.service_ref), []).append(timer)
174 for timer in self.getEnabledTimerList():
175 # Workaround to allow search for umlauts if we know the encoding
177 if timer.encoding != 'UTF-8':
179 match = match.decode('UTF-8').encode(timer.encoding)
180 except UnicodeDecodeError:
183 # Search EPG, default to empty list
184 epgcache = eEPGCache.getInstance()
185 ret = epgcache.search(('RI', 500, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or ()
187 for serviceref, eit in ret:
188 eserviceref = eServiceReference(serviceref)
190 evt = epgcache.lookupEventId(eserviceref, eit)
192 print "[AutoTimer] Could not create Event!"
195 # Try to determine real service (we always choose the last one)
196 n = evt.getNumOfLinkageServices()
198 i = evt.getLinkageService(eserviceref, n-1)
199 serviceref = i.toString()
202 name = evt.getEventName()
203 description = evt.getShortDescription()
204 begin = evt.getBeginTime()
205 duration = evt.getDuration()
206 end = begin + duration
208 # If event starts in less than 60 seconds skip it
209 if begin < time() + 60:
213 timestamp = localtime(begin)
216 timer.update(begin, timestamp)
218 # Check Duration, Timespan and Excludes
219 if timer.checkServices(serviceref) \
220 or timer.checkDuration(duration) \
221 or timer.checkTimespan(timestamp) \
222 or timer.checkFilter(name, description,
223 evt.getExtendedDescription(), str(timestamp.tm_wday)):
226 if timer.hasOffset():
227 # Apply custom Offset
228 begin, end = timer.applyOffset(begin, end)
231 begin -= config.recording.margin_before.value * 60
232 end += config.recording.margin_after.value * 60
234 # Eventually change service to alternative
235 if timer.overrideAlternatives:
236 serviceref = timer.getAlternative(serviceref)
240 # Append to timerlist and abort if simulating
241 timers.append((name, begin, end, serviceref, timer.name))
249 # Check for double Timers
250 # We first check eit and if user wants us to guess event based on time
251 # we try this as backup. The allowed diff should be configurable though.
252 for rtimer in recorddict.get(serviceref, ()):
253 if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8):
256 # Abort if we don't want to modify timers or timer is repeated
257 if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
258 print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer"
261 if hasattr(rtimer, "isAutoTimer"):
262 print "[AutoTimer] Modifying existing AutoTimer!"
264 if config.plugins.autotimer.refresh.value != "all":
265 print "[AutoTimer] Won't modify existing timer because it's no timer set by us"
268 print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us"
273 # Modify values saved in timer
275 newEntry.description = description
276 newEntry.begin = int(begin)
277 newEntry.end = int(end)
278 newEntry.service_ref = ServiceReference(serviceref)
281 elif timer.avoidDuplicateDescription == 1 and not rtimer.disabled and rtimer.name == name and rtimer.description == description:
283 print "[AutoTimer] We found a timer with same description, skipping event"
286 # We found no timer we want to edit
288 # But there is a match
292 # We want to search for possible doubles
293 if timer.avoidDuplicateDescription == 2:
294 # I thinks thats the fastest way to do this, though it's a little ugly
296 for list in recorddict.values():
298 if not rtimer.disabled and rtimer.name == name and rtimer.description == description:
299 raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event")
300 except AutoTimerIgnoreTimerException, etite:
304 if timer.checkCounter(timestamp):
307 print "[AutoTimer] Adding an event."
308 newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit)
310 # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
311 newEntry.isAutoTimer = True
314 if timer.hasAfterEvent():
315 afterEvent = timer.getAfterEventTimespan(localtime(end))
316 if afterEvent is None:
317 afterEvent = timer.getAfterEvent()
318 if afterEvent is not None:
319 newEntry.afterEvent = afterEvent
321 newEntry.dirname = timer.destination
322 newEntry.justplay = timer.justplay
323 newEntry.tags = timer.tags
326 # XXX: this won't perform a sanity check, but do we actually want to do so?
327 NavigationInstance.instance.RecordTimer.timeChanged(newEntry)
329 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
330 if conflicts and config.plugins.autotimer.disabled_on_conflict.value:
331 newEntry.disabled = True
332 # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
333 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
334 conflicting.append((name, begin, end, serviceref, timer.name))
335 if conflicts is None:
336 timer.decrementCounter()
338 recorddict.setdefault(serviceref, []).append(newEntry)
340 conflicting.append((name, begin, end, serviceref, timer.name))
342 return (total, new, modified, timers, conflicting)