do not show inaccessible devices in deviceselection
[enigma2.git] / RecordTimer.py
1 import time
2 #from time import datetime
3 from Tools import Directories, Notifications
4
5 from Components.config import config
6 import timer
7 import xml.etree.cElementTree
8
9 from enigma import eEPGCache, getBestPlayableServiceReference, \
10         eServiceReference, iRecordableService, quitMainloop
11
12 from Screens.MessageBox import MessageBox
13 from Components.TimerSanityCheck import TimerSanityCheck
14 import NavigationInstance
15
16 import Screens.Standby
17
18 from time import localtime
19
20 from Tools.XMLTools import stringToXML
21 from ServiceReference import ServiceReference
22
23 # ok, for descriptions etc we have:
24 # service reference  (to get the service name)
25 # name               (title)
26 # description        (description)
27 # event data         (ONLY for time adjustments etc.)
28
29
30 # parses an event, and gives out a (begin, end, name, duration, eit)-tuple.
31 # begin and end will be corrected
32 def parseEvent(ev, description = True):
33         if description:
34                 name = ev.getEventName()
35                 description = ev.getShortDescription()
36         else:
37                 name = ""
38                 description = ""
39         begin = ev.getBeginTime()
40         end = begin + ev.getDuration()
41         eit = ev.getEventId()
42         begin -= config.recording.margin_before.value * 60
43         end += config.recording.margin_after.value * 60
44         return (begin, end, name, description, eit)
45
46 class AFTEREVENT:
47         NONE = 0
48         STANDBY = 1
49         DEEPSTANDBY = 2
50         AUTO = 3
51
52 # please do not translate log messages
53 class RecordTimerEntry(timer.TimerEntry, object):
54 ######### the following static methods and members are only in use when the box is in (soft) standby
55         receiveRecordEvents = False
56
57         @staticmethod
58         def shutdown():
59                 quitMainloop(1)
60
61         @staticmethod
62         def staticGotRecordEvent(recservice, event):
63                 if event == iRecordableService.evEnd:
64                         print "RecordTimer.staticGotRecordEvent(iRecordableService.evEnd)"
65                         recordings = NavigationInstance.instance.getRecordings()
66                         if not len(recordings): # no more recordings exist
67                                 rec_time = NavigationInstance.instance.RecordTimer.getNextRecordingTime()
68                                 if rec_time > 0 and (rec_time - time.time()) < 360:
69                                         print "another recording starts in", rec_time - time.time(), "seconds... do not shutdown yet"
70                                 else:
71                                         print "no starting records in the next 360 seconds... immediate shutdown"
72                                         RecordTimerEntry.shutdown() # immediate shutdown
73                 elif event == iRecordableService.evStart:
74                         print "RecordTimer.staticGotRecordEvent(iRecordableService.evStart)"
75
76         @staticmethod
77         def stopTryQuitMainloop():
78                 print "RecordTimer.stopTryQuitMainloop"
79                 NavigationInstance.instance.record_event.remove(RecordTimerEntry.staticGotRecordEvent)
80                 RecordTimerEntry.receiveRecordEvents = False
81
82         @staticmethod
83         def TryQuitMainloop(default_yes = True):
84                 if not RecordTimerEntry.receiveRecordEvents:
85                         print "RecordTimer.TryQuitMainloop"
86                         NavigationInstance.instance.record_event.append(RecordTimerEntry.staticGotRecordEvent)
87                         RecordTimerEntry.receiveRecordEvents = True
88                         # send fake event.. to check if another recordings are running or
89                         # other timers start in a few seconds
90                         RecordTimerEntry.staticGotRecordEvent(None, iRecordableService.evEnd)
91                         # send normal notification for the case the user leave the standby now..
92                         Notifications.AddNotification(Screens.Standby.TryQuitMainloop, 1, onSessionOpenCallback=RecordTimerEntry.stopTryQuitMainloop, default_yes = default_yes)
93 #################################################################
94
95         def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.AUTO, checkOldTimers = False, dirname = None, tags = None):
96                 timer.TimerEntry.__init__(self, int(begin), int(end))
97
98                 if checkOldTimers == True:
99                         if self.begin < time.time() - 1209600:
100                                 self.begin = int(time.time())
101                 
102                 if self.end < self.begin:
103                         self.end = self.begin
104                 
105                 assert isinstance(serviceref, ServiceReference)
106                 
107                 self.service_ref = serviceref
108                 self.eit = eit
109                 self.dontSave = False
110                 self.name = name
111                 self.description = description
112                 self.disabled = disabled
113                 self.timer = None
114                 self.__record_service = None
115                 self.start_prepare = 0
116                 self.justplay = justplay
117                 self.afterEvent = afterEvent
118                 self.dirname = dirname
119                 self.dirnameHadToFallback = False
120                 self.autoincrease = False
121                 self.autoincreasetime = 3600 * 24 # 1 day
122                 self.tags = tags or []
123
124                 self.log_entries = []
125                 self.resetState()
126         
127         def log(self, code, msg):
128                 self.log_entries.append((int(time.time()), code, msg))
129                 print "[TIMER]", msg
130
131         def calculateFilename(self):
132                 service_name = self.service_ref.getServiceName()
133                 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
134                 
135                 print "begin_date: ", begin_date
136                 print "service_name: ", service_name
137                 print "name:", self.name
138                 print "description: ", self.description
139                 
140                 filename = begin_date + " - " + service_name
141                 if self.name:
142                         filename += " - " + self.name
143
144                 if self.dirname and not Directories.fileExists(self.dirname, 'w'):
145                         self.dirnameHadToFallback = True
146                         self.Filename = Directories.getRecordingFilename(filename, None)
147                 else:
148                         self.Filename = Directories.getRecordingFilename(filename, self.dirname)
149                 self.log(0, "Filename calculated as: '%s'" % self.Filename)
150                 #begin_date + " - " + service_name + description)
151
152         def tryPrepare(self):
153                 if self.justplay:
154                         return True
155                 else:
156                         self.calculateFilename()
157                         rec_ref = self.service_ref and self.service_ref.ref
158                         if rec_ref and rec_ref.flags & eServiceReference.isGroup:
159                                 rec_ref = getBestPlayableServiceReference(rec_ref, eServiceReference())
160                                 if not rec_ref:
161                                         self.log(1, "'get best playable service for group... record' failed")
162                                         return False
163                                 
164                         self.record_service = rec_ref and NavigationInstance.instance.recordService(rec_ref)
165
166                         if not self.record_service:
167                                 self.log(1, "'record service' failed")
168                                 return False
169
170                         if self.repeated:
171                                 epgcache = eEPGCache.getInstance()
172                                 queryTime=self.begin+(self.end-self.begin)/2
173                                 evt = epgcache.lookupEventTime(rec_ref, queryTime)
174                                 if evt:
175                                         self.description = evt.getShortDescription()
176                                         event_id = evt.getEventId()
177                                 else:
178                                         event_id = -1
179                         else:
180                                 event_id = self.eit
181                                 if event_id is None:
182                                         event_id = -1
183
184                         prep_res=self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id)
185                         if prep_res:
186                                 self.log(2, "'prepare' failed: error %d" % prep_res)
187                                 NavigationInstance.instance.stopRecordService(self.record_service)
188                                 self.record_service = None
189                                 return False
190
191                         self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
192                         try:
193                                 f = open(self.Filename + ".ts.meta", "w")
194                                 f.write(rec_ref.toString() + "\n")
195                                 f.write(self.name + "\n")
196                                 f.write(self.description + "\n")
197                                 f.write(str(self.begin) + "\n")
198                                 f.write(' '.join(self.tags))
199                                 f.close()
200                         except IOError:
201                                 self.log(4, "failed to write meta information")
202                                 NavigationInstance.instance.stopRecordService(self.record_service)
203                                 self.record_service = None
204                                 return False
205                         return True
206
207         def do_backoff(self):
208                 if self.backoff == 0:
209                         self.backoff = 5
210                 else:
211                         self.backoff *= 2
212                         if self.backoff > 100:
213                                 self.backoff = 100
214                 self.log(10, "backoff: retry in %d seconds" % self.backoff)
215
216         def activate(self):
217                 next_state = self.state + 1
218                 self.log(5, "activating state %d" % next_state)
219                 
220                 if next_state == self.StatePrepared:
221                         if self.tryPrepare():
222                                 self.log(6, "prepare ok, waiting for begin")
223                                 # fine. it worked, resources are allocated.
224                                 self.next_activation = self.begin
225                                 self.backoff = 0
226                                 return True
227                         
228                         self.log(7, "prepare failed")
229                         if self.first_try_prepare:
230                                 self.first_try_prepare = False
231                                 if not config.recording.asktozap.value:
232                                         self.log(8, "asking user to zap away")
233                                         Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"), timeout=20)
234                                 else: # zap without asking
235                                         self.log(9, "zap without asking")
236                                         Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20)
237                                         self.failureCB(True)
238
239                         self.do_backoff()
240                         # retry
241                         self.start_prepare = time.time() + self.backoff
242                         return False
243                 elif next_state == self.StateRunning:
244                         # if this timer has been cancelled, just go to "end" state.
245                         if self.cancelled:
246                                 return True
247
248                         if self.justplay:
249                                 if Screens.Standby.inStandby:
250                                         self.log(11, "wakeup and zap")
251                                         #set service to zap after standby
252                                         Screens.Standby.inStandby.prev_running_service = self.service_ref.ref
253                                         #wakeup standby
254                                         Screens.Standby.inStandby.Power()
255                                 else:
256                                         self.log(11, "zapping")
257                                         NavigationInstance.instance.playService(self.service_ref.ref)
258                                 return True
259                         else:
260                                 self.log(11, "start recording")
261                                 record_res = self.record_service.start()
262                                 
263                                 if record_res:
264                                         self.log(13, "start record returned %d" % record_res)
265                                         self.do_backoff()
266                                         # retry
267                                         self.begin = time.time() + self.backoff
268                                         return False
269
270                                 return True
271                 elif next_state == self.StateEnded:
272                         old_end = self.end
273                         if self.setAutoincreaseEnd():
274                                 self.log(12, "autoincrase recording %d minute(s)" % int((self.end - old_end)/60))
275                                 self.state -= 1
276                                 return True
277                         self.log(12, "stop recording")
278                         if not self.justplay:
279                                 NavigationInstance.instance.stopRecordService(self.record_service)
280                                 self.record_service = None
281                         if self.afterEvent == AFTEREVENT.STANDBY:
282                                 if not Screens.Standby.inStandby: # not already in standby
283                                         Notifications.AddNotificationWithCallback(self.sendStandbyNotification, MessageBox, _("A finished record timer wants to set your\nDreambox to standby. Do that now?"), timeout = 20)
284                         elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
285                                 if not Screens.Standby.inTryQuitMainloop: # not a shutdown messagebox is open
286                                         if Screens.Standby.inStandby: # in standby
287                                                 RecordTimerEntry.TryQuitMainloop() # start shutdown handling without screen
288                                         else:
289                                                 Notifications.AddNotificationWithCallback(self.sendTryQuitMainloopNotification, MessageBox, _("A finished record timer wants to shut down\nyour Dreambox. Shutdown now?"), timeout = 20)
290                         return True
291
292         def setAutoincreaseEnd(self, entry = None):
293                 if not self.autoincrease:
294                         return False
295                 if entry is None:
296                         new_end =  int(time.time()) + self.autoincreasetime
297                 else:
298                         new_end = entry.begin -30
299
300                 dummyentry = RecordTimerEntry(self.service_ref, self.begin, new_end, self.name, self.description, self.eit, disabled=True, justplay = self.justplay, afterEvent = self.afterEvent, dirname = self.dirname, tags = self.tags)
301                 dummyentry.disabled = self.disabled
302                 timersanitycheck = TimerSanityCheck(NavigationInstance.instance.RecordTimer.timer_list, dummyentry)
303                 if not timersanitycheck.check():
304                         simulTimerList = timersanitycheck.getSimulTimerList()
305                         new_end = simulTimerList[1].begin
306                         del simulTimerList
307                         new_end -= 30                           # 30 Sekunden Prepare-Zeit lassen
308                 del dummyentry
309                 if new_end <= time.time():
310                         return False
311                 self.end = new_end
312                 return True
313         
314         
315         def sendStandbyNotification(self, answer):
316                 if answer:
317                         Notifications.AddNotification(Screens.Standby.Standby)
318
319         def sendTryQuitMainloopNotification(self, answer):
320                 if answer:
321                         Notifications.AddNotification(Screens.Standby.TryQuitMainloop, 1)
322
323         def getNextActivation(self):
324                 if self.state == self.StateEnded:
325                         return self.end
326                 
327                 next_state = self.state + 1
328                 
329                 return {self.StatePrepared: self.start_prepare, 
330                                 self.StateRunning: self.begin, 
331                                 self.StateEnded: self.end }[next_state]
332
333         def failureCB(self, answer):
334                 if answer == True:
335                         self.log(13, "ok, zapped away")
336                         #NavigationInstance.instance.stopUserServices()
337                         NavigationInstance.instance.playService(self.service_ref.ref)
338                 else:
339                         self.log(14, "user didn't want to zap away, record will probably fail")
340
341         def timeChanged(self):
342                 old_prepare = self.start_prepare
343                 self.start_prepare = self.begin - self.prepare_time
344                 self.backoff = 0
345                 
346                 if int(old_prepare) != int(self.start_prepare):
347                         self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
348
349         def gotRecordEvent(self, record, event):
350                 # TODO: this is not working (never true), please fix. (comparing two swig wrapped ePtrs)
351                 if self.__record_service.__deref__() != record.__deref__():
352                         return
353                 self.log(16, "record event %d" % event)
354                 if event == iRecordableService.evRecordWriteError:
355                         print "WRITE ERROR on recording, disk full?"
356                         # show notification. the 'id' will make sure that it will be
357                         # displayed only once, even if more timers are failing at the
358                         # same time. (which is very likely in case of disk fullness)
359                         Notifications.AddPopup(text = _("Write error while recording. Disk full?\n"), type = MessageBox.TYPE_ERROR, timeout = 0, id = "DiskFullMessage")
360                         # ok, the recording has been stopped. we need to properly note 
361                         # that in our state, with also keeping the possibility to re-try.
362                         # TODO: this has to be done.
363                 elif event == iRecordableService.evStart:
364                         text = _("A record has been started:\n%s") % self.name
365                         if self.dirnameHadToFallback:
366                                 text = '\n'.join((text, _("Please note that the previously selected media could not be accessed and therefore the default directory is being used instead.")))
367
368                         # maybe this should be configurable?
369                         Notifications.AddPopup(text = text, type = MessageBox.TYPE_INFO, timeout = 3)
370
371         # we have record_service as property to automatically subscribe to record service events
372         def setRecordService(self, service):
373                 if self.__record_service is not None:
374                         print "[remove callback]"
375                         NavigationInstance.instance.record_event.remove(self.gotRecordEvent)
376
377                 self.__record_service = service
378
379                 if self.__record_service is not None:
380                         print "[add callback]"
381                         NavigationInstance.instance.record_event.append(self.gotRecordEvent)
382
383         record_service = property(lambda self: self.__record_service, setRecordService)
384
385 def createTimer(xml):
386         begin = int(xml.get("begin"))
387         end = int(xml.get("end"))
388         serviceref = ServiceReference(xml.get("serviceref").encode("utf-8"))
389         description = xml.get("description").encode("utf-8")
390         repeated = xml.get("repeated").encode("utf-8")
391         disabled = long(xml.get("disabled") or "0")
392         justplay = long(xml.get("justplay") or "0")
393         afterevent = str(xml.get("afterevent") or "nothing")
394         afterevent = {
395                 "nothing": AFTEREVENT.NONE,
396                 "standby": AFTEREVENT.STANDBY,
397                 "deepstandby": AFTEREVENT.DEEPSTANDBY,
398                 "auto": AFTEREVENT.AUTO
399                 }[afterevent]
400         eit = xml.get("eit")
401         if eit and eit != "None":
402                 eit = long(eit);
403         else:
404                 eit = None
405         location = xml.get("location")
406         if location and location != "None":
407                 location = location.encode("utf-8")
408         else:
409                 location = None
410         tags = xml.get("tags")
411         if tags and tags != "None":
412                 tags = tags.encode("utf-8").split(' ')
413         else:
414                 tags = None
415
416         name = xml.get("name").encode("utf-8")
417         #filename = xml.get("filename").encode("utf-8")
418         entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent, dirname = location, tags = tags)
419         entry.repeated = int(repeated)
420         
421         for l in xml.findall("log"):
422                 time = int(l.get("time"))
423                 code = int(l.get("code"))
424                 msg = l.text.strip().encode("utf-8")
425                 entry.log_entries.append((time, code, msg))
426         
427         return entry
428
429 class RecordTimer(timer.Timer):
430         def __init__(self):
431                 timer.Timer.__init__(self)
432                 
433                 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
434                 
435                 try:
436                         self.loadTimer()
437                 except IOError:
438                         print "unable to load timers from file!"
439                         
440         def isRecording(self):
441                 isRunning = False
442                 for timer in self.timer_list:
443                         if timer.isRunning() and not timer.justplay:
444                                 isRunning = True
445                 return isRunning
446         
447         def loadTimer(self):
448                 # TODO: PATH!
449                 try:
450                         doc = xml.etree.cElementTree.parse(self.Filename)
451                 except SyntaxError:
452                         from Tools.Notifications import AddPopup
453                         from Screens.MessageBox import MessageBox
454
455                         AddPopup(_("The timer file (timers.xml) is corrupt and could not be loaded."), type = MessageBox.TYPE_ERROR, timeout = 0, id = "TimerLoadFailed")
456
457                         print "timers.xml failed to load!"
458                         try:
459                                 import os
460                                 os.rename(self.Filename, self.Filename + "_old")
461                         except (IOError, OSError):
462                                 print "renaming broken timer failed"
463                         return
464                 except IOError:
465                         print "timers.xml not found!"
466                         return
467
468                 root = doc.getroot()
469
470                 # put out a message when at least one timer overlaps
471                 checkit = True
472                 for timer in root.findall("timer"):
473                         newTimer = createTimer(timer)
474                         if (self.record(newTimer, True, True) is not None) and (checkit == True):
475                                 from Tools.Notifications import AddPopup
476                                 from Screens.MessageBox import MessageBox
477                                 AddPopup(_("Timer overlap in timers.xml detected!\nPlease recheck it!"), type = MessageBox.TYPE_ERROR, timeout = 0, id = "TimerLoadFailed")
478                                 checkit = False # at moment it is enough when the message is displayed one time
479
480         def saveTimer(self):
481                 #root_element = xml.etree.cElementTree.Element('timers')
482                 #root_element.text = "\n"
483
484                 #for timer in self.timer_list + self.processed_timers:
485                         # some timers (instant records) don't want to be saved.
486                         # skip them
487                         #if timer.dontSave:
488                                 #continue
489                         #t = xml.etree.cElementTree.SubElement(root_element, 'timers')
490                         #t.set("begin", str(int(timer.begin)))
491                         #t.set("end", str(int(timer.end)))
492                         #t.set("serviceref", str(timer.service_ref))
493                         #t.set("repeated", str(timer.repeated))                 
494                         #t.set("name", timer.name)
495                         #t.set("description", timer.description)
496                         #t.set("afterevent", str({
497                         #       AFTEREVENT.NONE: "nothing",
498                         #       AFTEREVENT.STANDBY: "standby",
499                         #       AFTEREVENT.DEEPSTANDBY: "deepstandby",
500                         #       AFTEREVENT.AUTO: "auto"}))
501                         #if timer.eit is not None:
502                         #       t.set("eit", str(timer.eit))
503                         #if timer.dirname is not None:
504                         #       t.set("location", str(timer.dirname))
505                         #t.set("disabled", str(int(timer.disabled)))
506                         #t.set("justplay", str(int(timer.justplay)))
507                         #t.text = "\n"
508                         #t.tail = "\n"
509
510                         #for time, code, msg in timer.log_entries:
511                                 #l = xml.etree.cElementTree.SubElement(t, 'log')
512                                 #l.set("time", str(time))
513                                 #l.set("code", str(code))
514                                 #l.text = str(msg)
515                                 #l.tail = "\n"
516
517                 #doc = xml.etree.cElementTree.ElementTree(root_element)
518                 #doc.write(self.Filename)
519
520                 list = []
521
522                 list.append('<?xml version="1.0" ?>\n')
523                 list.append('<timers>\n')
524                 
525                 for timer in self.timer_list + self.processed_timers:
526                         if timer.dontSave:
527                                 continue
528
529                         list.append('<timer')
530                         list.append(' begin="' + str(int(timer.begin)) + '"')
531                         list.append(' end="' + str(int(timer.end)) + '"')
532                         list.append(' serviceref="' + stringToXML(str(timer.service_ref)) + '"')
533                         list.append(' repeated="' + str(int(timer.repeated)) + '"')
534                         list.append(' name="' + str(stringToXML(timer.name)) + '"')
535                         list.append(' description="' + str(stringToXML(timer.description)) + '"')
536                         list.append(' afterevent="' + str(stringToXML({
537                                 AFTEREVENT.NONE: "nothing",
538                                 AFTEREVENT.STANDBY: "standby",
539                                 AFTEREVENT.DEEPSTANDBY: "deepstandby",
540                                 AFTEREVENT.AUTO: "auto"
541                                 }[timer.afterEvent])) + '"')
542                         if timer.eit is not None:
543                                 list.append(' eit="' + str(timer.eit) + '"')
544                         if timer.dirname is not None:
545                                 list.append(' location="' + str(stringToXML(timer.dirname)) + '"')
546                         if timer.tags is not None:
547                                 list.append(' tags="' + str(stringToXML(' '.join(timer.tags))) + '"')
548                         list.append(' disabled="' + str(int(timer.disabled)) + '"')
549                         list.append(' justplay="' + str(int(timer.justplay)) + '"')
550                         list.append('>\n')
551                         
552                         if config.recording.debug.value:
553                                 for time, code, msg in timer.log_entries:
554                                         list.append('<log')
555                                         list.append(' code="' + str(code) + '"')
556                                         list.append(' time="' + str(time) + '"')
557                                         list.append('>')
558                                         list.append(str(stringToXML(msg)))
559                                         list.append('</log>\n')
560                         
561                         list.append('</timer>\n')
562
563                 list.append('</timers>\n')
564
565                 file = open(self.Filename, "w")
566                 for x in list:
567                         file.write(x)
568                 file.close()
569
570         def getNextZapTime(self):
571                 now = time.time()
572                 for timer in self.timer_list:
573                         if not timer.justplay or timer.begin < now:
574                                 continue
575                         return timer.begin
576                 return -1
577
578         def getNextRecordingTime(self):
579                 now = time.time()
580                 for timer in self.timer_list:
581                         if timer.justplay or timer.begin < now:
582                                 continue
583                         return timer.begin
584                 return -1
585
586         def isNextRecordAfterEventActionAuto(self):
587                 now = time.time()
588                 t = None
589                 for timer in self.timer_list:
590                         if timer.justplay or timer.begin < now:
591                                 continue
592                         if t is None or t.begin == timer.begin:
593                                 t = timer
594                                 if t.afterEvent == AFTEREVENT.AUTO:
595                                         return True
596                 return False
597
598         def record(self, entry, ignoreTSC=False, dosave=True):          #wird von loadTimer mit dosave=False aufgerufen
599                 timersanitycheck = TimerSanityCheck(self.timer_list,entry)
600                 if not timersanitycheck.check():
601                         if ignoreTSC != True:
602                                 print "timer conflict detected!"
603                                 print timersanitycheck.getSimulTimerList()
604                                 return timersanitycheck.getSimulTimerList()
605                         else:
606                                 print "ignore timer conflict"
607                 elif timersanitycheck.doubleCheck():
608                         print "ignore double timer"
609                         return None
610                 entry.timeChanged()
611                 print "[Timer] Record " + str(entry)
612                 entry.Timer = self
613                 self.addTimerEntry(entry)
614                 if dosave:
615                         self.saveTimer()
616                 return None
617
618         def isInTimer(self, eventid, begin, duration, service):
619                 time_match = 0
620                 chktime = None
621                 chktimecmp = None
622                 chktimecmp_end = None
623                 end = begin + duration
624                 refstr = str(service)
625                 for x in self.timer_list:
626                         check = x.service_ref.ref.toString() == refstr
627                         if not check:
628                                 sref = x.service_ref.ref
629                                 parent_sid = sref.getUnsignedData(5)
630                                 parent_tsid = sref.getUnsignedData(6)
631                                 if parent_sid and parent_tsid: # check for subservice
632                                         sid = sref.getUnsignedData(1)
633                                         tsid = sref.getUnsignedData(2)
634                                         sref.setUnsignedData(1, parent_sid)
635                                         sref.setUnsignedData(2, parent_tsid)
636                                         sref.setUnsignedData(5, 0)
637                                         sref.setUnsignedData(6, 0)
638                                         check = sref.toCompareString() == refstr
639                                         num = 0
640                                         if check:
641                                                 check = False
642                                                 event = eEPGCache.getInstance().lookupEventId(sref, eventid)
643                                                 num = event and event.getNumOfLinkageServices() or 0
644                                         sref.setUnsignedData(1, sid)
645                                         sref.setUnsignedData(2, tsid)
646                                         sref.setUnsignedData(5, parent_sid)
647                                         sref.setUnsignedData(6, parent_tsid)
648                                         for cnt in range(num):
649                                                 subservice = event.getLinkageService(sref, cnt)
650                                                 if sref.toCompareString() == subservice.toCompareString():
651                                                         check = True
652                                                         break
653                         if check:
654                                 if x.repeated != 0:
655                                         if chktime is None:
656                                                 chktime = localtime(begin)
657                                                 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
658                                                 chktimecmp_end = chktimecmp + (duration / 60)
659                                         time = localtime(x.begin)
660                                         for y in (0, 1, 2, 3, 4, 5, 6):
661                                                 if x.repeated & (2 ** y):
662                                                         timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
663                                                         if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
664                                                                 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
665                                                         elif chktimecmp <= timecmp < chktimecmp_end:
666                                                                 time_match = (chktimecmp_end - timecmp) * 60
667                                 else: #if x.eit is None:
668                                         if begin <= x.begin <= end:
669                                                 diff = end - x.begin
670                                                 if time_match < diff:
671                                                         time_match = diff
672                                         elif x.begin <= begin <= x.end:
673                                                 diff = x.end - begin
674                                                 if time_match < diff:
675                                                         time_match = diff
676                                 if time_match:
677                                         break
678                 return time_match
679
680         def removeEntry(self, entry):
681                 print "[Timer] Remove " + str(entry)
682                 
683                 # avoid re-enqueuing
684                 entry.repeated = False
685
686                 # abort timer.
687                 # this sets the end time to current time, so timer will be stopped.
688                 entry.autoincrease = False
689                 entry.abort()
690                 
691                 if entry.state != entry.StateEnded:
692                         self.timeChanged(entry)
693                 
694                 print "state: ", entry.state
695                 print "in processed: ", entry in self.processed_timers
696                 print "in running: ", entry in self.timer_list
697                 # autoincrease instanttimer if possible
698                 if not entry.dontSave:
699                         for x in self.timer_list:
700                                 if x.setAutoincreaseEnd():
701                                         self.timeChanged(x)
702                 # now the timer should be in the processed_timers list. remove it from there.
703                 self.processed_timers.remove(entry)
704                 self.saveTimer()
705
706         def shutdown(self):
707                 self.saveTimer()