3 #from time import datetime
4 from Tools import Directories, Notifications
6 from Components.config import config, ConfigYesNo
10 from enigma import eEPGCache, getBestPlayableServiceReference, eServiceReference
12 from Screens.MessageBox import MessageBox
13 from Screens.Standby import Standby, TryQuitMainloop, inStandby, inTryQuitMainloop
15 import NavigationInstance
16 from time import localtime
18 from Tools.XMLTools import elementsWithTag, mergeText, stringToXML
19 from ServiceReference import ServiceReference
21 # ok, for descriptions etc we have:
22 # service reference (to get the service name)
24 # description (description)
25 # event data (ONLY for time adjustments etc.)
28 # parses an event, and gives out a (begin, end, name, duration, eit)-tuple.
29 # begin and end will be corrected
30 def parseEvent(ev, description = True):
32 name = ev.getEventName()
33 description = ev.getShortDescription()
37 begin = ev.getBeginTime()
38 end = begin + ev.getDuration()
40 begin -= config.recording.margin_before.value * 60
41 end += config.recording.margin_after.value * 60
42 return (begin, end, name, description, eit)
49 # please do not translate log messages
50 class RecordTimerEntry(timer.TimerEntry):
51 def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.NONE, checkOldTimers = False):
52 timer.TimerEntry.__init__(self, int(begin), int(end))
54 if checkOldTimers == True:
55 if self.begin < time.time() - 1209600:
56 self.begin = int(time.time())
58 if self.end < self.begin:
61 assert isinstance(serviceref, ServiceReference)
63 self.service_ref = serviceref
67 self.description = description
68 self.disabled = disabled
70 self.record_service = None
71 self.start_prepare = 0
72 self.justplay = justplay
73 self.afterEvent = afterEvent
78 def log(self, code, msg):
79 self.log_entries.append((int(time.time()), code, msg))
83 self.state = self.StateWaiting
84 self.cancelled = False
85 self.first_try_prepare = True
88 def calculateFilename(self):
89 service_name = self.service_ref.getServiceName()
90 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
92 print "begin_date: ", begin_date
93 print "service_name: ", service_name
94 print "name:", self.name
95 print "description: ", self.description
97 filename = begin_date + " - " + service_name
99 filename += " - " + self.name
101 self.Filename = Directories.getRecordingFilename(filename)
102 self.log(0, "Filename calculated as: '%s'" % self.Filename)
103 #begin_date + " - " + service_name + description)
105 def tryPrepare(self):
109 self.calculateFilename()
110 rec_ref = self.service_ref and self.service_ref.ref
111 if rec_ref and rec_ref.flags & eServiceReference.isGroup:
112 rec_ref = getBestPlayableServiceReference(rec_ref, eServiceReference())
114 self.log(1, "'get best playable service for group... record' failed")
117 self.record_service = rec_ref and NavigationInstance.instance.recordService(rec_ref)
118 if not self.record_service:
119 self.log(1, "'record service' failed")
126 prep_res=self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id)
128 self.log(2, "'prepare' failed: error %d" % prep_res)
129 self.record_service = None
133 epgcache = eEPGCache.getInstance()
134 queryTime=self.begin+(self.end-self.begin)/2
135 evt = epgcache.lookupEventTime(rec_ref, queryTime)
137 self.description = evt.getShortDescription()
139 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
141 f = open(self.Filename + ".ts.meta", "w")
142 f.write(rec_ref.toString() + "\n")
143 f.write(self.name + "\n")
144 f.write(self.description + "\n")
145 f.write(str(self.begin) + "\n")
148 self.log(4, "failed to write meta information")
149 self.record_service = None
153 def do_backoff(self):
154 if self.backoff == 0:
158 if self.backoff > 100:
160 self.log(10, "backoff: retry in %d seconds" % self.backoff)
163 next_state = self.state + 1
164 self.log(5, "activating state %d" % next_state)
166 if next_state == self.StatePrepared:
167 if self.tryPrepare():
168 self.log(6, "prepare ok, waiting for begin")
169 # fine. it worked, resources are allocated.
170 self.next_activation = self.begin
174 self.log(7, "prepare failed")
175 if self.first_try_prepare:
176 self.first_try_prepare = False
177 if not config.recording.asktozap.value:
178 self.log(8, "asking user to zap away")
179 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"), timeout=20)
180 else: # zap without asking
181 self.log(9, "zap without asking")
182 Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20)
187 self.start_prepare = time.time() + self.backoff
189 elif next_state == self.StateRunning:
190 # if this timer has been cancelled, just go to "end" state.
195 self.log(11, "zapping")
196 NavigationInstance.instance.playService(self.service_ref.ref)
199 self.log(11, "start recording")
200 record_res = self.record_service.start()
203 self.log(13, "start record returned %d" % record_res)
206 self.begin = time.time() + self.backoff
210 elif next_state == self.StateEnded:
211 self.log(12, "stop recording")
212 if not self.justplay:
213 NavigationInstance.instance.stopRecordService(self.record_service)
214 self.record_service = None
215 if self.afterEvent == AFTEREVENT.STANDBY:
218 Notifications.AddNotificationWithCallback(self.sendStandbyNotification, MessageBox, _("A finished record timer want's to set your\nDreambox to standby. Do that now?"), timeout = 20)
219 if self.afterEvent == AFTEREVENT.DEEPSTANDBY:
220 global inTryQuitMainloop
221 if not inTryQuitMainloop:
222 Notifications.AddNotificationWithCallback(self.sendTryQuitMainloopNotification, MessageBox, _("A finished record timer want's to shut down\nyour Dreambox. Shutdown now?"), timeout = 20)
225 def sendStandbyNotification(self, answer):
227 Notifications.AddNotification(Standby)
229 def sendTryQuitMainloopNotification(self, answer):
231 Notifications.AddNotification(TryQuitMainloop, 1)
233 def getNextActivation(self):
234 if self.state == self.StateEnded:
237 next_state = self.state + 1
239 return {self.StatePrepared: self.start_prepare,
240 self.StateRunning: self.begin,
241 self.StateEnded: self.end }[next_state]
243 def failureCB(self, answer):
245 self.log(13, "ok, zapped away")
246 #NavigationInstance.instance.stopUserServices()
247 NavigationInstance.instance.playService(self.service_ref.ref)
249 self.log(14, "user didn't want to zap away, record will probably fail")
251 def timeChanged(self):
252 old_prepare = self.start_prepare
253 self.start_prepare = self.begin - self.prepare_time
256 if int(old_prepare) != int(self.start_prepare):
257 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
259 def createTimer(xml):
260 begin = int(xml.getAttribute("begin"))
261 end = int(xml.getAttribute("end"))
262 serviceref = ServiceReference(xml.getAttribute("serviceref").encode("utf-8"))
263 description = xml.getAttribute("description").encode("utf-8")
264 repeated = xml.getAttribute("repeated").encode("utf-8")
265 disabled = long(xml.getAttribute("disabled") or "0")
266 justplay = long(xml.getAttribute("justplay") or "0")
267 afterevent = str(xml.getAttribute("afterevent") or "nothing")
268 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
269 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
270 eit = long(xml.getAttribute("eit"))
274 name = xml.getAttribute("name").encode("utf-8")
275 #filename = xml.getAttribute("filename").encode("utf-8")
276 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
277 entry.repeated = int(repeated)
279 for l in elementsWithTag(xml.childNodes, "log"):
280 time = int(l.getAttribute("time"))
281 code = int(l.getAttribute("code"))
282 msg = mergeText(l.childNodes).strip().encode("utf-8")
283 entry.log_entries.append((time, code, msg))
287 class RecordTimer(timer.Timer):
289 timer.Timer.__init__(self)
291 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
296 print "unable to load timers from file!"
298 def isRecording(self):
300 for timer in self.timer_list:
301 if timer.isRunning() and not timer.justplay:
307 doc = xml.dom.minidom.parse(self.Filename)
309 root = doc.childNodes[0]
310 for timer in elementsWithTag(root.childNodes, "timer"):
311 self.record(createTimer(timer))
314 #doc = xml.dom.minidom.Document()
315 #root_element = doc.createElement('timers')
316 #doc.appendChild(root_element)
317 #root_element.appendChild(doc.createTextNode("\n"))
319 #for timer in self.timer_list + self.processed_timers:
320 # some timers (instant records) don't want to be saved.
324 #t = doc.createTextNode("\t")
325 #root_element.appendChild(t)
326 #t = doc.createElement('timer')
327 #t.setAttribute("begin", str(int(timer.begin)))
328 #t.setAttribute("end", str(int(timer.end)))
329 #t.setAttribute("serviceref", str(timer.service_ref))
330 #t.setAttribute("repeated", str(timer.repeated))
331 #t.setAttribute("name", timer.name)
332 #t.setAttribute("description", timer.description)
333 #t.setAttribute("eit", str(timer.eit))
335 #for time, code, msg in timer.log_entries:
336 #t.appendChild(doc.createTextNode("\t\t"))
337 #l = doc.createElement('log')
338 #l.setAttribute("time", str(time))
339 #l.setAttribute("code", str(code))
340 #l.appendChild(doc.createTextNode(msg))
342 #t.appendChild(doc.createTextNode("\n"))
344 #root_element.appendChild(t)
345 #t = doc.createTextNode("\n")
346 #root_element.appendChild(t)
349 #file = open(self.Filename, "w")
356 list.append('<?xml version="1.0" ?>\n')
357 list.append('<timers>\n')
359 for timer in self.timer_list + self.processed_timers:
363 list.append('<timer')
364 list.append(' begin="' + str(int(timer.begin)) + '"')
365 list.append(' end="' + str(int(timer.end)) + '"')
366 list.append(' serviceref="' + stringToXML(str(timer.service_ref)) + '"')
367 list.append(' repeated="' + str(int(timer.repeated)) + '"')
368 list.append(' name="' + str(stringToXML(timer.name)) + '"')
369 list.append(' description="' + str(stringToXML(timer.description)) + '"')
370 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
371 if timer.eit is not None:
372 list.append(' eit="' + str(timer.eit) + '"')
373 list.append(' disabled="' + str(int(timer.disabled)) + '"')
374 list.append(' justplay="' + str(int(timer.justplay)) + '"')
377 if config.recording.debug.value:
378 for time, code, msg in timer.log_entries:
380 list.append(' code="' + str(code) + '"')
381 list.append(' time="' + str(time) + '"')
383 list.append(str(stringToXML(msg)))
384 list.append('</log>\n')
386 list.append('</timer>\n')
388 list.append('</timers>\n')
390 file = open(self.Filename, "w")
395 def record(self, entry):
397 print "[Timer] Record " + str(entry)
399 self.addTimerEntry(entry)
401 def isInTimer(self, eventid, begin, duration, service):
405 chktimecmp_end = None
406 end = begin + duration
407 for x in self.timer_list:
408 if str(x.service_ref) == str(service):
409 #if x.eit is not None and x.repeated == 0:
410 # if x.eit == eventid:
414 chktime = localtime(begin)
415 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
416 chktimecmp_end = chktimecmp + (duration / 60)
417 time = localtime(x.begin)
419 if x.repeated & (2 ** y):
420 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
421 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
422 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
423 elif chktimecmp <= timecmp < chktimecmp_end:
424 time_match = (chktimecmp_end - timecmp) * 60
425 else: #if x.eit is None:
426 if begin <= x.begin <= end:
428 if time_match < diff:
430 elif x.begin <= begin <= x.end:
432 if time_match < diff:
436 def removeEntry(self, entry):
437 print "[Timer] Remove " + str(entry)
440 entry.repeated = False
443 # this sets the end time to current time, so timer will be stopped.
446 if entry.state != entry.StateEnded:
447 self.timeChanged(entry)
449 print "state: ", entry.state
450 print "in processed: ", entry in self.processed_timers
451 print "in running: ", entry in self.timer_list
452 # now the timer should be in the processed_timers list. remove it from there.
453 self.processed_timers.remove(entry)