eDVBCAHandler: Prevent memory access of out of scope local variable
[openblackhole/openblackhole-enigma2.git] / RecordTimer.py
1 import os
2 from enigma import eEPGCache, getBestPlayableServiceReference, \
3         eServiceReference, iRecordableService, quitMainloop, eActionMap, setPreferredTuner
4
5 from Components.config import config
6 from Components.UsageConfig import defaultMoviePath
7 from Components.TimerSanityCheck import TimerSanityCheck
8
9 from Screens.MessageBox import MessageBox
10 import Screens.Standby
11 import Screens.InfoBar
12 from Tools import Directories, Notifications, ASCIItranslit, Trashcan
13 from Tools.XMLTools import stringToXML
14
15 import timer
16 import xml.etree.cElementTree
17 import NavigationInstance
18 from ServiceReference import ServiceReference
19
20 from time import localtime, strftime, ctime, time
21 from bisect import insort
22 from sys import maxint
23
24 # ok, for descriptions etc we have:
25 # service reference  (to get the service name)
26 # name               (title)
27 # description        (description)
28 # event data         (ONLY for time adjustments etc.)
29
30
31 # parses an event, and gives out a (begin, end, name, duration, eit)-tuple.
32 # begin and end will be corrected
33 def parseEvent(ev, description = True):
34         if description:
35                 name = ev.getEventName()
36                 description = ev.getShortDescription()
37                 if description == "":
38                         description = ev.getExtendedDescription()
39         else:
40                 name = ""
41                 description = ""
42         begin = ev.getBeginTime()
43         end = begin + ev.getDuration()
44         eit = ev.getEventId()
45         begin -= config.recording.margin_before.value * 60
46         end += config.recording.margin_after.value * 60
47         return (begin, end, name, description, eit)
48
49 class AFTEREVENT:
50         NONE = 0
51         STANDBY = 1
52         DEEPSTANDBY = 2
53         AUTO = 3
54
55 def findSafeRecordPath(dirname):
56         if not dirname:
57                 return None
58         from Components import Harddisk
59         dirname = os.path.realpath(dirname)
60         mountpoint = Harddisk.findMountPoint(dirname)
61         if mountpoint in ('/', '/media'):
62                 print '[RecordTimer] media is not mounted:', dirname
63                 return None
64         if not os.path.isdir(dirname):
65                 try:
66                         os.makedirs(dirname)
67                 except Exception, ex:
68                         print '[RecordTimer] Failed to create dir "%s":' % dirname, ex
69                         return None
70         return dirname
71
72 def checkForRecordings():
73         if NavigationInstance.instance.getRecordings():
74                 return True
75         rec_time = NavigationInstance.instance.RecordTimer.getNextTimerTime(isWakeup=True)
76         return rec_time > 0 and (rec_time - time()) < 360
77
78 # please do not translate log messages
79 class RecordTimerEntry(timer.TimerEntry, object):
80 ######### the following static methods and members are only in use when the box is in (soft) standby
81         wasInStandby = False
82         wasInDeepStandby = False
83         receiveRecordEvents = False
84
85         @staticmethod
86         def keypress(key=None, flag=1):
87                 if flag and (RecordTimerEntry.wasInStandby or RecordTimerEntry.wasInDeepStandby):
88                         RecordTimerEntry.wasInStandby = False
89                         RecordTimerEntry.wasInDeepStandby = False
90                         eActionMap.getInstance().unbindAction('', RecordTimerEntry.keypress)
91
92         @staticmethod
93         def setWasInDeepStandby():
94                 RecordTimerEntry.wasInDeepStandby = True
95                 eActionMap.getInstance().bindAction('', -maxint - 1, RecordTimerEntry.keypress)
96
97         @staticmethod
98         def setWasInStandby():
99                 if not RecordTimerEntry.wasInStandby:
100                         if not RecordTimerEntry.wasInDeepStandby:
101                                 eActionMap.getInstance().bindAction('', -maxint - 1, RecordTimerEntry.keypress)
102                         RecordTimerEntry.wasInDeepStandby = False
103                         RecordTimerEntry.wasInStandby = True
104
105         @staticmethod
106         def shutdown():
107                 quitMainloop(1)
108
109         @staticmethod
110         def staticGotRecordEvent(recservice, event):
111                 if event == iRecordableService.evEnd:
112                         print "RecordTimer.staticGotRecordEvent(iRecordableService.evEnd)"
113                         if not checkForRecordings():
114                                 print "No recordings busy of sceduled within 6 minutes so shutdown"
115                                 RecordTimerEntry.shutdown() # immediate shutdown
116                 elif event == iRecordableService.evStart:
117                         print "RecordTimer.staticGotRecordEvent(iRecordableService.evStart)"
118
119         @staticmethod
120         def stopTryQuitMainloop():
121                 print "RecordTimer.stopTryQuitMainloop"
122                 NavigationInstance.instance.record_event.remove(RecordTimerEntry.staticGotRecordEvent)
123                 RecordTimerEntry.receiveRecordEvents = False
124
125         @staticmethod
126         def TryQuitMainloop():
127                 if not RecordTimerEntry.receiveRecordEvents and Screens.Standby.inStandby:
128                         print "RecordTimer.TryQuitMainloop"
129                         NavigationInstance.instance.record_event.append(RecordTimerEntry.staticGotRecordEvent)
130                         RecordTimerEntry.receiveRecordEvents = True
131                         # send fake event.. to check if another recordings are running or
132                         # other timers start in a few seconds
133                         RecordTimerEntry.staticGotRecordEvent(None, iRecordableService.evEnd)
134 #################################################################
135
136         def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.AUTO, checkOldTimers = False, dirname = None, tags = None, descramble = True, record_ecm = False, always_zap = False, zap_wakeup = "always", rename_repeat = True):
137                 timer.TimerEntry.__init__(self, int(begin), int(end))
138
139                 if checkOldTimers == True:
140                         if self.begin < time() - 1209600:
141                                 self.begin = int(time())
142
143                 if self.end < self.begin:
144                         self.end = self.begin
145
146                 assert isinstance(serviceref, ServiceReference)
147
148                 if serviceref and serviceref.isRecordable():
149                         self.service_ref = serviceref
150                 else:
151                         self.service_ref = ServiceReference(None)
152                 self.eit = eit
153                 self.dontSave = False
154                 self.name = name
155                 self.description = description
156                 self.disabled = disabled
157                 self.timer = None
158                 self.__record_service = None
159                 self.start_prepare = 0
160                 self.justplay = justplay
161                 self.always_zap = always_zap
162                 self.zap_wakeup = zap_wakeup
163                 self.afterEvent = afterEvent
164                 self.dirname = dirname
165                 self.dirnameHadToFallback = False
166                 self.autoincrease = False
167                 self.autoincreasetime = 3600 * 24 # 1 day
168                 self.tags = tags or []
169                 self.descramble = descramble
170                 self.record_ecm = record_ecm
171                 self.rename_repeat = rename_repeat
172                 self.needChangePriorityFrontend = config.usage.recording_frontend_priority.value != "-2" and config.usage.recording_frontend_priority.value != config.usage.frontend_priority.value
173                 self.change_frontend = False
174                 self.InfoBarInstance = Screens.InfoBar.InfoBar.instance
175                 self.ts_dialog = None
176                 self.log_entries = []
177                 self.resetState()
178
179         def __repr__(self):
180                 return "RecordTimerEntry(name=%s, begin=%s, serviceref=%s, justplay=%s)" % (self.name, ctime(self.begin), self.service_ref, self.justplay)
181
182         def log(self, code, msg):
183                 self.log_entries.append((int(time()), code, msg))
184                 print "[TIMER]", msg
185
186         def calculateFilename(self, name=None):
187                 service_name = self.service_ref.getServiceName()
188                 begin_date = strftime("%Y%m%d %H%M", localtime(self.begin))
189                 name = name or self.name
190                 filename = begin_date + " - " + service_name
191                 if name:
192                         if config.recording.filename_composition.value == "short":
193                                 filename = strftime("%Y%m%d", localtime(self.begin)) + " - " + name
194                         elif config.recording.filename_composition.value == "long":
195                                 filename += " - " + name + " - " + self.description
196                         else:
197                                 filename += " - " + name # standard
198
199                 if config.recording.ascii_filenames.value:
200                         filename = ASCIItranslit.legacyEncode(filename)
201                 if not self.dirname:
202                         dirname = findSafeRecordPath(defaultMoviePath())
203                 else:
204                         dirname = findSafeRecordPath(self.dirname)
205                         if dirname is None:
206                                 dirname = findSafeRecordPath(defaultMoviePath())
207                                 self.dirnameHadToFallback = True
208                 if not dirname:
209                         return None
210                 self.Filename = Directories.getRecordingFilename(filename, dirname)
211                 self.log(0, "Filename calculated as: '%s'" % self.Filename)
212                 return self.Filename
213
214         def tryPrepare(self):
215                 if self.justplay:
216                         return True
217                 else:
218                         if not self.calculateFilename():
219                                 self.do_backoff()
220                                 self.start_prepare = time() + self.backoff
221                                 return False
222                         rec_ref = self.service_ref and self.service_ref.ref
223                         if rec_ref and rec_ref.flags & eServiceReference.isGroup:
224                                 rec_ref = getBestPlayableServiceReference(rec_ref, eServiceReference())
225                                 if not rec_ref:
226                                         self.log(1, "'get best playable service for group... record' failed")
227                                         return False
228                         self.setRecordingPreferredTuner()
229                         self.record_service = rec_ref and NavigationInstance.instance.recordService(rec_ref)
230
231                         if not self.record_service:
232                                 self.log(1, "'record service' failed")
233                                 self.setRecordingPreferredTuner(setdefault=True)
234                                 return False
235
236                         name = self.name
237                         description = self.description
238                         if self.repeated:
239                                 epgcache = eEPGCache.getInstance()
240                                 queryTime=self.begin+(self.end-self.begin)/2
241                                 evt = epgcache.lookupEventTime(rec_ref, queryTime)
242                                 if evt:
243                                         if self.rename_repeat:
244                                                 event_description = evt.getShortDescription()
245                                                 if not event_description:
246                                                         event_description = evt.getExtendedDescription()
247                                                 if event_description and event_description != description:
248                                                         description = event_description
249                                                 event_name = evt.getEventName()
250                                                 if event_name and event_name != name:
251                                                         name = event_name
252                                                         if not self.calculateFilename(event_name):
253                                                                 self.do_backoff()
254                                                                 self.start_prepare = time() + self.backoff
255                                                                 return False
256                                         event_id = evt.getEventId()
257                                 else:
258                                         event_id = -1
259                         else:
260                                 event_id = self.eit
261                                 if event_id is None:
262                                         event_id = -1
263
264                         prep_res=self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id, name.replace("\n", ""), description.replace("\n", ""), ' '.join(self.tags), bool(self.descramble), bool(self.record_ecm))
265                         if prep_res:
266                                 if prep_res == -255:
267                                         self.log(4, "failed to write meta information")
268                                 else:
269                                         self.log(2, "'prepare' failed: error %d" % prep_res)
270
271                                 # we must calc nur start time before stopRecordService call because in Screens/Standby.py TryQuitMainloop tries to get
272                                 # the next start time in evEnd event handler...
273                                 self.do_backoff()
274                                 self.start_prepare = time() + self.backoff
275
276                                 NavigationInstance.instance.stopRecordService(self.record_service)
277                                 self.record_service = None
278                                 self.setRecordingPreferredTuner(setdefault=True)
279                                 return False
280                         return True
281
282         def do_backoff(self):
283                 if self.backoff == 0:
284                         self.backoff = 5
285                 else:
286                         self.backoff *= 2
287                         if self.backoff > 100:
288                                 self.backoff = 100
289                 self.log(10, "backoff: retry in %d seconds" % self.backoff)
290
291         def activate(self):
292                 next_state = self.state + 1
293                 self.log(5, "activating state %d" % next_state)
294
295                 if next_state == 1:
296                         if self.always_zap:
297                                 if Screens.Standby.inStandby:
298                                         self.log(5, "wakeup and zap to recording service")
299                                         RecordTimerEntry.setWasInStandby()
300                                         #set service to zap after standby
301                                         Screens.Standby.inStandby.prev_running_service = self.service_ref.ref
302                                         Screens.Standby.inStandby.paused_service = None
303                                         #wakeup standby
304                                         Screens.Standby.inStandby.Power()
305                                 else:
306                                         if RecordTimerEntry.wasInDeepStandby:
307                                                 RecordTimerEntry.setWasInStandby()
308                                         cur_zap_ref = NavigationInstance.instance.getCurrentlyPlayingServiceReference()
309                                         if cur_zap_ref and not cur_zap_ref.getPath():# we do not zap away if it is no live service
310                                                 if self.checkingTimeshiftRunning():
311                                                         if self.ts_dialog is None:
312                                                                 self.openChoiceActionBeforeZap()
313                                                 else:
314                                                         Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20)
315                                                         self.setRecordingPreferredTuner()
316                                                         self.failureCB(True)
317                                                         self.log(5, "zap to recording service")
318
319                 if next_state == self.StatePrepared:
320                         if self.tryPrepare():
321                                 self.log(6, "prepare ok, waiting for begin")
322                                 # create file to "reserve" the filename
323                                 # because another recording at the same time on another service can try to record the same event
324                                 # i.e. cable / sat.. then the second recording needs an own extension... when we create the file
325                                 # here than calculateFilename is happy
326                                 if not self.justplay:
327                                         open(self.Filename + ".ts", "w").close()
328                                         # Give the Trashcan a chance to clean up
329                                         try:
330                                                 Trashcan.instance.cleanIfIdle(self.Filename)
331                                         except Exception, e:
332                                                  print "[TIMER] Failed to call Trashcan.instance.cleanIfIdle()"
333                                                  print "[TIMER] Error:", e
334                                 # fine. it worked, resources are allocated.
335                                 self.next_activation = self.begin
336                                 self.backoff = 0
337                                 return True
338
339                         self.log(7, "prepare failed")
340                         if self.first_try_prepare or (self.ts_dialog is not None and not self.checkingTimeshiftRunning()):
341                                 self.first_try_prepare = False
342                                 cur_ref = NavigationInstance.instance.getCurrentlyPlayingServiceReference()
343                                 if cur_ref and not cur_ref.getPath():
344                                         if self.always_zap:
345                                                 return False
346                                         if Screens.Standby.inStandby:
347                                                 self.setRecordingPreferredTuner()
348                                                 self.failureCB(True)
349                                         elif self.checkingTimeshiftRunning():
350                                                 if self.ts_dialog is None:
351                                                         self.openChoiceActionBeforeZap()
352                                         elif not config.recording.asktozap.value:
353                                                 self.log(8, "asking user to zap away")
354                                                 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"), timeout=20, default=True)
355                                         else: # zap without asking
356                                                 self.log(9, "zap without asking")
357                                                 Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20)
358                                                 self.setRecordingPreferredTuner()
359                                                 self.failureCB(True)
360                                 elif cur_ref:
361                                         self.log(8, "currently running service is not a live service.. so stop it makes no sense")
362                                 else:
363                                         self.log(8, "currently no service running... so we dont need to stop it")
364                         return False
365
366                 elif next_state == self.StateRunning:
367                         # if this timer has been cancelled, just go to "end" state.
368                         if self.cancelled:
369                                 return True
370                         if self.justplay:
371                                 if Screens.Standby.inStandby:
372                                         if RecordTimerEntry.wasInDeepStandby and self.zap_wakeup in ("always", "from_deep_standby") or self.zap_wakeup in ("always", "from_standby"):
373                                                 self.log(11, "wakeup and zap")
374                                                 RecordTimerEntry.setWasInStandby()
375                                                 #set service to zap after standby
376                                                 Screens.Standby.inStandby.prev_running_service = self.service_ref.ref
377                                                 Screens.Standby.inStandby.paused_service = None
378                                                 #wakeup standby
379                                                 Screens.Standby.inStandby.Power()
380                                 else:
381                                         if RecordTimerEntry.wasInDeepStandby:
382                                                 RecordTimerEntry.setWasInStandby()
383                                         if self.checkingTimeshiftRunning():
384                                                 if self.ts_dialog is None:
385                                                         self.openChoiceActionBeforeZap()
386                                         else:
387                                                 self.log(11, "zapping")
388                                                 NavigationInstance.instance.playService(self.service_ref.ref)
389                                 return True
390                         else:
391                                 self.log(11, "start recording")
392
393                                 if RecordTimerEntry.wasInDeepStandby:
394                                         RecordTimerEntry.keypress()
395                                         if Screens.Standby.inStandby: #In case some plugin did put the receiver already in standby
396                                                 config.misc.standbyCounter.value = 0
397                                         else:
398                                                 Notifications.AddNotification(Screens.Standby.Standby, StandbyCounterIncrease=False)
399                                 record_res = self.record_service.start()
400                                 self.setRecordingPreferredTuner(setdefault=True)
401                                 if record_res:
402                                         self.log(13, "start record returned %d" % record_res)
403                                         self.do_backoff()
404                                         # retry
405                                         self.begin = time() + self.backoff
406                                         return False
407
408                                 # Tell the trashcan we started recording. The trashcan gets events,
409                                 # but cannot tell what the associated path is.
410                                 Trashcan.instance.markDirty(self.Filename)
411
412                                 return True
413
414                 elif next_state == self.StateEnded:
415                         old_end = self.end
416                         self.ts_dialog = None
417                         if self.setAutoincreaseEnd():
418                                 self.log(12, "autoincrase recording %d minute(s)" % int((self.end - old_end)/60))
419                                 self.state -= 1
420                                 return True
421                         self.log(12, "stop recording")
422                         if not self.justplay:
423                                 NavigationInstance.instance.stopRecordService(self.record_service)
424                                 self.record_service = None
425                         if not checkForRecordings():
426                                 if self.afterEvent == AFTEREVENT.DEEPSTANDBY or self.afterEvent == AFTEREVENT.AUTO and (Screens.Standby.inStandby or RecordTimerEntry.wasInStandby) and not config.misc.standbyCounter.value:
427                                         if not Screens.Standby.inTryQuitMainloop:
428                                                 if Screens.Standby.inStandby:
429                                                         RecordTimerEntry.TryQuitMainloop()
430                                                 else:
431                                                         Notifications.AddNotificationWithCallback(self.sendTryQuitMainloopNotification, MessageBox, _("A finished record timer wants to shut down\nyour receiver. Shutdown now?"), timeout=20, default=True)
432                                 elif self.afterEvent == AFTEREVENT.STANDBY or self.afterEvent == AFTEREVENT.AUTO and RecordTimerEntry.wasInStandby:
433                                         if not Screens.Standby.inStandby:
434                                                 Notifications.AddNotificationWithCallback(self.sendStandbyNotification, MessageBox, _("A finished record timer wants to set your\nreceiver to standby. Do that now?"), timeout=20, default=True)
435                                 else:
436                                         RecordTimerEntry.keypress()
437                         return True
438
439         def setAutoincreaseEnd(self, entry = None):
440                 if not self.autoincrease:
441                         return False
442                 if entry is None:
443                         new_end =  int(time()) + self.autoincreasetime
444                 else:
445                         new_end = entry.begin - 30
446
447                 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)
448                 dummyentry.disabled = self.disabled
449                 timersanitycheck = TimerSanityCheck(NavigationInstance.instance.RecordTimer.timer_list, dummyentry)
450                 if not timersanitycheck.check():
451                         simulTimerList = timersanitycheck.getSimulTimerList()
452                         if simulTimerList is not None and len(simulTimerList) > 1:
453                                 new_end = simulTimerList[1].begin
454                                 new_end -= 30                           # 30 Sekunden Prepare-Zeit lassen
455                 if new_end <= time():
456                         return False
457                 self.end = new_end
458                 return True
459
460         def setRecordingPreferredTuner(self, setdefault=False):
461                 if self.needChangePriorityFrontend:
462                         elem = None
463                         if not self.change_frontend and not setdefault:
464                                 elem = config.usage.recording_frontend_priority.value
465                                 self.change_frontend = True
466                         elif self.change_frontend and setdefault:
467                                 elem = config.usage.frontend_priority.value
468                                 self.change_frontend = False
469                         if elem is not None:
470                                 setPreferredTuner(int(elem))
471
472         def checkingTimeshiftRunning(self):
473                 return config.usage.check_timeshift.value and self.InfoBarInstance and self.InfoBarInstance.timeshiftEnabled() and self.InfoBarInstance.timeshift_was_activated
474
475         def openChoiceActionBeforeZap(self):
476                 if self.ts_dialog is None:
477                         type = _("record")
478                         if self.justplay:
479                                 type = _("zap")
480                         elif self.always_zap:
481                                 type = _("zap and record")
482                         message = _("You must switch to the service %s (%s - '%s')!\n") % (type, self.service_ref.getServiceName(), self.name)
483                         if self.repeated:
484                                 message += _("Attention, this is repeated timer!\n")
485                         message += _("Timeshift is running. Select an action.\n")
486                         choice = [(_("Zap"), "zap"), (_("Don't zap and disable timer"), "disable"), (_("Don't zap and remove timer"), "remove")]
487                         if not self.InfoBarInstance.save_timeshift_file:
488                                 choice.insert(1, (_("Save timeshift in movie dir and zap"), "save_movie"))
489                                 if self.InfoBarInstance.timeshiftActivated():
490                                         choice.insert(0, (_("Save timeshift and zap"), "save"))
491                                 else:
492                                         choice.insert(1, (_("Save timeshift and zap"), "save"))
493                         else:
494                                 message += _("Reminder, you have chosen to save timeshift file.")
495                         #if self.justplay or self.always_zap:
496                         #       choice.insert(2, (_("Don't zap"), "continue"))
497                         choice.insert(2, (_("Don't zap"), "continue"))
498                         def zapAction(choice):
499                                 start_zap = True
500                                 if choice:
501                                         if choice in ("zap", "save", "save_movie"):
502                                                 self.log(8, "zap to recording service")
503                                                 if choice in ("save", "save_movie"):
504                                                         ts = self.InfoBarInstance.getTimeshift()
505                                                         if ts and ts.isTimeshiftEnabled():
506                                                                 if choice =="save_movie":
507                                                                         self.InfoBarInstance.save_timeshift_in_movie_dir = True
508                                                                 self.InfoBarInstance.save_timeshift_file = True
509                                                                 ts.saveTimeshiftFile()
510                                                                 del ts
511                                                                 self.InfoBarInstance.saveTimeshiftFiles()
512                                         elif choice == "disable":
513                                                 self.disable()
514                                                 NavigationInstance.instance.RecordTimer.timeChanged(self)
515                                                 start_zap = False
516                                                 self.log(8, "zap canceled by the user, timer disabled")
517                                         elif choice == "remove":
518                                                 start_zap = False
519                                                 self.afterEvent = AFTEREVENT.NONE
520                                                 NavigationInstance.instance.RecordTimer.removeEntry(self)
521                                                 self.log(8, "zap canceled by the user, timer removed")
522                                         elif choice == "continue":
523                                                 if self.justplay:
524                                                         self.end = self.begin
525                                                 start_zap = False
526                                                 self.log(8, "zap canceled by the user")
527                                 if start_zap:
528                                         if not self.justplay:
529                                                 self.setRecordingPreferredTuner()
530                                                 self.failureCB(True)
531                                         else:
532                                                 self.log(8, "zapping")
533                                                 NavigationInstance.instance.playService(self.service_ref.ref)
534                         self.ts_dialog = self.InfoBarInstance.session.openWithCallback(zapAction, MessageBox, message, simple=True, list=choice, timeout=20)
535
536         def sendStandbyNotification(self, answer):
537                 RecordTimerEntry.keypress()
538                 if answer:
539                         Notifications.AddNotification(Screens.Standby.Standby)
540
541         def sendTryQuitMainloopNotification(self, answer):
542                 RecordTimerEntry.keypress()
543                 if answer:
544                         Notifications.AddNotification(Screens.Standby.TryQuitMainloop, 1)
545
546         def getNextActivation(self):
547                 if self.state == self.StateEnded:
548                         return self.end
549
550                 next_state = self.state + 1
551
552                 return {self.StatePrepared: self.start_prepare,
553                                 self.StateRunning: self.begin,
554                                 self.StateEnded: self.end }[next_state]
555
556         def failureCB(self, answer):
557                 self.ts_dialog = None
558                 if answer == True:
559                         self.log(13, "ok, zapped away")
560                         #NavigationInstance.instance.stopUserServices()
561                         NavigationInstance.instance.playService(self.service_ref.ref)
562                 else:
563                         self.log(14, "user didn't want to zap away, record will probably fail")
564
565         def timeChanged(self):
566                 old_prepare = self.start_prepare
567                 self.start_prepare = self.begin - self.prepare_time
568                 self.backoff = 0
569
570                 if int(old_prepare) != int(self.start_prepare):
571                         self.log(15, "record time changed, start prepare is now: %s" % ctime(self.start_prepare))
572
573         def gotRecordEvent(self, record, event):
574                 # TODO: this is not working (never true), please fix. (comparing two swig wrapped ePtrs)
575                 if self.__record_service.__deref__() != record.__deref__():
576                         return
577                 self.log(16, "record event %d" % event)
578                 if event == iRecordableService.evRecordWriteError:
579                         print "WRITE ERROR on recording, disk full?"
580                         # show notification. the 'id' will make sure that it will be
581                         # displayed only once, even if more timers are failing at the
582                         # same time. (which is very likely in case of disk fullness)
583                         Notifications.AddPopup(text = _("Write error while recording. Disk full?\n"), type = MessageBox.TYPE_ERROR, timeout = 0, id = "DiskFullMessage")
584                         # ok, the recording has been stopped. we need to properly note
585                         # that in our state, with also keeping the possibility to re-try.
586                         # TODO: this has to be done.
587                 elif event == iRecordableService.evStart:
588                         text = _("A record has been started:\n%s") % self.name
589                         notify = config.usage.show_message_when_recording_starts.value and not Screens.Standby.inStandby and self.InfoBarInstance and self.InfoBarInstance.execing
590                         if self.dirnameHadToFallback:
591                                 text = '\n'.join((text, _("Please note that the previously selected media could not be accessed and therefore the default directory is being used instead.")))
592                                 notify = True
593                         if notify:
594                                 Notifications.AddPopup(text = text, type = MessageBox.TYPE_INFO, timeout = 3)
595                 elif event == iRecordableService.evRecordAborted:
596                         NavigationInstance.instance.RecordTimer.removeEntry(self)
597
598         # we have record_service as property to automatically subscribe to record service events
599         def setRecordService(self, service):
600                 if self.__record_service is not None:
601                         print "[remove callback]"
602                         NavigationInstance.instance.record_event.remove(self.gotRecordEvent)
603
604                 self.__record_service = service
605
606                 if self.__record_service is not None:
607                         print "[add callback]"
608                         NavigationInstance.instance.record_event.append(self.gotRecordEvent)
609
610         record_service = property(lambda self: self.__record_service, setRecordService)
611
612 def createTimer(xml):
613         begin = int(xml.get("begin"))
614         end = int(xml.get("end"))
615         serviceref = ServiceReference(xml.get("serviceref").encode("utf-8"))
616         description = xml.get("description").encode("utf-8")
617         repeated = xml.get("repeated").encode("utf-8")
618         rename_repeat = long(xml.get("rename_repeat") or "1")
619         disabled = long(xml.get("disabled") or "0")
620         justplay = long(xml.get("justplay") or "0")
621         always_zap = long(xml.get("always_zap") or "0")
622         zap_wakeup = str(xml.get("zap_wakeup") or "always")
623         afterevent = str(xml.get("afterevent") or "nothing")
624         afterevent = {
625                 "nothing": AFTEREVENT.NONE,
626                 "standby": AFTEREVENT.STANDBY,
627                 "deepstandby": AFTEREVENT.DEEPSTANDBY,
628                 "auto": AFTEREVENT.AUTO
629                 }[afterevent]
630         eit = xml.get("eit")
631         if eit and eit != "None":
632                 eit = long(eit)
633         else:
634                 eit = None
635         location = xml.get("location")
636         if location and location != "None":
637                 location = location.encode("utf-8")
638         else:
639                 location = None
640         tags = xml.get("tags")
641         if tags and tags != "None":
642                 tags = tags.encode("utf-8").split(' ')
643         else:
644                 tags = None
645         descramble = int(xml.get("descramble") or "1")
646         record_ecm = int(xml.get("record_ecm") or "0")
647
648         name = xml.get("name").encode("utf-8")
649         #filename = xml.get("filename").encode("utf-8")
650         entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent, dirname = location, tags = tags, descramble = descramble, record_ecm = record_ecm, always_zap = always_zap, zap_wakeup = zap_wakeup, rename_repeat = rename_repeat)
651         entry.repeated = int(repeated)
652
653         for l in xml.findall("log"):
654                 time = int(l.get("time"))
655                 code = int(l.get("code"))
656                 msg = l.text.strip().encode("utf-8")
657                 entry.log_entries.append((time, code, msg))
658
659         return entry
660
661 class RecordTimer(timer.Timer):
662         def __init__(self):
663                 timer.Timer.__init__(self)
664
665                 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
666
667                 try:
668                         self.loadTimer()
669                 except IOError:
670                         print "unable to load timers from file!"
671
672         def doActivate(self, w):
673                 # when activating a timer which has already passed,
674                 # simply abort the timer. don't run trough all the stages.
675                 if w.shouldSkip():
676                         w.state = RecordTimerEntry.StateEnded
677                 else:
678                         # when active returns true, this means "accepted".
679                         # otherwise, the current state is kept.
680                         # the timer entry itself will fix up the delay then.
681                         if w.activate():
682                                 w.state += 1
683
684                 self.timer_list.remove(w)
685
686                 # did this timer reached the last state?
687                 if w.state < RecordTimerEntry.StateEnded:
688                         # no, sort it into active list
689                         insort(self.timer_list, w)
690                 else:
691                         # yes. Process repeated, and re-add.
692                         if w.repeated:
693                                 w.processRepeated()
694                                 w.state = RecordTimerEntry.StateWaiting
695                                 w.first_try_prepare = True
696                                 self.addTimerEntry(w)
697                         else:
698                                 # Remove old timers as set in config
699                                 self.cleanupDaily(config.recording.keep_timers.value)
700                                 insort(self.processed_timers, w)
701                 self.stateChanged(w)
702
703         def isRecording(self):
704                 for timer in self.timer_list:
705                         if timer.isRunning() and not timer.justplay:
706                                 return True
707                 return False
708
709         def loadTimer(self):
710                 # TODO: PATH!
711                 if not Directories.fileExists(self.Filename):
712                         return
713                 try:
714                         doc = xml.etree.cElementTree.parse(self.Filename)
715                 except SyntaxError:
716                         from Tools.Notifications import AddPopup
717                         from Screens.MessageBox import MessageBox
718
719                         AddPopup(_("The timer file (timers.xml) is corrupt and could not be loaded."), type = MessageBox.TYPE_ERROR, timeout = 0, id = "TimerLoadFailed")
720
721                         print "timers.xml failed to load!"
722                         try:
723                                 import os
724                                 os.rename(self.Filename, self.Filename + "_old")
725                         except (IOError, OSError):
726                                 print "renaming broken timer failed"
727                         return
728                 except IOError:
729                         print "timers.xml not found!"
730                         return
731
732                 root = doc.getroot()
733
734                 # put out a message when at least one timer overlaps
735                 checkit = True
736                 for timer in root.findall("timer"):
737                         newTimer = createTimer(timer)
738                         if (self.record(newTimer, True, dosave=False) is not None) and (checkit == True):
739                                 from Tools.Notifications import AddPopup
740                                 from Screens.MessageBox import MessageBox
741                                 AddPopup(_("Timer overlap in timers.xml detected!\nPlease recheck it!"), type = MessageBox.TYPE_ERROR, timeout = 0, id = "TimerLoadFailed")
742                                 checkit = False # at moment it is enough when the message is displayed one time
743
744         def saveTimer(self):
745                 #root_element = xml.etree.cElementTree.Element('timers')
746                 #root_element.text = "\n"
747
748                 #for timer in self.timer_list + self.processed_timers:
749                         # some timers (instant records) don't want to be saved.
750                         # skip them
751                         #if timer.dontSave:
752                                 #continue
753                         #t = xml.etree.cElementTree.SubElement(root_element, 'timers')
754                         #t.set("begin", str(int(timer.begin)))
755                         #t.set("end", str(int(timer.end)))
756                         #t.set("serviceref", str(timer.service_ref))
757                         #t.set("repeated", str(timer.repeated))
758                         #t.set("name", timer.name)
759                         #t.set("description", timer.description)
760                         #t.set("afterevent", str({
761                         #       AFTEREVENT.NONE: "nothing",
762                         #       AFTEREVENT.STANDBY: "standby",
763                         #       AFTEREVENT.DEEPSTANDBY: "deepstandby",
764                         #       AFTEREVENT.AUTO: "auto"}))
765                         #if timer.eit is not None:
766                         #       t.set("eit", str(timer.eit))
767                         #if timer.dirname is not None:
768                         #       t.set("location", str(timer.dirname))
769                         #t.set("disabled", str(int(timer.disabled)))
770                         #t.set("justplay", str(int(timer.justplay)))
771                         #t.text = "\n"
772                         #t.tail = "\n"
773
774                         #for time, code, msg in timer.log_entries:
775                                 #l = xml.etree.cElementTree.SubElement(t, 'log')
776                                 #l.set("time", str(time))
777                                 #l.set("code", str(code))
778                                 #l.text = str(msg)
779                                 #l.tail = "\n"
780
781                 #doc = xml.etree.cElementTree.ElementTree(root_element)
782                 #doc.write(self.Filename)
783
784                 list = []
785
786                 list.append('<?xml version="1.0" ?>\n')
787                 list.append('<timers>\n')
788
789                 for timer in self.timer_list + self.processed_timers:
790                         if timer.dontSave:
791                                 continue
792
793                         list.append('<timer')
794                         list.append(' begin="' + str(int(timer.begin)) + '"')
795                         list.append(' end="' + str(int(timer.end)) + '"')
796                         list.append(' serviceref="' + stringToXML(str(timer.service_ref)) + '"')
797                         list.append(' repeated="' + str(int(timer.repeated)) + '"')
798                         list.append(' name="' + str(stringToXML(timer.name)) + '"')
799                         list.append(' description="' + str(stringToXML(timer.description)) + '"')
800                         list.append(' afterevent="' + str(stringToXML({
801                                 AFTEREVENT.NONE: "nothing",
802                                 AFTEREVENT.STANDBY: "standby",
803                                 AFTEREVENT.DEEPSTANDBY: "deepstandby",
804                                 AFTEREVENT.AUTO: "auto"
805                                 }[timer.afterEvent])) + '"')
806                         if timer.eit is not None:
807                                 list.append(' eit="' + str(timer.eit) + '"')
808                         if timer.dirname is not None:
809                                 list.append(' location="' + str(stringToXML(timer.dirname)) + '"')
810                         if timer.tags is not None:
811                                 list.append(' tags="' + str(stringToXML(' '.join(timer.tags))) + '"')
812                         list.append(' disabled="' + str(int(timer.disabled)) + '"')
813                         list.append(' justplay="' + str(int(timer.justplay)) + '"')
814                         list.append(' always_zap="' + str(int(timer.always_zap)) + '"')
815                         list.append(' zap_wakeup="' + str(timer.zap_wakeup) + '"')
816                         list.append(' rename_repeat="' + str(int(timer.rename_repeat)) + '"')
817                         list.append(' descramble="' + str(int(timer.descramble)) + '"')
818                         list.append(' record_ecm="' + str(int(timer.record_ecm)) + '"')
819                         list.append('>\n')
820
821                         if config.recording.debug.value:
822                                 for time, code, msg in timer.log_entries:
823                                         list.append('<log')
824                                         list.append(' code="' + str(code) + '"')
825                                         list.append(' time="' + str(time) + '"')
826                                         list.append('>')
827                                         list.append(str(stringToXML(msg)))
828                                         list.append('</log>\n')
829
830                         list.append('</timer>\n')
831
832                 list.append('</timers>\n')
833
834                 file = open(self.Filename + ".writing", "w")
835                 for x in list:
836                         file.write(x)
837                 file.flush()
838
839                 import os
840                 os.fsync(file.fileno())
841                 file.close()
842                 os.rename(self.Filename + ".writing", self.Filename)
843
844         def getNextZapTime(self, isWakeup=False):
845                 now = time()
846                 for timer in self.timer_list:
847                         if not timer.justplay or timer.begin < now or isWakeup and timer.zap_wakeup in ("from_standby", "never"):
848                                 continue
849                         return timer.begin
850                 return -1
851
852         def getNextRecordingTime(self):
853                 now = time()
854                 for timer in self.timer_list:
855                         next_act = timer.getNextActivation()
856                         if timer.justplay or next_act < now:
857                                 continue
858                         return next_act
859                 return -1
860
861         def getNextTimerTime(self, isWakeup=False):
862                 now = time()
863                 for timer in self.timer_list:
864                         next_act = timer.getNextActivation()
865                         if next_act < now or isWakeup and timer.justplay and timer.zap_wakeup in ("from_standby", "never"):
866                                 continue
867                         return next_act
868                 return -1
869
870         def isNextRecordAfterEventActionAuto(self):
871                 now = time()
872                 t = None
873                 for timer in self.timer_list:
874                         if timer.justplay or timer.begin < now:
875                                 continue
876                         if t is None or t.begin == timer.begin:
877                                 t = timer
878                                 if t.afterEvent == AFTEREVENT.AUTO:
879                                         return True
880                 return False
881
882         def record(self, entry, ignoreTSC=False, dosave=True): # wird von loadTimer mit dosave=False aufgerufen
883                 timersanitycheck = TimerSanityCheck(self.timer_list,entry)
884                 if not timersanitycheck.check():
885                         if ignoreTSC != True:
886                                 print "timer conflict detected!"
887                                 print timersanitycheck.getSimulTimerList()
888                                 return timersanitycheck.getSimulTimerList()
889                         else:
890                                 print "ignore timer conflict"
891                 elif timersanitycheck.doubleCheck():
892                         print "ignore double timer"
893                         return None
894                 entry.timeChanged()
895                 print "[Timer] Record " + str(entry)
896                 entry.Timer = self
897                 self.addTimerEntry(entry)
898                 if dosave:
899                         self.saveTimer()
900                 return None
901
902         def isInRepeatTimer(self, timer, event):
903                 time_match = 0
904                 is_editable = False
905                 begin = event.getBeginTime()
906                 duration = event.getDuration()
907                 end = begin + duration
908                 timer_end = timer.end
909                 if timer.disabled and timer.isRunning():
910                         if begin < timer.begin <= end or timer.begin <= begin <= timer_end:
911                                 return True
912                         else:
913                                 return False
914                 if timer.justplay and (timer_end - timer.begin) <= 1:
915                         timer_end += 60
916                 bt = localtime(begin)
917                 bday = bt.tm_wday
918                 begin2 = 1440 + bt.tm_hour * 60 + bt.tm_min
919                 end2 = begin2 + duration / 60
920                 xbt = localtime(timer.begin)
921                 xet = localtime(timer_end)
922                 offset_day = False
923                 checking_time = timer.begin < begin or begin <= timer.begin <= end
924                 if xbt.tm_yday != xet.tm_yday:
925                         oday = bday - 1
926                         if oday == -1: oday = 6
927                         offset_day = timer.repeated & (1 << oday)
928                 xbegin = 1440 + xbt.tm_hour * 60 + xbt.tm_min
929                 xend = xbegin + ((timer_end - timer.begin) / 60)
930                 if xend < xbegin:
931                         xend += 1440
932                 if timer.repeated & (1 << bday) and checking_time:
933                         if begin2 < xbegin <= end2:
934                                 if xend < end2:
935                                         # recording within event
936                                         time_match = (xend - xbegin) * 60
937                                         is_editable = True
938                                 else:
939                                         # recording last part of event
940                                         time_match = (end2 - xbegin) * 60
941                                         summary_end = (xend - end2) * 60
942                                         is_editable = not summary_end and True or time_match >= summary_end
943                         elif xbegin <= begin2 <= xend:
944                                 if xend < end2:
945                                         # recording first part of event
946                                         time_match = (xend - begin2) * 60
947                                         summary_end = (begin2 - xbegin) * 60
948                                         is_editable = not summary_end and True or time_match >= summary_end
949                                 else:
950                                         # recording whole event
951                                         time_match = (end2 - begin2) * 60
952                                         is_editable = True
953                         elif offset_day:
954                                 xbegin -= 1440
955                                 xend -= 1440
956                                 if begin2 < xbegin <= end2:
957                                         if xend < end2:
958                                                 # recording within event
959                                                 time_match = (xend - xbegin) * 60
960                                                 is_editable = True
961                                         else:
962                                                 # recording last part of event
963                                                 time_match = (end2 - xbegin) * 60
964                                                 summary_end = (xend - end2) * 60
965                                                 is_editable = not summary_end and True or time_match >= summary_end
966                                 elif xbegin <= begin2 <= xend:
967                                         if xend < end2:
968                                                 # recording first part of event
969                                                 time_match = (xend - begin2) * 60
970                                                 summary_end = (begin2 - xbegin) * 60
971                                                 is_editable = not summary_end and True or time_match >= summary_end
972                                         else:
973                                                 # recording whole event
974                                                 time_match = (end2 - begin2) * 60
975                                                 is_editable = True
976                 elif offset_day and checking_time:
977                         xbegin -= 1440
978                         xend -= 1440
979                         if begin2 < xbegin <= end2:
980                                 if xend < end2:
981                                         # recording within event
982                                         time_match = (xend - xbegin) * 60
983                                         is_editable = True
984                                 else:
985                                         # recording last part of event
986                                         time_match = (end2 - xbegin) * 60
987                                         summary_end = (xend - end2) * 60
988                                         is_editable = not summary_end and True or time_match >= summary_end
989                         elif xbegin <= begin2 <= xend:
990                                 if xend < end2:
991                                         # recording first part of event
992                                         time_match = (xend - begin2) * 60
993                                         summary_end = (begin2 - xbegin) * 60
994                                         is_editable = not summary_end and True or time_match >= summary_end
995                                 else:
996                                         # recording whole event
997                                         time_match = (end2 - begin2) * 60
998                                         is_editable = True
999                 return time_match and is_editable
1000
1001         def isInTimer(self, eventid, begin, duration, service):
1002                 returnValue = None
1003                 type = 0
1004                 time_match = 0
1005                 bt = None
1006                 check_offset_time = not config.recording.margin_before.value and not config.recording.margin_after.value
1007                 end = begin + duration
1008                 refstr = ':'.join(service.split(':')[:11])
1009                 for x in self.timer_list:
1010                         check = ':'.join(x.service_ref.ref.toString().split(':')[:11]) == refstr
1011                         if not check:
1012                                 sref = x.service_ref.ref
1013                                 parent_sid = sref.getUnsignedData(5)
1014                                 parent_tsid = sref.getUnsignedData(6)
1015                                 if parent_sid and parent_tsid:
1016                                         # check for subservice
1017                                         sid = sref.getUnsignedData(1)
1018                                         tsid = sref.getUnsignedData(2)
1019                                         sref.setUnsignedData(1, parent_sid)
1020                                         sref.setUnsignedData(2, parent_tsid)
1021                                         sref.setUnsignedData(5, 0)
1022                                         sref.setUnsignedData(6, 0)
1023                                         check = sref.toCompareString() == refstr
1024                                         num = 0
1025                                         if check:
1026                                                 check = False
1027                                                 event = eEPGCache.getInstance().lookupEventId(sref, eventid)
1028                                                 num = event and event.getNumOfLinkageServices() or 0
1029                                         sref.setUnsignedData(1, sid)
1030                                         sref.setUnsignedData(2, tsid)
1031                                         sref.setUnsignedData(5, parent_sid)
1032                                         sref.setUnsignedData(6, parent_tsid)
1033                                         for cnt in range(num):
1034                                                 subservice = event.getLinkageService(sref, cnt)
1035                                                 if sref.toCompareString() == subservice.toCompareString():
1036                                                         check = True
1037                                                         break
1038                         if check:
1039                                 timer_end = x.end
1040                                 timer_begin = x.begin
1041                                 type_offset = 0
1042                                 if not x.repeated and check_offset_time:
1043                                         if 0 < end - timer_end <= 59:
1044                                                 timer_end = end
1045                                         elif 0 < timer_begin - begin <= 59:
1046                                                 timer_begin = begin
1047                                 if x.justplay:
1048                                         type_offset = 5
1049                                         if (timer_end - x.begin) <= 1:
1050                                                 timer_end += 60
1051                                 if x.always_zap:
1052                                         type_offset = 10
1053
1054                                 timer_repeat = x.repeated
1055                                 # if set 'don't stop current event but disable coming events' for repeat timer
1056                                 running_only_curevent = x.disabled and x.isRunning() and timer_repeat
1057                                 if running_only_curevent:
1058                                         timer_repeat = 0
1059                                         type_offset += 15
1060
1061                                 if timer_repeat != 0:
1062                                         type_offset += 15
1063                                         if bt is None:
1064                                                 bt = localtime(begin)
1065                                                 bday = bt.tm_wday
1066                                                 begin2 = 1440 + bt.tm_hour * 60 + bt.tm_min
1067                                                 end2 = begin2 + duration / 60
1068                                         xbt = localtime(x.begin)
1069                                         xet = localtime(timer_end)
1070                                         offset_day = False
1071                                         checking_time = x.begin < begin or begin <= x.begin <= end
1072                                         if xbt.tm_yday != xet.tm_yday:
1073                                                 oday = bday - 1
1074                                                 if oday == -1: oday = 6
1075                                                 offset_day = x.repeated & (1 << oday)
1076                                         xbegin = 1440 + xbt.tm_hour * 60 + xbt.tm_min
1077                                         xend = xbegin + ((timer_end - x.begin) / 60)
1078                                         if xend < xbegin:
1079                                                 xend += 1440
1080                                         if x.repeated & (1 << bday) and checking_time:
1081                                                 if begin2 < xbegin <= end2:
1082                                                         if xend < end2:
1083                                                                 # recording within event
1084                                                                 time_match = (xend - xbegin) * 60
1085                                                                 type = type_offset + 3
1086                                                         else:
1087                                                                 # recording last part of event
1088                                                                 time_match = (end2 - xbegin) * 60
1089                                                                 type = type_offset + 1
1090                                                 elif xbegin <= begin2 <= xend:
1091                                                         if xend < end2:
1092                                                                 # recording first part of event
1093                                                                 time_match = (xend - begin2) * 60
1094                                                                 type = type_offset + 4
1095                                                         else:
1096                                                                 # recording whole event
1097                                                                 time_match = (end2 - begin2) * 60
1098                                                                 type = type_offset + 2
1099                                                 elif offset_day:
1100                                                         xbegin -= 1440
1101                                                         xend -= 1440
1102                                                         if begin2 < xbegin <= end2:
1103                                                                 if xend < end2:
1104                                                                         # recording within event
1105                                                                         time_match = (xend - xbegin) * 60
1106                                                                         type = type_offset + 3
1107                                                                 else:
1108                                                                         # recording last part of event
1109                                                                         time_match = (end2 - xbegin) * 60
1110                                                                         type = type_offset + 1
1111                                                         elif xbegin <= begin2 <= xend:
1112                                                                 if xend < end2:
1113                                                                         # recording first part of event
1114                                                                         time_match = (xend - begin2) * 60
1115                                                                         type = type_offset + 4
1116                                                                 else:
1117                                                                         # recording whole event
1118                                                                         time_match = (end2 - begin2) * 60
1119                                                                         type = type_offset + 2
1120                                         elif offset_day and checking_time:
1121                                                 xbegin -= 1440
1122                                                 xend -= 1440
1123                                                 if begin2 < xbegin <= end2:
1124                                                         if xend < end2:
1125                                                                 # recording within event
1126                                                                 time_match = (xend - xbegin) * 60
1127                                                                 type = type_offset + 3
1128                                                         else:
1129                                                                 # recording last part of event
1130                                                                 time_match = (end2 - xbegin) * 60
1131                                                                 type = type_offset + 1
1132                                                 elif xbegin <= begin2 <= xend:
1133                                                         if xend < end2:
1134                                                                 # recording first part of event
1135                                                                 time_match = (xend - begin2) * 60
1136                                                                 type = type_offset + 4
1137                                                         else:
1138                                                                 # recording whole event
1139                                                                 time_match = (end2 - begin2) * 60
1140                                                                 type = type_offset + 2
1141                                 else:
1142                                         if begin < timer_begin <= end:
1143                                                 if timer_end < end:
1144                                                         # recording within event
1145                                                         time_match = timer_end - timer_begin
1146                                                         type = type_offset + 3
1147                                                 else:
1148                                                         # recording last part of event
1149                                                         time_match = end - timer_begin
1150                                                         type = type_offset + 1
1151                                         elif timer_begin <= begin <= timer_end:
1152                                                 if timer_end < end:
1153                                                         # recording first part of event
1154                                                         time_match = timer_end - begin
1155                                                         type = type_offset + 4
1156                                                 else:
1157                                                         # recording whole event
1158                                                         time_match = end - begin
1159                                                         type = type_offset + 2
1160                                 if time_match:
1161                                         if type in (2,7,12,17,22,27):
1162                                                 # When full recording do not look further
1163                                                 returnValue = (time_match, [type])
1164                                                 break
1165                                         elif returnValue:
1166                                                 if type not in returnValue[1]:
1167                                                         returnValue[1].append(type)
1168                                         else:
1169                                                 returnValue = (time_match, [type])
1170
1171                 return returnValue
1172
1173         def removeEntry(self, entry):
1174                 print "[Timer] Remove " + str(entry)
1175
1176                 # avoid re-enqueuing
1177                 entry.repeated = False
1178
1179                 # abort timer.
1180                 # this sets the end time to current time, so timer will be stopped.
1181                 entry.autoincrease = False
1182                 entry.abort()
1183
1184                 if entry.state != entry.StateEnded:
1185                         self.timeChanged(entry)
1186
1187                 print "state: ", entry.state
1188                 print "in processed: ", entry in self.processed_timers
1189                 print "in running: ", entry in self.timer_list
1190                 # autoincrease instanttimer if possible
1191                 if not entry.dontSave:
1192                         for x in self.timer_list:
1193                                 if x.setAutoincreaseEnd():
1194                                         self.timeChanged(x)
1195                 # now the timer should be in the processed_timers list. remove it from there.
1196                 self.processed_timers.remove(entry)
1197                 self.saveTimer()
1198
1199         def shutdown(self):
1200                 self.saveTimer()