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