Update Latvian translation
[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 + 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))
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 + self.record_service.getFilenameExtension(), "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                 elif event == iRecordableService.evGstRecordEnded:
598                         if self.repeated:
599                                 self.processRepeated(findRunningEvent = False)
600                         NavigationInstance.instance.RecordTimer.doActivate(self)
601
602         # we have record_service as property to automatically subscribe to record service events
603         def setRecordService(self, service):
604                 if self.__record_service is not None:
605                         print "[remove callback]"
606                         NavigationInstance.instance.record_event.remove(self.gotRecordEvent)
607
608                 self.__record_service = service
609
610                 if self.__record_service is not None:
611                         print "[add callback]"
612                         NavigationInstance.instance.record_event.append(self.gotRecordEvent)
613
614         record_service = property(lambda self: self.__record_service, setRecordService)
615
616 def createTimer(xml):
617         begin = int(xml.get("begin"))
618         end = int(xml.get("end"))
619         serviceref = ServiceReference(xml.get("serviceref").encode("utf-8"))
620         description = xml.get("description").encode("utf-8")
621         repeated = xml.get("repeated").encode("utf-8")
622         rename_repeat = long(xml.get("rename_repeat") or "1")
623         disabled = long(xml.get("disabled") or "0")
624         justplay = long(xml.get("justplay") or "0")
625         always_zap = long(xml.get("always_zap") or "0")
626         zap_wakeup = str(xml.get("zap_wakeup") or "always")
627         afterevent = str(xml.get("afterevent") or "nothing")
628         afterevent = {
629                 "nothing": AFTEREVENT.NONE,
630                 "standby": AFTEREVENT.STANDBY,
631                 "deepstandby": AFTEREVENT.DEEPSTANDBY,
632                 "auto": AFTEREVENT.AUTO
633                 }[afterevent]
634         eit = xml.get("eit")
635         if eit and eit != "None":
636                 eit = long(eit)
637         else:
638                 eit = None
639         location = xml.get("location")
640         if location and location != "None":
641                 location = location.encode("utf-8")
642         else:
643                 location = None
644         tags = xml.get("tags")
645         if tags and tags != "None":
646                 tags = tags.encode("utf-8").split(' ')
647         else:
648                 tags = None
649         descramble = int(xml.get("descramble") or "1")
650         record_ecm = int(xml.get("record_ecm") or "0")
651
652         name = xml.get("name").encode("utf-8")
653         #filename = xml.get("filename").encode("utf-8")
654         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)
655         entry.repeated = int(repeated)
656
657         for l in xml.findall("log"):
658                 time = int(l.get("time"))
659                 code = int(l.get("code"))
660                 msg = l.text.strip().encode("utf-8")
661                 entry.log_entries.append((time, code, msg))
662
663         return entry
664
665 class RecordTimer(timer.Timer):
666         def __init__(self):
667                 timer.Timer.__init__(self)
668
669                 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
670
671                 try:
672                         self.loadTimer()
673                 except IOError:
674                         print "unable to load timers from file!"
675
676         def doActivate(self, w):
677                 # when activating a timer which has already passed,
678                 # simply abort the timer. don't run trough all the stages.
679                 if w.shouldSkip():
680                         w.state = RecordTimerEntry.StateEnded
681                 else:
682                         # when active returns true, this means "accepted".
683                         # otherwise, the current state is kept.
684                         # the timer entry itself will fix up the delay then.
685                         if w.activate():
686                                 w.state += 1
687
688                 self.timer_list.remove(w)
689
690                 # did this timer reached the last state?
691                 if w.state < RecordTimerEntry.StateEnded:
692                         # no, sort it into active list
693                         insort(self.timer_list, w)
694                 else:
695                         # yes. Process repeated, and re-add.
696                         if w.repeated:
697                                 w.processRepeated()
698                                 w.state = RecordTimerEntry.StateWaiting
699                                 w.first_try_prepare = True
700                                 self.addTimerEntry(w)
701                         else:
702                                 # Remove old timers as set in config
703                                 self.cleanupDaily(config.recording.keep_timers.value)
704                                 insort(self.processed_timers, w)
705                 self.stateChanged(w)
706
707         def isRecording(self):
708                 for timer in self.timer_list:
709                         if timer.isRunning() and not timer.justplay:
710                                 return True
711                 return False
712
713         def loadTimer(self):
714                 # TODO: PATH!
715                 if not Directories.fileExists(self.Filename):
716                         return
717                 try:
718                         doc = xml.etree.cElementTree.parse(self.Filename)
719                 except SyntaxError:
720                         from Tools.Notifications import AddPopup
721                         from Screens.MessageBox import MessageBox
722
723                         AddPopup(_("The timer file (timers.xml) is corrupt and could not be loaded."), type = MessageBox.TYPE_ERROR, timeout = 0, id = "TimerLoadFailed")
724
725                         print "timers.xml failed to load!"
726                         try:
727                                 import os
728                                 os.rename(self.Filename, self.Filename + "_old")
729                         except (IOError, OSError):
730                                 print "renaming broken timer failed"
731                         return
732                 except IOError:
733                         print "timers.xml not found!"
734                         return
735
736                 root = doc.getroot()
737
738                 # put out a message when at least one timer overlaps
739                 checkit = True
740                 for timer in root.findall("timer"):
741                         newTimer = createTimer(timer)
742                         if (self.record(newTimer, ignoreTSC=True, dosave=False) is not None) and checkit:
743                                 from Tools.Notifications import AddPopup
744                                 from Screens.MessageBox import MessageBox
745                                 timer_text = _("\nTimer '%s' disabled!") % newTimer.name
746                                 AddPopup(_("Timer overlap in timers.xml detected!\nPlease recheck it!") + timer_text, type = MessageBox.TYPE_ERROR, timeout = 0, id = "TimerLoadFailed")
747                                 checkit = False # at moment it is enough when the message is displayed one time
748
749         def saveTimer(self):
750                 #root_element = xml.etree.cElementTree.Element('timers')
751                 #root_element.text = "\n"
752
753                 #for timer in self.timer_list + self.processed_timers:
754                         # some timers (instant records) don't want to be saved.
755                         # skip them
756                         #if timer.dontSave:
757                                 #continue
758                         #t = xml.etree.cElementTree.SubElement(root_element, 'timers')
759                         #t.set("begin", str(int(timer.begin)))
760                         #t.set("end", str(int(timer.end)))
761                         #t.set("serviceref", str(timer.service_ref))
762                         #t.set("repeated", str(timer.repeated))
763                         #t.set("name", timer.name)
764                         #t.set("description", timer.description)
765                         #t.set("afterevent", str({
766                         #       AFTEREVENT.NONE: "nothing",
767                         #       AFTEREVENT.STANDBY: "standby",
768                         #       AFTEREVENT.DEEPSTANDBY: "deepstandby",
769                         #       AFTEREVENT.AUTO: "auto"}))
770                         #if timer.eit is not None:
771                         #       t.set("eit", str(timer.eit))
772                         #if timer.dirname is not None:
773                         #       t.set("location", str(timer.dirname))
774                         #t.set("disabled", str(int(timer.disabled)))
775                         #t.set("justplay", str(int(timer.justplay)))
776                         #t.text = "\n"
777                         #t.tail = "\n"
778
779                         #for time, code, msg in timer.log_entries:
780                                 #l = xml.etree.cElementTree.SubElement(t, 'log')
781                                 #l.set("time", str(time))
782                                 #l.set("code", str(code))
783                                 #l.text = str(msg)
784                                 #l.tail = "\n"
785
786                 #doc = xml.etree.cElementTree.ElementTree(root_element)
787                 #doc.write(self.Filename)
788
789                 list = []
790
791                 list.append('<?xml version="1.0" ?>\n')
792                 list.append('<timers>\n')
793
794                 for timer in self.timer_list + self.processed_timers:
795                         if timer.dontSave:
796                                 continue
797
798                         list.append('<timer')
799                         list.append(' begin="' + str(int(timer.begin)) + '"')
800                         list.append(' end="' + str(int(timer.end)) + '"')
801                         list.append(' serviceref="' + stringToXML(str(timer.service_ref)) + '"')
802                         list.append(' repeated="' + str(int(timer.repeated)) + '"')
803                         list.append(' name="' + str(stringToXML(timer.name)) + '"')
804                         list.append(' description="' + str(stringToXML(timer.description)) + '"')
805                         list.append(' afterevent="' + str(stringToXML({
806                                 AFTEREVENT.NONE: "nothing",
807                                 AFTEREVENT.STANDBY: "standby",
808                                 AFTEREVENT.DEEPSTANDBY: "deepstandby",
809                                 AFTEREVENT.AUTO: "auto"
810                                 }[timer.afterEvent])) + '"')
811                         if timer.eit is not None:
812                                 list.append(' eit="' + str(timer.eit) + '"')
813                         if timer.dirname is not None:
814                                 list.append(' location="' + str(stringToXML(timer.dirname)) + '"')
815                         if timer.tags is not None:
816                                 list.append(' tags="' + str(stringToXML(' '.join(timer.tags))) + '"')
817                         list.append(' disabled="' + str(int(timer.disabled)) + '"')
818                         list.append(' justplay="' + str(int(timer.justplay)) + '"')
819                         list.append(' always_zap="' + str(int(timer.always_zap)) + '"')
820                         list.append(' zap_wakeup="' + str(timer.zap_wakeup) + '"')
821                         list.append(' rename_repeat="' + str(int(timer.rename_repeat)) + '"')
822                         list.append(' descramble="' + str(int(timer.descramble)) + '"')
823                         list.append(' record_ecm="' + str(int(timer.record_ecm)) + '"')
824                         list.append('>\n')
825
826                         if config.recording.debug.value:
827                                 for time, code, msg in timer.log_entries:
828                                         list.append('<log')
829                                         list.append(' code="' + str(code) + '"')
830                                         list.append(' time="' + str(time) + '"')
831                                         list.append('>')
832                                         list.append(str(stringToXML(msg)))
833                                         list.append('</log>\n')
834
835                         list.append('</timer>\n')
836
837                 list.append('</timers>\n')
838
839                 file = open(self.Filename + ".writing", "w")
840                 for x in list:
841                         file.write(x)
842                 file.flush()
843
844                 import os
845                 os.fsync(file.fileno())
846                 file.close()
847                 os.rename(self.Filename + ".writing", self.Filename)
848
849         def getNextZapTime(self, isWakeup=False):
850                 now = time()
851                 for timer in self.timer_list:
852                         if not timer.justplay or timer.begin < now or isWakeup and timer.zap_wakeup in ("from_standby", "never"):
853                                 continue
854                         return timer.begin
855                 return -1
856
857         def getNextRecordingTime(self):
858                 now = time()
859                 for timer in self.timer_list:
860                         next_act = timer.getNextActivation()
861                         if timer.justplay or next_act < now:
862                                 continue
863                         return next_act
864                 return -1
865
866         def getNextTimerTime(self, isWakeup=False):
867                 now = time()
868                 for timer in self.timer_list:
869                         next_act = timer.getNextActivation()
870                         if next_act < now or isWakeup and timer.justplay and timer.zap_wakeup in ("from_standby", "never"):
871                                 continue
872                         return next_act
873                 return -1
874
875         def isNextRecordAfterEventActionAuto(self):
876                 now = time()
877                 t = None
878                 for timer in self.timer_list:
879                         if timer.justplay or timer.begin < now:
880                                 continue
881                         if t is None or t.begin == timer.begin:
882                                 t = timer
883                                 if t.afterEvent == AFTEREVENT.AUTO:
884                                         return True
885                 return False
886
887         def record(self, entry, ignoreTSC=False, dosave=True): # wird von loadTimer mit dosave=False aufgerufen
888                 timersanitycheck = TimerSanityCheck(self.timer_list,entry)
889                 answer = None
890                 if not timersanitycheck.check():
891                         if not ignoreTSC:
892                                 print "[RecordTimer] timer conflict detected!"
893                                 print timersanitycheck.getSimulTimerList()
894                                 return timersanitycheck.getSimulTimerList()
895                         else:
896                                 print "[RecordTimer] ignore timer conflict..."
897                                 if not dosave:
898                                         entry.disabled = True
899                                         answer = timersanitycheck.getSimulTimerList()
900                 elif timersanitycheck.doubleCheck():
901                         print "[RecordTimer] ignore double timer..."
902                         return None
903                 entry.timeChanged()
904                 print "[Timer] Record " + str(entry)
905                 entry.Timer = self
906                 self.addTimerEntry(entry)
907                 if dosave:
908                         self.saveTimer()
909                 return answer
910
911         def isInRepeatTimer(self, timer, event):
912                 time_match = 0
913                 is_editable = False
914                 begin = event.getBeginTime()
915                 duration = event.getDuration()
916                 end = begin + duration
917                 timer_end = timer.end
918                 if timer.disabled and timer.isRunning():
919                         if begin < timer.begin <= end or timer.begin <= begin <= timer_end:
920                                 return True
921                         else:
922                                 return False
923                 if timer.justplay and (timer_end - timer.begin) <= 1:
924                         timer_end += 60
925                 bt = localtime(begin)
926                 bday = bt.tm_wday
927                 begin2 = 1440 + bt.tm_hour * 60 + bt.tm_min
928                 end2 = begin2 + duration / 60
929                 xbt = localtime(timer.begin)
930                 xet = localtime(timer_end)
931                 offset_day = False
932                 checking_time = timer.begin < begin or begin <= timer.begin <= end
933                 if xbt.tm_yday != xet.tm_yday:
934                         oday = bday - 1
935                         if oday == -1: oday = 6
936                         offset_day = timer.repeated & (1 << oday)
937                 xbegin = 1440 + xbt.tm_hour * 60 + xbt.tm_min
938                 xend = xbegin + ((timer_end - timer.begin) / 60)
939                 if xend < xbegin:
940                         xend += 1440
941                 if timer.repeated & (1 << bday) and checking_time:
942                         if begin2 < xbegin <= end2:
943                                 if xend < end2:
944                                         # recording within event
945                                         time_match = (xend - xbegin) * 60
946                                         is_editable = True
947                                 else:
948                                         # recording last part of event
949                                         time_match = (end2 - xbegin) * 60
950                                         summary_end = (xend - end2) * 60
951                                         is_editable = not summary_end and True or time_match >= summary_end
952                         elif xbegin <= begin2 <= xend:
953                                 if xend < end2:
954                                         # recording first part of event
955                                         time_match = (xend - begin2) * 60
956                                         summary_end = (begin2 - xbegin) * 60
957                                         is_editable = not summary_end and True or time_match >= summary_end
958                                 else:
959                                         # recording whole event
960                                         time_match = (end2 - begin2) * 60
961                                         is_editable = True
962                         elif offset_day:
963                                 xbegin -= 1440
964                                 xend -= 1440
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 and checking_time:
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                 return time_match and is_editable
1009
1010         def isInTimer(self, eventid, begin, duration, service):
1011                 returnValue = None
1012                 type = 0
1013                 time_match = 0
1014                 bt = None
1015                 check_offset_time = not config.recording.margin_before.value and not config.recording.margin_after.value
1016                 end = begin + duration
1017                 refstr = ':'.join(service.split(':')[:11])
1018                 for x in self.timer_list:
1019                         check = ':'.join(x.service_ref.ref.toString().split(':')[:11]) == refstr
1020                         if not check:
1021                                 sref = x.service_ref.ref
1022                                 parent_sid = sref.getUnsignedData(5)
1023                                 parent_tsid = sref.getUnsignedData(6)
1024                                 if parent_sid and parent_tsid:
1025                                         # check for subservice
1026                                         sid = sref.getUnsignedData(1)
1027                                         tsid = sref.getUnsignedData(2)
1028                                         sref.setUnsignedData(1, parent_sid)
1029                                         sref.setUnsignedData(2, parent_tsid)
1030                                         sref.setUnsignedData(5, 0)
1031                                         sref.setUnsignedData(6, 0)
1032                                         check = sref.toCompareString() == refstr
1033                                         num = 0
1034                                         if check:
1035                                                 check = False
1036                                                 event = eEPGCache.getInstance().lookupEventId(sref, eventid)
1037                                                 num = event and event.getNumOfLinkageServices() or 0
1038                                         sref.setUnsignedData(1, sid)
1039                                         sref.setUnsignedData(2, tsid)
1040                                         sref.setUnsignedData(5, parent_sid)
1041                                         sref.setUnsignedData(6, parent_tsid)
1042                                         for cnt in range(num):
1043                                                 subservice = event.getLinkageService(sref, cnt)
1044                                                 if sref.toCompareString() == subservice.toCompareString():
1045                                                         check = True
1046                                                         break
1047                         if check:
1048                                 timer_end = x.end
1049                                 timer_begin = x.begin
1050                                 type_offset = 0
1051                                 if not x.repeated and check_offset_time:
1052                                         if 0 < end - timer_end <= 59:
1053                                                 timer_end = end
1054                                         elif 0 < timer_begin - begin <= 59:
1055                                                 timer_begin = begin
1056                                 if x.justplay:
1057                                         type_offset = 5
1058                                         if (timer_end - x.begin) <= 1:
1059                                                 timer_end += 60
1060                                 if x.always_zap:
1061                                         type_offset = 10
1062
1063                                 timer_repeat = x.repeated
1064                                 # if set 'don't stop current event but disable coming events' for repeat timer
1065                                 running_only_curevent = x.disabled and x.isRunning() and timer_repeat
1066                                 if running_only_curevent:
1067                                         timer_repeat = 0
1068                                         type_offset += 15
1069
1070                                 if timer_repeat != 0:
1071                                         type_offset += 15
1072                                         if bt is None:
1073                                                 bt = localtime(begin)
1074                                                 bday = bt.tm_wday
1075                                                 begin2 = 1440 + bt.tm_hour * 60 + bt.tm_min
1076                                                 end2 = begin2 + duration / 60
1077                                         xbt = localtime(x.begin)
1078                                         xet = localtime(timer_end)
1079                                         offset_day = False
1080                                         checking_time = x.begin < begin or begin <= x.begin <= end
1081                                         if xbt.tm_yday != xet.tm_yday:
1082                                                 oday = bday - 1
1083                                                 if oday == -1: oday = 6
1084                                                 offset_day = x.repeated & (1 << oday)
1085                                         xbegin = 1440 + xbt.tm_hour * 60 + xbt.tm_min
1086                                         xend = xbegin + ((timer_end - x.begin) / 60)
1087                                         if xend < xbegin:
1088                                                 xend += 1440
1089                                         if x.repeated & (1 << bday) and checking_time:
1090                                                 if begin2 < xbegin <= end2:
1091                                                         if xend < end2:
1092                                                                 # recording within event
1093                                                                 time_match = (xend - xbegin) * 60
1094                                                                 type = type_offset + 3
1095                                                         else:
1096                                                                 # recording last part of event
1097                                                                 time_match = (end2 - xbegin) * 60
1098                                                                 type = type_offset + 1
1099                                                 elif xbegin <= begin2 <= xend:
1100                                                         if xend < end2:
1101                                                                 # recording first part of event
1102                                                                 time_match = (xend - begin2) * 60
1103                                                                 type = type_offset + 4
1104                                                         else:
1105                                                                 # recording whole event
1106                                                                 time_match = (end2 - begin2) * 60
1107                                                                 type = type_offset + 2
1108                                                 elif offset_day:
1109                                                         xbegin -= 1440
1110                                                         xend -= 1440
1111                                                         if begin2 < xbegin <= end2:
1112                                                                 if xend < end2:
1113                                                                         # recording within event
1114                                                                         time_match = (xend - xbegin) * 60
1115                                                                         type = type_offset + 3
1116                                                                 else:
1117                                                                         # recording last part of event
1118                                                                         time_match = (end2 - xbegin) * 60
1119                                                                         type = type_offset + 1
1120                                                         elif xbegin <= begin2 <= xend:
1121                                                                 if xend < end2:
1122                                                                         # recording first part of event
1123                                                                         time_match = (xend - begin2) * 60
1124                                                                         type = type_offset + 4
1125                                                                 else:
1126                                                                         # recording whole event
1127                                                                         time_match = (end2 - begin2) * 60
1128                                                                         type = type_offset + 2
1129                                         elif offset_day and checking_time:
1130                                                 xbegin -= 1440
1131                                                 xend -= 1440
1132                                                 if begin2 < xbegin <= end2:
1133                                                         if xend < end2:
1134                                                                 # recording within event
1135                                                                 time_match = (xend - xbegin) * 60
1136                                                                 type = type_offset + 3
1137                                                         else:
1138                                                                 # recording last part of event
1139                                                                 time_match = (end2 - xbegin) * 60
1140                                                                 type = type_offset + 1
1141                                                 elif xbegin <= begin2 <= xend:
1142                                                         if xend < end2:
1143                                                                 # recording first part of event
1144                                                                 time_match = (xend - begin2) * 60
1145                                                                 type = type_offset + 4
1146                                                         else:
1147                                                                 # recording whole event
1148                                                                 time_match = (end2 - begin2) * 60
1149                                                                 type = type_offset + 2
1150                                 else:
1151                                         if begin < timer_begin <= end:
1152                                                 if timer_end < end:
1153                                                         # recording within event
1154                                                         time_match = timer_end - timer_begin
1155                                                         type = type_offset + 3
1156                                                 else:
1157                                                         # recording last part of event
1158                                                         time_match = end - timer_begin
1159                                                         type = type_offset + 1
1160                                         elif timer_begin <= begin <= timer_end:
1161                                                 if timer_end < end:
1162                                                         # recording first part of event
1163                                                         time_match = timer_end - begin
1164                                                         type = type_offset + 4
1165                                                 else:
1166                                                         # recording whole event
1167                                                         time_match = end - begin
1168                                                         type = type_offset + 2
1169                                 if time_match:
1170                                         if type in (2,7,12,17,22,27):
1171                                                 # When full recording do not look further
1172                                                 returnValue = (time_match, [type])
1173                                                 break
1174                                         elif returnValue:
1175                                                 if type not in returnValue[1]:
1176                                                         returnValue[1].append(type)
1177                                         else:
1178                                                 returnValue = (time_match, [type])
1179
1180                 return returnValue
1181
1182         def removeEntry(self, entry):
1183                 print "[Timer] Remove " + str(entry)
1184
1185                 # avoid re-enqueuing
1186                 entry.repeated = False
1187
1188                 # abort timer.
1189                 # this sets the end time to current time, so timer will be stopped.
1190                 entry.autoincrease = False
1191                 entry.abort()
1192
1193                 if entry.state != entry.StateEnded:
1194                         self.timeChanged(entry)
1195
1196                 print "state: ", entry.state
1197                 print "in processed: ", entry in self.processed_timers
1198                 print "in running: ", entry in self.timer_list
1199                 # autoincrease instanttimer if possible
1200                 if not entry.dontSave:
1201                         for x in self.timer_list:
1202                                 if x.setAutoincreaseEnd():
1203                                         self.timeChanged(x)
1204                 # now the timer should be in the processed_timers list. remove it from there.
1205                 self.processed_timers.remove(entry)
1206                 self.saveTimer()
1207
1208         def shutdown(self):
1209                 self.saveTimer()