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 if self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id):
126 self.log(2, "'prepare' failed: error %d" % prep_res)
127 self.record_service = None
131 epgcache = eEPGCache.getInstance()
132 queryTime=self.begin+(self.end-self.begin)/2
133 evt = epgcache.lookupEventTime(rec_ref, queryTime)
135 self.description = evt.getShortDescription()
137 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
139 f = open(self.Filename + ".ts.meta", "w")
140 f.write(rec_ref.toString() + "\n")
141 f.write(self.name + "\n")
142 f.write(self.description + "\n")
143 f.write(str(self.begin) + "\n")
146 self.log(4, "failed to write meta information")
147 self.record_service = None
151 def do_backoff(self):
152 if self.backoff == 0:
156 if self.backoff > 100:
158 self.log(10, "backoff: retry in %d seconds" % self.backoff)
161 next_state = self.state + 1
162 self.log(5, "activating state %d" % next_state)
164 if next_state == self.StatePrepared:
165 if self.tryPrepare():
166 self.log(6, "prepare ok, waiting for begin")
167 # fine. it worked, resources are allocated.
168 self.next_activation = self.begin
172 self.log(7, "prepare failed")
173 if self.first_try_prepare:
174 self.first_try_prepare = False
175 if not config.recording.asktozap.value:
176 self.log(8, "asking user to zap away")
177 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"), timeout=20)
178 else: # zap without asking
179 self.log(9, "zap without asking")
180 Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20)
185 self.start_prepare = time.time() + self.backoff
187 elif next_state == self.StateRunning:
188 # if this timer has been cancelled, just go to "end" state.
193 self.log(11, "zapping")
194 NavigationInstance.instance.playService(self.service_ref.ref)
197 self.log(11, "start recording")
198 record_res = self.record_service.start()
201 self.log(13, "start record returned %d" % record_res)
204 self.begin = time.time() + self.backoff
208 elif next_state == self.StateEnded:
209 self.log(12, "stop recording")
210 if not self.justplay:
211 NavigationInstance.instance.stopRecordService(self.record_service)
212 self.record_service = None
213 if self.afterEvent == AFTEREVENT.STANDBY:
214 if self.session is not None:
215 self.session.open(Standby, self)
216 elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
220 def getNextActivation(self):
221 if self.state == self.StateEnded:
224 next_state = self.state + 1
226 return {self.StatePrepared: self.start_prepare,
227 self.StateRunning: self.begin,
228 self.StateEnded: self.end }[next_state]
230 def failureCB(self, answer):
232 self.log(13, "ok, zapped away")
233 #NavigationInstance.instance.stopUserServices()
234 NavigationInstance.instance.playService(self.service_ref.ref)
236 self.log(14, "user didn't want to zap away, record will probably fail")
238 def timeChanged(self):
239 old_prepare = self.start_prepare
240 self.start_prepare = self.begin - self.prepare_time
243 if int(old_prepare) != int(self.start_prepare):
244 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
246 def createTimer(xml):
247 begin = int(xml.getAttribute("begin"))
248 end = int(xml.getAttribute("end"))
249 serviceref = ServiceReference(xml.getAttribute("serviceref").encode("utf-8"))
250 description = xml.getAttribute("description").encode("utf-8")
251 repeated = xml.getAttribute("repeated").encode("utf-8")
252 disabled = long(xml.getAttribute("disabled") or "0")
253 justplay = long(xml.getAttribute("justplay") or "0")
254 afterevent = str(xml.getAttribute("afterevent") or "nothing")
255 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
256 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
257 eit = long(xml.getAttribute("eit"))
261 name = xml.getAttribute("name").encode("utf-8")
262 #filename = xml.getAttribute("filename").encode("utf-8")
263 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
264 entry.repeated = int(repeated)
266 for l in elementsWithTag(xml.childNodes, "log"):
267 time = int(l.getAttribute("time"))
268 code = int(l.getAttribute("code"))
269 msg = mergeText(l.childNodes).strip().encode("utf-8")
270 entry.log_entries.append((time, code, msg))
274 class RecordTimer(timer.Timer):
276 timer.Timer.__init__(self)
278 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
283 print "unable to load timers from file!"
285 def isRecording(self):
287 for timer in self.timer_list:
288 if timer.isRunning() and not timer.justplay:
294 doc = xml.dom.minidom.parse(self.Filename)
296 root = doc.childNodes[0]
297 for timer in elementsWithTag(root.childNodes, "timer"):
298 self.record(createTimer(timer))
301 #doc = xml.dom.minidom.Document()
302 #root_element = doc.createElement('timers')
303 #doc.appendChild(root_element)
304 #root_element.appendChild(doc.createTextNode("\n"))
306 #for timer in self.timer_list + self.processed_timers:
307 # some timers (instant records) don't want to be saved.
311 #t = doc.createTextNode("\t")
312 #root_element.appendChild(t)
313 #t = doc.createElement('timer')
314 #t.setAttribute("begin", str(int(timer.begin)))
315 #t.setAttribute("end", str(int(timer.end)))
316 #t.setAttribute("serviceref", str(timer.service_ref))
317 #t.setAttribute("repeated", str(timer.repeated))
318 #t.setAttribute("name", timer.name)
319 #t.setAttribute("description", timer.description)
320 #t.setAttribute("eit", str(timer.eit))
322 #for time, code, msg in timer.log_entries:
323 #t.appendChild(doc.createTextNode("\t\t"))
324 #l = doc.createElement('log')
325 #l.setAttribute("time", str(time))
326 #l.setAttribute("code", str(code))
327 #l.appendChild(doc.createTextNode(msg))
329 #t.appendChild(doc.createTextNode("\n"))
331 #root_element.appendChild(t)
332 #t = doc.createTextNode("\n")
333 #root_element.appendChild(t)
336 #file = open(self.Filename, "w")
343 list.append('<?xml version="1.0" ?>\n')
344 list.append('<timers>\n')
346 for timer in self.timer_list + self.processed_timers:
350 list.append('<timer')
351 list.append(' begin="' + str(int(timer.begin)) + '"')
352 list.append(' end="' + str(int(timer.end)) + '"')
353 list.append(' serviceref="' + stringToXML(str(timer.service_ref)) + '"')
354 list.append(' repeated="' + str(int(timer.repeated)) + '"')
355 list.append(' name="' + str(stringToXML(timer.name)) + '"')
356 list.append(' description="' + str(stringToXML(timer.description)) + '"')
357 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
358 if timer.eit is not None:
359 list.append(' eit="' + str(timer.eit) + '"')
360 list.append(' disabled="' + str(int(timer.disabled)) + '"')
361 list.append(' justplay="' + str(int(timer.justplay)) + '"')
364 if config.recording.debug.value:
365 for time, code, msg in timer.log_entries:
367 list.append(' code="' + str(code) + '"')
368 list.append(' time="' + str(time) + '"')
370 list.append(str(stringToXML(msg)))
371 list.append('</log>\n')
373 list.append('</timer>\n')
375 list.append('</timers>\n')
377 file = open(self.Filename, "w")
382 def record(self, entry):
384 print "[Timer] Record " + str(entry)
386 self.addTimerEntry(entry)
388 def isInTimer(self, eventid, begin, duration, service):
392 chktimecmp_end = None
393 end = begin + duration
394 for x in self.timer_list:
395 if str(x.service_ref) == str(service):
396 #if x.eit is not None and x.repeated == 0:
397 # if x.eit == eventid:
401 chktime = localtime(begin)
402 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
403 chktimecmp_end = chktimecmp + (duration / 60)
404 time = localtime(x.begin)
406 if x.repeated & (2 ** y):
407 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
408 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
409 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
410 elif chktimecmp <= timecmp < chktimecmp_end:
411 time_match = (chktimecmp_end - timecmp) * 60
412 else: #if x.eit is None:
413 if begin <= x.begin <= end:
415 if time_match < diff:
417 elif x.begin <= begin <= x.end:
419 if time_match < diff:
423 def removeEntry(self, entry):
424 print "[Timer] Remove " + str(entry)
427 entry.repeated = False
430 # this sets the end time to current time, so timer will be stopped.
433 if entry.state != entry.StateEnded:
434 self.timeChanged(entry)
436 print "state: ", entry.state
437 print "in processed: ", entry in self.processed_timers
438 print "in running: ", entry in self.timer_list
439 # now the timer should be in the processed_timers list. remove it from there.
440 self.processed_timers.remove(entry)