3 #from time import datetime
4 from Tools import Directories, Notifications
6 from Components.config import config
10 from enigma import quitMainloop
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
29 name = ev.getEventName()
30 description = ev.getShortDescription()
31 begin = ev.getBeginTime()
32 end = begin + ev.getDuration()
34 begin -= config.recording.margin_before.value[0] * 60
35 end += config.recording.margin_after.value[0] * 60
36 return (begin, end, name, description, eit)
43 # please do not translate log messages
44 class RecordTimerEntry(timer.TimerEntry):
45 def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.NONE):
46 timer.TimerEntry.__init__(self, int(begin), int(end))
48 assert isinstance(serviceref, ServiceReference)
50 self.service_ref = serviceref
54 self.description = description
55 self.disabled = disabled
57 self.record_service = None
58 self.start_prepare = 0
59 self.justplay = justplay
60 self.afterEvent = afterEvent
66 def log(self, code, msg):
67 self.log_entries.append((int(time.time()), code, msg))
71 self.state = self.StateWaiting
72 self.cancelled = False
73 self.first_try_prepare = True
76 def calculateFilename(self):
77 service_name = self.service_ref.getServiceName()
78 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
80 print "begin_date: ", begin_date
81 print "service_name: ", service_name
82 print "name:", self.name
83 print "description: ", self.description
85 filename = begin_date + " - " + service_name
87 filename += " - " + self.name
89 self.Filename = Directories.getRecordingFilename(filename)
90 self.log(0, "Filename calculated as: '%s'" % self.Filename)
91 #begin_date + " - " + service_name + description)
97 self.calculateFilename()
98 self.record_service = NavigationInstance.instance.recordService(self.service_ref)
99 if self.record_service == None:
100 self.log(1, "'record service' failed")
106 prep_res = self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id )
108 self.log(2, "'prepare' failed: error %d" % prep_res)
109 self.record_service = None
112 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
114 f = open(self.Filename + ".ts.meta", "w")
115 f.write(str(self.service_ref) + "\n")
116 f.write(self.name + "\n")
117 f.write(self.description + "\n")
118 f.write(str(self.begin) + "\n")
121 self.log(4, "failed to write meta information")
124 def do_backoff(self):
125 if self.backoff == 0:
129 if self.backoff > 100:
131 self.log(10, "backoff: retry in %d seconds" % self.backoff)
134 next_state = self.state + 1
135 self.log(5, "activating state %d" % next_state)
137 if next_state == self.StatePrepared:
138 if self.tryPrepare():
139 self.log(6, "prepare ok, waiting for begin")
140 # fine. it worked, resources are allocated.
141 self.next_activation = self.begin
145 self.log(7, "prepare failed")
146 if self.first_try_prepare:
147 self.first_try_prepare = False
148 if config.recording.asktozap.value == 0:
149 self.log(8, "asking user to zap away")
150 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"))
151 else: # zap without asking
152 self.log(9, "zap without asking")
157 self.start_prepare = time.time() + self.backoff
159 elif next_state == self.StateRunning:
160 # if this timer has been cancelled, just go to "end" state.
165 self.log(11, "zapping")
166 NavigationInstance.instance.playService(self.service_ref.ref)
169 self.log(11, "start recording")
170 record_res = self.record_service.start()
173 self.log(13, "start record returned %d" % record_res)
176 self.begin = time.time() + self.backoff
180 elif next_state == self.StateEnded:
181 self.log(12, "stop recording")
182 if not self.justplay:
183 self.record_service.stop()
184 self.record_service = None
185 if self.afterEvent == AFTEREVENT.STANDBY:
186 if self.session is not None:
187 self.session.open(Standby, self)
188 elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
192 def getNextActivation(self):
193 if self.state == self.StateEnded:
196 next_state = self.state + 1
198 return {self.StatePrepared: self.start_prepare,
199 self.StateRunning: self.begin,
200 self.StateEnded: self.end }[next_state]
202 def failureCB(self, answer):
204 self.log(13, "ok, zapped away")
205 #NavigationInstance.instance.stopUserServices()
206 NavigationInstance.instance.playService(self.service_ref.ref)
208 self.log(14, "user didn't want to zap away, record will probably fail")
210 def timeChanged(self):
211 old_prepare = self.start_prepare
212 self.start_prepare = self.begin - self.prepare_time
215 if old_prepare != self.start_prepare:
216 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
218 def createTimer(xml):
219 begin = int(xml.getAttribute("begin"))
220 end = int(xml.getAttribute("end"))
221 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
222 description = xml.getAttribute("description").encode("utf-8")
223 repeated = xml.getAttribute("repeated").encode("utf-8")
224 disabled = long(xml.getAttribute("disabled") or "0")
225 justplay = long(xml.getAttribute("justplay") or "0")
226 afterevent = str(xml.getAttribute("afterevent") or "nothing")
227 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
228 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
229 eit = long(xml.getAttribute("eit"))
233 name = xml.getAttribute("name").encode("utf-8")
234 #filename = xml.getAttribute("filename").encode("utf-8")
235 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
236 entry.repeated = int(repeated)
238 for l in elementsWithTag(xml.childNodes, "log"):
239 time = int(l.getAttribute("time"))
240 code = int(l.getAttribute("code"))
241 msg = mergeText(l.childNodes).strip().encode("utf-8")
242 entry.log_entries.append((time, code, msg))
246 class RecordTimer(timer.Timer):
248 timer.Timer.__init__(self)
250 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
255 print "unable to load timers from file!"
257 def isRecording(self):
259 for timer in self.timer_list:
260 if timer.isRunning() and not timer.justplay:
266 doc = xml.dom.minidom.parse(self.Filename)
268 root = doc.childNodes[0]
269 for timer in elementsWithTag(root.childNodes, "timer"):
270 self.record(createTimer(timer))
273 #doc = xml.dom.minidom.Document()
274 #root_element = doc.createElement('timers')
275 #doc.appendChild(root_element)
276 #root_element.appendChild(doc.createTextNode("\n"))
278 #for timer in self.timer_list + self.processed_timers:
279 # some timers (instant records) don't want to be saved.
283 #t = doc.createTextNode("\t")
284 #root_element.appendChild(t)
285 #t = doc.createElement('timer')
286 #t.setAttribute("begin", str(int(timer.begin)))
287 #t.setAttribute("end", str(int(timer.end)))
288 #t.setAttribute("serviceref", str(timer.service_ref))
289 #t.setAttribute("repeated", str(timer.repeated))
290 #t.setAttribute("name", timer.name)
291 #t.setAttribute("description", timer.description)
292 #t.setAttribute("eit", str(timer.eit))
294 #for time, code, msg in timer.log_entries:
295 #t.appendChild(doc.createTextNode("\t\t"))
296 #l = doc.createElement('log')
297 #l.setAttribute("time", str(time))
298 #l.setAttribute("code", str(code))
299 #l.appendChild(doc.createTextNode(msg))
301 #t.appendChild(doc.createTextNode("\n"))
303 #root_element.appendChild(t)
304 #t = doc.createTextNode("\n")
305 #root_element.appendChild(t)
308 #file = open(self.Filename, "w")
315 list.append('<?xml version="1.0" ?>\n')
316 list.append('<timers>\n')
318 for timer in self.timer_list + self.processed_timers:
322 list.append('<timer')
323 list.append(' begin="' + str(int(timer.begin)) + '"')
324 list.append(' end="' + str(int(timer.end)) + '"')
325 list.append(' serviceref="' + str(timer.service_ref) + '"')
326 list.append(' repeated="' + str(int(timer.repeated)) + '"')
327 list.append(' name="' + str(stringToXML(timer.name)) + '"')
328 list.append(' description="' + str(stringToXML(timer.description)) + '"')
329 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
330 if timer.eit is not None:
331 list.append(' eit="' + str(timer.eit) + '"')
332 list.append(' disabled="' + str(int(timer.disabled)) + '"')
333 list.append(' justplay="' + str(int(timer.justplay)) + '"')
336 #for time, code, msg in timer.log_entries:
338 #list.append(' code="' + str(code) + '"')
339 #list.append(' time="' + str(time) + '"')
341 #list.append(str(msg))
342 #list.append('</log>\n')
345 list.append('</timer>\n')
347 list.append('</timers>\n')
349 file = open(self.Filename, "w")
354 def record(self, entry):
356 print "[Timer] Record " + str(entry)
358 self.addTimerEntry(entry)
360 def isInTimer(self, eventid, begin, duration, service):
364 chktimecmp_end = None
365 end = begin + duration
366 for x in self.timer_list:
367 if str(x.service_ref) == str(service):
368 #if x.eit is not None and x.repeated == 0:
369 # if x.eit == eventid:
373 chktime = localtime(begin)
374 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
375 chktimecmp_end = chktimecmp + (duration / 60)
376 time = localtime(x.begin)
378 if x.repeated & (2 ** y):
379 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
380 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
381 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
382 elif chktimecmp <= timecmp < chktimecmp_end:
383 time_match = (chktimecmp_end - timecmp) * 60
384 else: #if x.eit is None:
385 if begin <= x.begin <= end:
387 if time_match < diff:
389 elif x.begin <= begin <= x.end:
391 if time_match < diff:
395 def removeEntry(self, entry):
396 print "[Timer] Remove " + str(entry)
399 entry.repeated = False
402 # this sets the end time to current time, so timer will be stopped.
405 if entry.state != entry.StateEnded:
406 self.timeChanged(entry)
408 print "state: ", entry.state
409 print "in processed: ", entry in self.processed_timers
410 print "in running: ", entry in self.timer_list
411 # now the timer should be in the processed_timers list. remove it from there.
412 self.processed_timers.remove(entry)