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