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 from Screens.SubserviceSelection import SubserviceSelection
14 import NavigationInstance
15 from time import localtime
17 from Tools.XMLTools import elementsWithTag, mergeText, stringToXML
18 from ServiceReference import ServiceReference
20 # ok, for descriptions etc we have:
21 # service reference (to get the service name)
23 # description (description)
24 # event data (ONLY for time adjustments etc.)
27 # parses an event, and gives out a (begin, end, name, duration, eit)-tuple.
28 # begin and end will be corrected
30 name = ev.getEventName()
31 description = ev.getShortDescription()
32 begin = ev.getBeginTime()
33 end = begin + ev.getDuration()
35 begin -= config.recording.margin_before.value[0] * 60
36 end += config.recording.margin_after.value[0] * 60
37 return (begin, end, name, description, eit)
44 # please do not translate log messages
45 class RecordTimerEntry(timer.TimerEntry):
46 def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.NONE):
47 timer.TimerEntry.__init__(self, int(begin), int(end))
49 assert isinstance(serviceref, ServiceReference)
51 self.service_ref = serviceref
55 self.description = description
56 self.disabled = disabled
58 self.record_service = None
59 self.start_prepare = 0
60 self.justplay = justplay
61 self.afterEvent = afterEvent
67 def log(self, code, msg):
68 self.log_entries.append((int(time.time()), code, msg))
72 self.state = self.StateWaiting
73 self.cancelled = False
74 self.first_try_prepare = True
77 def calculateFilename(self):
78 service_name = self.service_ref.getServiceName()
79 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
81 print "begin_date: ", begin_date
82 print "service_name: ", service_name
83 print "name:", self.name
84 print "description: ", self.description
86 filename = begin_date + " - " + service_name
88 filename += " - " + self.name
90 self.Filename = Directories.getRecordingFilename(filename)
91 self.log(0, "Filename calculated as: '%s'" % self.Filename)
92 #begin_date + " - " + service_name + description)
98 self.calculateFilename()
99 self.record_service = NavigationInstance.instance.recordService(self.service_ref)
100 if self.record_service == None:
101 self.log(1, "'record service' failed")
107 prep_res = self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id )
109 self.log(2, "'prepare' failed: error %d" % prep_res)
110 self.record_service = None
113 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
115 f = open(self.Filename + ".ts.meta", "w")
116 f.write(str(self.service_ref) + "\n")
117 f.write(self.name + "\n")
118 f.write(self.description + "\n")
119 f.write(str(self.begin) + "\n")
122 self.log(4, "failed to write meta information")
125 def do_backoff(self):
126 if self.backoff == 0:
130 if self.backoff > 100:
132 self.log(10, "backoff: retry in %d seconds" % self.backoff)
135 next_state = self.state + 1
136 self.log(5, "activating state %d" % next_state)
138 if next_state == self.StatePrepared:
139 if self.tryPrepare():
140 self.log(6, "prepare ok, waiting for begin")
141 # fine. it worked, resources are allocated.
142 self.next_activation = self.begin
146 self.log(7, "prepare failed")
147 if self.first_try_prepare:
148 self.first_try_prepare = False
149 if config.recording.asktozap.value == 0:
150 self.log(8, "asking user to zap away")
151 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"))
152 else: # zap without asking
153 self.log(9, "zap without asking")
158 self.start_prepare = time.time() + self.backoff
160 elif next_state == self.StateRunning:
161 # if this timer has been cancelled, just go to "end" state.
166 self.log(11, "zapping")
167 NavigationInstance.instance.playService(self.service_ref.ref)
170 self.log(11, "start recording")
171 record_res = self.record_service.start()
174 self.log(13, "start record returned %d" % record_res)
177 self.begin = time.time() + self.backoff
181 elif next_state == self.StateEnded:
182 self.log(12, "stop recording")
183 if not self.justplay:
184 self.record_service.stop()
185 self.record_service = None
186 if self.afterEvent == AFTEREVENT.STANDBY:
187 if self.session is not None:
188 self.session.open(Standby, self)
189 elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
193 def getNextActivation(self):
194 if self.state == self.StateEnded:
197 next_state = self.state + 1
199 return {self.StatePrepared: self.start_prepare,
200 self.StateRunning: self.begin,
201 self.StateEnded: self.end }[next_state]
203 def failureCB(self, answer):
205 self.log(13, "ok, zapped away")
206 #NavigationInstance.instance.stopUserServices()
207 NavigationInstance.instance.playService(self.service_ref.ref)
209 self.log(14, "user didn't want to zap away, record will probably fail")
211 def timeChanged(self):
212 old_prepare = self.start_prepare
213 self.start_prepare = self.begin - self.prepare_time
216 if old_prepare != self.start_prepare:
217 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
219 def createTimer(xml):
220 begin = int(xml.getAttribute("begin"))
221 end = int(xml.getAttribute("end"))
222 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
223 description = xml.getAttribute("description").encode("utf-8")
224 repeated = xml.getAttribute("repeated").encode("utf-8")
225 disabled = long(xml.getAttribute("disabled") or "0")
226 justplay = long(xml.getAttribute("justplay") or "0")
227 afterevent = str(xml.getAttribute("afterevent") or "nothing")
228 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
229 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
230 eit = long(xml.getAttribute("eit"))
234 name = xml.getAttribute("name").encode("utf-8")
235 #filename = xml.getAttribute("filename").encode("utf-8")
236 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
237 entry.repeated = int(repeated)
239 for l in elementsWithTag(xml.childNodes, "log"):
240 time = int(l.getAttribute("time"))
241 code = int(l.getAttribute("code"))
242 msg = mergeText(l.childNodes).strip().encode("utf-8")
243 entry.log_entries.append((time, code, msg))
247 class RecordTimer(timer.Timer):
249 timer.Timer.__init__(self)
251 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
256 print "unable to load timers from file!"
258 def isRecording(self):
260 for timer in self.timer_list:
261 if timer.isRunning() and not timer.justplay:
267 doc = xml.dom.minidom.parse(self.Filename)
269 root = doc.childNodes[0]
270 for timer in elementsWithTag(root.childNodes, "timer"):
271 self.record(createTimer(timer))
274 #doc = xml.dom.minidom.Document()
275 #root_element = doc.createElement('timers')
276 #doc.appendChild(root_element)
277 #root_element.appendChild(doc.createTextNode("\n"))
279 #for timer in self.timer_list + self.processed_timers:
280 # some timers (instant records) don't want to be saved.
284 #t = doc.createTextNode("\t")
285 #root_element.appendChild(t)
286 #t = doc.createElement('timer')
287 #t.setAttribute("begin", str(int(timer.begin)))
288 #t.setAttribute("end", str(int(timer.end)))
289 #t.setAttribute("serviceref", str(timer.service_ref))
290 #t.setAttribute("repeated", str(timer.repeated))
291 #t.setAttribute("name", timer.name)
292 #t.setAttribute("description", timer.description)
293 #t.setAttribute("eit", str(timer.eit))
295 #for time, code, msg in timer.log_entries:
296 #t.appendChild(doc.createTextNode("\t\t"))
297 #l = doc.createElement('log')
298 #l.setAttribute("time", str(time))
299 #l.setAttribute("code", str(code))
300 #l.appendChild(doc.createTextNode(msg))
302 #t.appendChild(doc.createTextNode("\n"))
304 #root_element.appendChild(t)
305 #t = doc.createTextNode("\n")
306 #root_element.appendChild(t)
309 #file = open(self.Filename, "w")
316 list.append('<?xml version="1.0" ?>\n')
317 list.append('<timers>\n')
319 for timer in self.timer_list + self.processed_timers:
323 list.append('<timer')
324 list.append(' begin="' + str(int(timer.begin)) + '"')
325 list.append(' end="' + str(int(timer.end)) + '"')
326 list.append(' serviceref="' + str(timer.service_ref) + '"')
327 list.append(' repeated="' + str(int(timer.repeated)) + '"')
328 list.append(' name="' + str(stringToXML(timer.name)) + '"')
329 list.append(' description="' + str(stringToXML(timer.description)) + '"')
330 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
331 if timer.eit is not None:
332 list.append(' eit="' + str(timer.eit) + '"')
333 list.append(' disabled="' + str(int(timer.disabled)) + '"')
334 list.append(' justplay="' + str(int(timer.justplay)) + '"')
337 #for time, code, msg in timer.log_entries:
339 #list.append(' code="' + str(code) + '"')
340 #list.append(' time="' + str(time) + '"')
342 #list.append(str(msg))
343 #list.append('</log>\n')
346 list.append('</timer>\n')
348 list.append('</timers>\n')
350 file = open(self.Filename, "w")
355 def record(self, entry):
357 print "[Timer] Record " + str(entry)
359 self.addTimerEntry(entry)
361 def isInTimer(self, eventid, begin, duration, service):
365 chktimecmp_end = None
366 end = begin + duration
367 for x in self.timer_list:
368 if str(x.service_ref) == str(service):
369 #if x.eit is not None and x.repeated == 0:
370 # if x.eit == eventid:
374 chktime = localtime(begin)
375 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
376 chktimecmp_end = chktimecmp + (duration / 60)
377 time = localtime(x.begin)
379 if x.repeated & (2 ** y):
380 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
381 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
382 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
383 elif chktimecmp <= timecmp < chktimecmp_end:
384 time_match = (chktimecmp_end - timecmp) * 60
385 else: #if x.eit is None:
386 if begin <= x.begin <= end:
388 if time_match < diff:
390 elif x.begin <= begin <= x.end:
392 if time_match < diff:
396 def removeEntry(self, entry):
397 print "[Timer] Remove " + str(entry)
400 entry.repeated = False
403 # this sets the end time to current time, so timer will be stopped.
406 if entry.state != entry.StateEnded:
407 self.timeChanged(entry)
409 print "state: ", entry.state
410 print "in processed: ", entry in self.processed_timers
411 print "in running: ", entry in self.timer_list
412 # now the timer should be in the processed_timers list. remove it from there.
413 self.processed_timers.remove(entry)