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
35 class AutoTimerIgnoreTimerException(Exception):
36 def __init__(self, cause):
40 return "[AutoTimer] " + str(self.cause)
43 return str(type(self))
46 """Read and save xml configuration, query EPGCache"""
50 self.epgcache = eEPGCache.getInstance()
55 self.uniqueTimerId = 0
56 self.defaultTimer = AutoTimerComponent(
66 # Abort if no config found
67 if not os_path.exists(XML_CONFIG):
68 print "[AutoTimer] No configuration file present"
71 # Parse if mtime differs from whats saved
72 mtime = os_path.getmtime(XML_CONFIG)
73 if mtime == self.configMtime:
74 print "[AutoTimer] No changes in configuration, won't parse"
78 self.configMtime = mtime
81 configuration = cet_parse(XML_CONFIG).getroot()
83 # Empty out timers and reset Ids
85 self.defaultTimer.clear(-1, True)
90 configuration.get("version"),
94 self.uniqueTimerId = len(self.timers)
97 writeConfig(XML_CONFIG, self.defaultTimer, self.timers)
101 def add(self, timer):
102 self.timers.append(timer)
104 def getEnabledTimerList(self):
105 return [x for x in self.timers if x.enabled]
107 def getTimerList(self):
110 def getTupleTimerList(self):
112 return [(x,) for x in list]
114 def getSortedTupleTimerList(self):
115 list = self.timers[:]
117 return [(x,) for x in list]
119 def getUniqueId(self):
120 self.uniqueTimerId += 1
121 return self.uniqueTimerId
123 def remove(self, uniqueId):
125 for timer in self.timers:
126 if timer.id == uniqueId:
131 def set(self, timer):
133 for stimer in self.timers:
135 self.timers[idx] = timer
138 self.timers.append(timer)
142 def parseEPG(self, simulateOnly = False):
143 if NavigationInstance.instance is None:
144 print "[AutoTimer] Navigation is not available, can't parse EPG"
154 # Save Recordings in a dict to speed things up a little
155 # We include processed timers as we might search for duplicate descriptions
157 for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
158 if not recorddict.has_key(str(timer.service_ref)):
159 recorddict[str(timer.service_ref)] = [timer]
161 recorddict[str(timer.service_ref)].append(timer)
164 for timer in self.getEnabledTimerList():
165 # Workaround to allow search for umlauts if we know the encoding
167 if timer.encoding != 'UTF-8':
169 match = match.decode('UTF-8').encode(timer.encoding)
170 except UnicodeDecodeError:
173 # Search EPG, default to empty list
174 ret = self.epgcache.search(('RI', 100, eEPGCache.PARTIAL_TITLE_SEARCH, match, eEPGCache.NO_CASE_CHECK)) or []
176 for serviceref, eit in ret:
177 eserviceref = eServiceReference(serviceref)
179 evt = self.epgcache.lookupEventId(eserviceref, eit)
181 print "[AutoTimer] Could not create Event!"
184 # Try to determine real service (we always choose the last one)
185 n = evt.getNumOfLinkageServices()
187 i = evt.getLinkageService(eserviceref, n-1)
188 serviceref = i.toString()
191 name = evt.getEventName()
192 description = evt.getShortDescription()
193 begin = evt.getBeginTime()
194 duration = evt.getDuration()
195 end = begin + duration
197 # If event starts in less than 60 seconds skip it
198 if begin < time() + 60:
202 timestamp = localtime(begin)
205 timer.update(begin, timestamp)
207 # Check Duration, Timespan and Excludes
208 if timer.checkServices(serviceref) \
209 or timer.checkDuration(duration) \
210 or timer.checkTimespan(timestamp) \
211 or timer.checkFilter(name, description,
212 evt.getExtendedDescription(), str(timestamp.tm_wday)):
215 if timer.hasOffset():
216 # Apply custom Offset
217 begin, end = timer.applyOffset(begin, end)
220 begin -= config.recording.margin_before.value * 60
221 end += config.recording.margin_after.value * 60
226 # Append to timerlist and abort if simulating
227 timers.append((name, begin, end, serviceref, timer.name))
235 # Check for double Timers
236 # We first check eit and if user wants us to guess event based on time
237 # we try this as backup. The allowed diff should be configurable though.
238 for rtimer in recorddict.get(serviceref, []):
239 if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8):
242 # Abort if we don't want to modify timers or timer is repeated
243 if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated:
244 print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer"
247 if hasattr(rtimer, "isAutoTimer"):
248 print "[AutoTimer] Modifying existing AutoTimer!"
250 if config.plugins.autotimer.refresh.value != "all":
251 print "[AutoTimer] Won't modify existing timer because it's no timer set by us"
254 print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us"
259 # Modify values saved in timer
261 newEntry.description = description
262 newEntry.begin = int(begin)
263 newEntry.end = int(end)
264 newEntry.service_ref = ServiceReference(serviceref)
267 elif timer.getAvoidDuplicateDescription() == 1 and rtimer.description == description:
269 print "[AutoTimer] We found a timer with same description, skipping event"
272 # We found no timer we want to edit
274 # But there is a match
278 # We want to search for possible doubles
279 if timer.getAvoidDuplicateDescription() == 2:
280 # I thinks thats the fastest way to do this, though it's a little ugly
282 for list in recorddict.values():
284 if rtimer.description == description:
285 raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event")
286 except AutoTimerIgnoreTimerException, etite:
290 if timer.checkCounter(timestamp):
293 print "[AutoTimer] Adding an event."
294 newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit)
296 # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
297 newEntry.isAutoTimer = True
300 if timer.hasAfterEvent():
301 afterEvent = timer.getAfterEventTimespan(localtime(end))
302 if afterEvent is None:
303 afterEvent = timer.getAfterEvent()
304 if afterEvent is not None:
305 newEntry.afterEvent = afterEvent
307 newEntry.dirname = timer.destination
308 newEntry.justplay = timer.justplay
309 newEntry.tags = timer.tags
312 # XXX: this won't perform a sanity check, but do we actually want to do so?
313 NavigationInstance.instance.RecordTimer.timeChanged(newEntry)
315 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
316 if conflicts and config.plugins.autotimer.disabled_on_conflict.value:
317 newEntry.disabled = True
318 # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway
319 conflicts = NavigationInstance.instance.RecordTimer.record(newEntry)
320 if conflicts is None:
321 timer.decrementCounter()
323 if recorddict.has_key(serviceref):
324 recorddict[serviceref].append(newEntry)
326 recorddict[serviceref] = [newEntry]
328 return (total, new, modified, timers)