3 #from time import datetime
4 from Tools import Directories, Notifications
6 from Components.config import config, ConfigYesNo
10 from enigma import quitMainloop, eEPGCache
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 self.record_service = NavigationInstance.instance.recordService(self.service_ref)
110 if self.record_service == None:
111 self.log(1, "'record service' failed")
117 prep_res = self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id )
119 self.log(2, "'prepare' failed: error %d" % prep_res)
120 self.record_service = None
124 epgcache = eEPGCache.getInstance()
125 queryTime=self.begin+(self.end-self.begin)/2
126 evt = epgcache.lookupEventTime(self.service_ref.ref, queryTime)
128 self.description = evt.getShortDescription()
129 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
131 f = open(self.Filename + ".ts.meta", "w")
132 f.write(str(self.service_ref) + "\n")
133 f.write(self.name + "\n")
134 f.write(self.description + "\n")
135 f.write(str(self.begin) + "\n")
138 self.log(4, "failed to write meta information")
141 def do_backoff(self):
142 if self.backoff == 0:
146 if self.backoff > 100:
148 self.log(10, "backoff: retry in %d seconds" % self.backoff)
151 next_state = self.state + 1
152 self.log(5, "activating state %d" % next_state)
154 if next_state == self.StatePrepared:
155 if self.tryPrepare():
156 self.log(6, "prepare ok, waiting for begin")
157 # fine. it worked, resources are allocated.
158 self.next_activation = self.begin
162 self.log(7, "prepare failed")
163 if self.first_try_prepare:
164 self.first_try_prepare = False
165 if not config.recording.asktozap.value:
166 self.log(8, "asking user to zap away")
167 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"), timeout=20)
168 else: # zap without asking
169 self.log(9, "zap without asking")
170 Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20)
175 self.start_prepare = time.time() + self.backoff
177 elif next_state == self.StateRunning:
178 # if this timer has been cancelled, just go to "end" state.
183 self.log(11, "zapping")
184 NavigationInstance.instance.playService(self.service_ref.ref)
187 self.log(11, "start recording")
188 record_res = self.record_service.start()
191 self.log(13, "start record returned %d" % record_res)
194 self.begin = time.time() + self.backoff
198 elif next_state == self.StateEnded:
199 self.log(12, "stop recording")
200 if not self.justplay:
201 self.record_service.stop()
202 self.record_service = None
203 if self.afterEvent == AFTEREVENT.STANDBY:
204 if self.session is not None:
205 self.session.open(Standby, self)
206 elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
210 def getNextActivation(self):
211 if self.state == self.StateEnded:
214 next_state = self.state + 1
216 return {self.StatePrepared: self.start_prepare,
217 self.StateRunning: self.begin,
218 self.StateEnded: self.end }[next_state]
220 def failureCB(self, answer):
222 self.log(13, "ok, zapped away")
223 #NavigationInstance.instance.stopUserServices()
224 NavigationInstance.instance.playService(self.service_ref.ref)
226 self.log(14, "user didn't want to zap away, record will probably fail")
228 def timeChanged(self):
229 old_prepare = self.start_prepare
230 self.start_prepare = self.begin - self.prepare_time
233 if int(old_prepare) != int(self.start_prepare):
234 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
236 def createTimer(xml):
237 begin = int(xml.getAttribute("begin"))
238 end = int(xml.getAttribute("end"))
239 serviceref = ServiceReference(xml.getAttribute("serviceref").encode("utf-8"))
240 description = xml.getAttribute("description").encode("utf-8")
241 repeated = xml.getAttribute("repeated").encode("utf-8")
242 disabled = long(xml.getAttribute("disabled") or "0")
243 justplay = long(xml.getAttribute("justplay") or "0")
244 afterevent = str(xml.getAttribute("afterevent") or "nothing")
245 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
246 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
247 eit = long(xml.getAttribute("eit"))
251 name = xml.getAttribute("name").encode("utf-8")
252 #filename = xml.getAttribute("filename").encode("utf-8")
253 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
254 entry.repeated = int(repeated)
256 for l in elementsWithTag(xml.childNodes, "log"):
257 time = int(l.getAttribute("time"))
258 code = int(l.getAttribute("code"))
259 msg = mergeText(l.childNodes).strip().encode("utf-8")
260 entry.log_entries.append((time, code, msg))
264 class RecordTimer(timer.Timer):
266 timer.Timer.__init__(self)
268 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
273 print "unable to load timers from file!"
275 def isRecording(self):
277 for timer in self.timer_list:
278 if timer.isRunning() and not timer.justplay:
284 doc = xml.dom.minidom.parse(self.Filename)
286 root = doc.childNodes[0]
287 for timer in elementsWithTag(root.childNodes, "timer"):
288 self.record(createTimer(timer))
291 #doc = xml.dom.minidom.Document()
292 #root_element = doc.createElement('timers')
293 #doc.appendChild(root_element)
294 #root_element.appendChild(doc.createTextNode("\n"))
296 #for timer in self.timer_list + self.processed_timers:
297 # some timers (instant records) don't want to be saved.
301 #t = doc.createTextNode("\t")
302 #root_element.appendChild(t)
303 #t = doc.createElement('timer')
304 #t.setAttribute("begin", str(int(timer.begin)))
305 #t.setAttribute("end", str(int(timer.end)))
306 #t.setAttribute("serviceref", str(timer.service_ref))
307 #t.setAttribute("repeated", str(timer.repeated))
308 #t.setAttribute("name", timer.name)
309 #t.setAttribute("description", timer.description)
310 #t.setAttribute("eit", str(timer.eit))
312 #for time, code, msg in timer.log_entries:
313 #t.appendChild(doc.createTextNode("\t\t"))
314 #l = doc.createElement('log')
315 #l.setAttribute("time", str(time))
316 #l.setAttribute("code", str(code))
317 #l.appendChild(doc.createTextNode(msg))
319 #t.appendChild(doc.createTextNode("\n"))
321 #root_element.appendChild(t)
322 #t = doc.createTextNode("\n")
323 #root_element.appendChild(t)
326 #file = open(self.Filename, "w")
333 list.append('<?xml version="1.0" ?>\n')
334 list.append('<timers>\n')
336 for timer in self.timer_list + self.processed_timers:
340 list.append('<timer')
341 list.append(' begin="' + str(int(timer.begin)) + '"')
342 list.append(' end="' + str(int(timer.end)) + '"')
343 list.append(' serviceref="' + str(timer.service_ref) + '"')
344 list.append(' repeated="' + str(int(timer.repeated)) + '"')
345 list.append(' name="' + str(stringToXML(timer.name)) + '"')
346 list.append(' description="' + str(stringToXML(timer.description)) + '"')
347 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
348 if timer.eit is not None:
349 list.append(' eit="' + str(timer.eit) + '"')
350 list.append(' disabled="' + str(int(timer.disabled)) + '"')
351 list.append(' justplay="' + str(int(timer.justplay)) + '"')
354 if config.recording.debug.value:
355 for time, code, msg in timer.log_entries:
357 list.append(' code="' + str(code) + '"')
358 list.append(' time="' + str(time) + '"')
360 list.append(str(stringToXML(msg)))
361 list.append('</log>\n')
363 list.append('</timer>\n')
365 list.append('</timers>\n')
367 file = open(self.Filename, "w")
372 def record(self, entry):
374 print "[Timer] Record " + str(entry)
376 self.addTimerEntry(entry)
378 def isInTimer(self, eventid, begin, duration, service):
382 chktimecmp_end = None
383 end = begin + duration
384 for x in self.timer_list:
385 if str(x.service_ref) == str(service):
386 #if x.eit is not None and x.repeated == 0:
387 # if x.eit == eventid:
391 chktime = localtime(begin)
392 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
393 chktimecmp_end = chktimecmp + (duration / 60)
394 time = localtime(x.begin)
396 if x.repeated & (2 ** y):
397 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
398 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
399 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
400 elif chktimecmp <= timecmp < chktimecmp_end:
401 time_match = (chktimecmp_end - timecmp) * 60
402 else: #if x.eit is None:
403 if begin <= x.begin <= end:
405 if time_match < diff:
407 elif x.begin <= begin <= x.end:
409 if time_match < diff:
413 def removeEntry(self, entry):
414 print "[Timer] Remove " + str(entry)
417 entry.repeated = False
420 # this sets the end time to current time, so timer will be stopped.
423 if entry.state != entry.StateEnded:
424 self.timeChanged(entry)
426 print "state: ", entry.state
427 print "in processed: ", entry in self.processed_timers
428 print "in running: ", entry in self.timer_list
429 # now the timer should be in the processed_timers list. remove it from there.
430 self.processed_timers.remove(entry)