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"""
60 self.epgcache = eEPGCache.getInstance()
65 self.uniqueTimerId = 0
66 self.defaultTimer = AutoTimerComponent(
76 # Abort if no config found
77 if not os_path.exists(XML_CONFIG):
78 print "[AutoTimer] No configuration file present"
81 # Parse if mtime differs from whats saved
82 mtime = os_path.getmtime(XML_CONFIG)
83 if mtime == self.configMtime:
84 print "[AutoTimer] No changes in configuration, won't parse"
88 self.configMtime = mtime
91 configuration = cet_parse(XML_CONFIG).getroot()
93 # Empty out timers and reset Ids
95 self.defaultTimer.clear(-1, True)
100 configuration.get("version"),
104 self.uniqueTimerId = len(self.timers)
107 writeConfig(XML_CONFIG, self.defaultTimer, self.timers)
111 def add(self, timer):
112 self.timers.append(timer)
114 def getEnabledTimerList(self):
115 return [x for x in self.timers if x.enabled]
117 def getTimerList(self):
120 def getTupleTimerList(self):
122 return [(x,) for x in list]
124 def getSortedTupleTimerList(self):
125 list = self.timers[:]
127 return [(x,) for x in list]
129 def getUniqueId(self):
130 self.uniqueTimerId += 1
131 return self.uniqueTimerId
133 def remove(self, uniqueId):
135 for timer in self.timers:
136 if timer.id == uniqueId:
141 def set(self, timer):
143 for stimer in self.timers:
145 self.timers[idx] = timer
148 self.timers.append(timer)
152 def parseEPG(self, simulateOnly = False):
153 if NavigationInstance.instance is None:
154 print "[AutoTimer] Navigation is not available, can't parse EPG"
164 # Save Recordings in a dict to speed things up a little
165 # We include processed timers as we might search for duplicate descriptions
167 for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
168 if not recorddict.has_key(str(timer.service_ref)):
169 recorddict[str(timer.service_ref)] = [timer]
171 recorddict[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 ret = self.epgcache.search(('RI', 100, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or []
186 for serviceref, eit in ret:
187 eserviceref = eServiceReference(serviceref)
189 evt = self.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
236 # Append to timerlist and abort if simulating
237 timers.append((name, begin, end, serviceref, timer.name))
245 # Check for double Timers
246 # We first check eit and if user wants us to guess event based on time
247 # we try this as backup. The allowed diff should be configurable though.
248 for rtimer in recorddict.get(serviceref, []):
249 if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8):
252 # Abort if we don't want to modify timers or timer is repeated
253 if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
254 print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer"
257 if hasattr(rtimer, "isAutoTimer"):
258 print "[AutoTimer] Modifying existing AutoTimer!"
260 if config.plugins.autotimer.refresh.value != "all":
261 print "[AutoTimer] Won't modify existing timer because it's no timer set by us"
264 print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us"
269 # Modify values saved in timer
271 newEntry.description = description
272 newEntry.begin = int(begin)
273 newEntry.end = int(end)
274 newEntry.service_ref = ServiceReference(serviceref)
277 elif timer.getAvoidDuplicateDescription() == 1 and rtimer.description == description:
279 print "[AutoTimer] We found a timer with same description, skipping event"
282 # We found no timer we want to edit
284 # But there is a match
288 # We want to search for possible doubles
289 if timer.getAvoidDuplicateDescription() == 2:
290 # I thinks thats the fastest way to do this, though it's a little ugly
292 for list in recorddict.values():
294 if rtimer.description == description:
295 raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event")
296 except AutoTimerIgnoreTimerException, etite:
300 if timer.checkCounter(timestamp):
303 print "[AutoTimer] Adding an event."
304 newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit)
306 # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
307 newEntry.isAutoTimer = True
310 if timer.hasAfterEvent():
311 afterEvent = timer.getAfterEventTimespan(localtime(end))
312 if afterEvent is None:
313 afterEvent = timer.getAfterEvent()
314 if afterEvent is not None:
315 newEntry.afterEvent = afterEvent
317 newEntry.dirname = timer.destination
318 newEntry.justplay = timer.justplay
319 newEntry.tags = timer.tags
322 # XXX: this won't perform a sanity check, but do we actually want to do so?
323 NavigationInstance.instance.RecordTimer.timeChanged(newEntry)
325 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
326 if conflicts and config.plugins.autotimer.disabled_on_conflict.value:
327 newEntry.disabled = True
328 # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
329 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
330 if conflicts is None:
331 timer.decrementCounter()
333 if recorddict.has_key(serviceref):
334 recorddict[serviceref].append(newEntry)
336 recorddict[serviceref] = [newEntry]
338 return (total, new, modified, timers)