3 #from time import datetime
4 from Tools import Directories, Notifications
6 from Components.config import config, ConfigYesNo
10 from enigma import quitMainloop, eEPGCache, getBestPlayableServiceReference, eServiceReference
12 from Screens.MessageBox import MessageBox
13 import NavigationInstance
14 from time import localtime
16 from Tools.XMLTools import elementsWithTag, mergeText, stringToXML
17 from ServiceReference import ServiceReference
19 # ok, for descriptions etc we have:
20 # service reference (to get the service name)
22 # description (description)
23 # event data (ONLY for time adjustments etc.)
26 # parses an event, and gives out a (begin, end, name, duration, eit)-tuple.
27 # begin and end will be corrected
28 def parseEvent(ev, description = True):
30 name = ev.getEventName()
31 description = ev.getShortDescription()
35 begin = ev.getBeginTime()
36 end = begin + ev.getDuration()
38 begin -= config.recording.margin_before.value * 60
39 end += config.recording.margin_after.value * 60
40 return (begin, end, name, description, eit)
47 # please do not translate log messages
48 class RecordTimerEntry(timer.TimerEntry):
49 def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.NONE, checkOldTimers = False):
50 timer.TimerEntry.__init__(self, int(begin), int(end))
52 if checkOldTimers == True:
53 if self.begin < time.time() - 1209600:
54 self.begin = int(time.time())
56 if self.end < self.begin:
59 assert isinstance(serviceref, ServiceReference)
61 self.service_ref = serviceref
65 self.description = description
66 self.disabled = disabled
68 self.record_service = None
69 self.start_prepare = 0
70 self.justplay = justplay
71 self.afterEvent = afterEvent
77 def log(self, code, msg):
78 self.log_entries.append((int(time.time()), code, msg))
82 self.state = self.StateWaiting
83 self.cancelled = False
84 self.first_try_prepare = True
87 def calculateFilename(self):
88 service_name = self.service_ref.getServiceName()
89 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
91 print "begin_date: ", begin_date
92 print "service_name: ", service_name
93 print "name:", self.name
94 print "description: ", self.description
96 filename = begin_date + " - " + service_name
98 filename += " - " + self.name
100 self.Filename = Directories.getRecordingFilename(filename)
101 self.log(0, "Filename calculated as: '%s'" % self.Filename)
102 #begin_date + " - " + service_name + description)
104 def tryPrepare(self):
108 self.calculateFilename()
109 rec_ref = self.service_ref and self.service_ref.ref
110 if rec_ref and rec_ref.flags & eServiceReference.isGroup:
111 rec_ref = getBestPlayableServiceReference(rec_ref, eServiceReference())
113 self.log(1, "'get best playable service for group... record' failed")
116 self.record_service = rec_ref and NavigationInstance.instance.recordService(rec_ref)
117 if not self.record_service:
118 self.log(1, "'record service' failed")
125 prep_res=self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id)
127 self.log(2, "'prepare' failed: error %d" % prep_res)
128 self.record_service = None
132 epgcache = eEPGCache.getInstance()
133 queryTime=self.begin+(self.end-self.begin)/2
134 evt = epgcache.lookupEventTime(rec_ref, queryTime)
136 self.description = evt.getShortDescription()
138 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
140 f = open(self.Filename + ".ts.meta", "w")
141 f.write(rec_ref.toString() + "\n")
142 f.write(self.name + "\n")
143 f.write(self.description + "\n")
144 f.write(str(self.begin) + "\n")
147 self.log(4, "failed to write meta information")
148 self.record_service = None
152 def do_backoff(self):
153 if self.backoff == 0:
157 if self.backoff > 100:
159 self.log(10, "backoff: retry in %d seconds" % self.backoff)
162 next_state = self.state + 1
163 self.log(5, "activating state %d" % next_state)
165 if next_state == self.StatePrepared:
166 if self.tryPrepare():
167 self.log(6, "prepare ok, waiting for begin")
168 # fine. it worked, resources are allocated.
169 self.next_activation = self.begin
173 self.log(7, "prepare failed")
174 if self.first_try_prepare:
175 self.first_try_prepare = False
176 if not config.recording.asktozap.value:
177 self.log(8, "asking user to zap away")
178 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"), timeout=20)
179 else: # zap without asking
180 self.log(9, "zap without asking")
181 Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20)
186 self.start_prepare = time.time() + self.backoff
188 elif next_state == self.StateRunning:
189 # if this timer has been cancelled, just go to "end" state.
194 self.log(11, "zapping")
195 NavigationInstance.instance.playService(self.service_ref.ref)
198 self.log(11, "start recording")
199 record_res = self.record_service.start()
202 self.log(13, "start record returned %d" % record_res)
205 self.begin = time.time() + self.backoff
209 elif next_state == self.StateEnded:
210 self.log(12, "stop recording")
211 if not self.justplay:
212 NavigationInstance.instance.stopRecordService(self.record_service)
213 self.record_service = None
214 if self.afterEvent == AFTEREVENT.STANDBY:
215 if self.session is not None:
216 self.session.open(Standby, self)
217 elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
221 def getNextActivation(self):
222 if self.state == self.StateEnded:
225 next_state = self.state + 1
227 return {self.StatePrepared: self.start_prepare,
228 self.StateRunning: self.begin,
229 self.StateEnded: self.end }[next_state]
231 def failureCB(self, answer):
233 self.log(13, "ok, zapped away")
234 #NavigationInstance.instance.stopUserServices()
235 NavigationInstance.instance.playService(self.service_ref.ref)
237 self.log(14, "user didn't want to zap away, record will probably fail")
239 def timeChanged(self):
240 old_prepare = self.start_prepare
241 self.start_prepare = self.begin - self.prepare_time
244 if int(old_prepare) != int(self.start_prepare):
245 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
247 def createTimer(xml):
248 begin = int(xml.getAttribute("begin"))
249 end = int(xml.getAttribute("end"))
250 serviceref = ServiceReference(xml.getAttribute("serviceref").encode("utf-8"))
251 description = xml.getAttribute("description").encode("utf-8")
252 repeated = xml.getAttribute("repeated").encode("utf-8")
253 disabled = long(xml.getAttribute("disabled") or "0")
254 justplay = long(xml.getAttribute("justplay") or "0")
255 afterevent = str(xml.getAttribute("afterevent") or "nothing")
256 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
257 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
258 eit = long(xml.getAttribute("eit"))
262 name = xml.getAttribute("name").encode("utf-8")
263 #filename = xml.getAttribute("filename").encode("utf-8")
264 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
265 entry.repeated = int(repeated)
267 for l in elementsWithTag(xml.childNodes, "log"):
268 time = int(l.getAttribute("time"))
269 code = int(l.getAttribute("code"))
270 msg = mergeText(l.childNodes).strip().encode("utf-8")
271 entry.log_entries.append((time, code, msg))
275 class RecordTimer(timer.Timer):
277 timer.Timer.__init__(self)
279 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
284 print "unable to load timers from file!"
286 def isRecording(self):
288 for timer in self.timer_list:
289 if timer.isRunning() and not timer.justplay:
295 doc = xml.dom.minidom.parse(self.Filename)
297 root = doc.childNodes[0]
298 for timer in elementsWithTag(root.childNodes, "timer"):
299 self.record(createTimer(timer))
302 #doc = xml.dom.minidom.Document()
303 #root_element = doc.createElement('timers')
304 #doc.appendChild(root_element)
305 #root_element.appendChild(doc.createTextNode("\n"))
307 #for timer in self.timer_list + self.processed_timers:
308 # some timers (instant records) don't want to be saved.
312 #t = doc.createTextNode("\t")
313 #root_element.appendChild(t)
314 #t = doc.createElement('timer')
315 #t.setAttribute("begin", str(int(timer.begin)))
316 #t.setAttribute("end", str(int(timer.end)))
317 #t.setAttribute("serviceref", str(timer.service_ref))
318 #t.setAttribute("repeated", str(timer.repeated))
319 #t.setAttribute("name", timer.name)
320 #t.setAttribute("description", timer.description)
321 #t.setAttribute("eit", str(timer.eit))
323 #for time, code, msg in timer.log_entries:
324 #t.appendChild(doc.createTextNode("\t\t"))
325 #l = doc.createElement('log')
326 #l.setAttribute("time", str(time))
327 #l.setAttribute("code", str(code))
328 #l.appendChild(doc.createTextNode(msg))
330 #t.appendChild(doc.createTextNode("\n"))
332 #root_element.appendChild(t)
333 #t = doc.createTextNode("\n")
334 #root_element.appendChild(t)
337 #file = open(self.Filename, "w")
344 list.append('<?xml version="1.0" ?>\n')
345 list.append('<timers>\n')
347 for timer in self.timer_list + self.processed_timers:
351 list.append('<timer')
352 list.append(' begin="' + str(int(timer.begin)) + '"')
353 list.append(' end="' + str(int(timer.end)) + '"')
354 list.append(' serviceref="' + stringToXML(str(timer.service_ref)) + '"')
355 list.append(' repeated="' + str(int(timer.repeated)) + '"')
356 list.append(' name="' + str(stringToXML(timer.name)) + '"')
357 list.append(' description="' + str(stringToXML(timer.description)) + '"')
358 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
359 if timer.eit is not None:
360 list.append(' eit="' + str(timer.eit) + '"')
361 list.append(' disabled="' + str(int(timer.disabled)) + '"')
362 list.append(' justplay="' + str(int(timer.justplay)) + '"')
365 if config.recording.debug.value:
366 for time, code, msg in timer.log_entries:
368 list.append(' code="' + str(code) + '"')
369 list.append(' time="' + str(time) + '"')
371 list.append(str(stringToXML(msg)))
372 list.append('</log>\n')
374 list.append('</timer>\n')
376 list.append('</timers>\n')
378 file = open(self.Filename, "w")
383 def record(self, entry):
385 print "[Timer] Record " + str(entry)
387 self.addTimerEntry(entry)
389 def isInTimer(self, eventid, begin, duration, service):
393 chktimecmp_end = None
394 end = begin + duration
395 for x in self.timer_list:
396 if str(x.service_ref) == str(service):
397 #if x.eit is not None and x.repeated == 0:
398 # if x.eit == eventid:
402 chktime = localtime(begin)
403 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
404 chktimecmp_end = chktimecmp + (duration / 60)
405 time = localtime(x.begin)
407 if x.repeated & (2 ** y):
408 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
409 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
410 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
411 elif chktimecmp <= timecmp < chktimecmp_end:
412 time_match = (chktimecmp_end - timecmp) * 60
413 else: #if x.eit is None:
414 if begin <= x.begin <= end:
416 if time_match < diff:
418 elif x.begin <= begin <= x.end:
420 if time_match < diff:
424 def removeEntry(self, entry):
425 print "[Timer] Remove " + str(entry)
428 entry.repeated = False
431 # this sets the end time to current time, so timer will be stopped.
434 if entry.state != entry.StateEnded:
435 self.timeChanged(entry)
437 print "state: ", entry.state
438 print "in processed: ", entry in self.processed_timers
439 print "in running: ", entry in self.timer_list
440 # now the timer should be in the processed_timers list. remove it from there.
441 self.processed_timers.remove(entry)