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, checkOldTimers = False):
46 timer.TimerEntry.__init__(self, int(begin), int(end))
48 if checkOldTimers == True:
49 if self.begin < time.time() - 1209600:
50 self.begin = int(time.time())
52 if self.end < self.begin:
55 assert isinstance(serviceref, ServiceReference)
57 self.service_ref = serviceref
61 self.description = description
62 self.disabled = disabled
64 self.record_service = None
65 self.start_prepare = 0
66 self.justplay = justplay
67 self.afterEvent = afterEvent
73 def log(self, code, msg):
74 self.log_entries.append((int(time.time()), code, msg))
78 self.state = self.StateWaiting
79 self.cancelled = False
80 self.first_try_prepare = True
83 def calculateFilename(self):
84 service_name = self.service_ref.getServiceName()
85 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
87 print "begin_date: ", begin_date
88 print "service_name: ", service_name
89 print "name:", self.name
90 print "description: ", self.description
92 filename = begin_date + " - " + service_name
94 filename += " - " + self.name
96 self.Filename = Directories.getRecordingFilename(filename)
97 self.log(0, "Filename calculated as: '%s'" % self.Filename)
98 #begin_date + " - " + service_name + description)
100 def tryPrepare(self):
104 self.calculateFilename()
105 self.record_service = NavigationInstance.instance.recordService(self.service_ref)
106 if self.record_service == None:
107 self.log(1, "'record service' failed")
113 prep_res = self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id )
115 self.log(2, "'prepare' failed: error %d" % prep_res)
116 self.record_service = None
119 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
121 f = open(self.Filename + ".ts.meta", "w")
122 f.write(str(self.service_ref) + "\n")
123 f.write(self.name + "\n")
124 f.write(self.description + "\n")
125 f.write(str(self.begin) + "\n")
128 self.log(4, "failed to write meta information")
131 def do_backoff(self):
132 if self.backoff == 0:
136 if self.backoff > 100:
138 self.log(10, "backoff: retry in %d seconds" % self.backoff)
141 next_state = self.state + 1
142 self.log(5, "activating state %d" % next_state)
144 if next_state == self.StatePrepared:
145 if self.tryPrepare():
146 self.log(6, "prepare ok, waiting for begin")
147 # fine. it worked, resources are allocated.
148 self.next_activation = self.begin
152 self.log(7, "prepare failed")
153 if self.first_try_prepare:
154 self.first_try_prepare = False
155 if config.recording.asktozap.value == 0:
156 self.log(8, "asking user to zap away")
157 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"))
158 else: # zap without asking
159 self.log(9, "zap without asking")
160 Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_WARNING)
165 self.start_prepare = time.time() + self.backoff
167 elif next_state == self.StateRunning:
168 # if this timer has been cancelled, just go to "end" state.
173 self.log(11, "zapping")
174 NavigationInstance.instance.playService(self.service_ref.ref)
177 self.log(11, "start recording")
178 record_res = self.record_service.start()
181 self.log(13, "start record returned %d" % record_res)
184 self.begin = time.time() + self.backoff
188 elif next_state == self.StateEnded:
189 self.log(12, "stop recording")
190 if not self.justplay:
191 self.record_service.stop()
192 self.record_service = None
193 if self.afterEvent == AFTEREVENT.STANDBY:
194 if self.session is not None:
195 self.session.open(Standby, self)
196 elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
200 def getNextActivation(self):
201 if self.state == self.StateEnded:
204 next_state = self.state + 1
206 return {self.StatePrepared: self.start_prepare,
207 self.StateRunning: self.begin,
208 self.StateEnded: self.end }[next_state]
210 def failureCB(self, answer):
212 self.log(13, "ok, zapped away")
213 #NavigationInstance.instance.stopUserServices()
214 NavigationInstance.instance.playService(self.service_ref.ref)
216 self.log(14, "user didn't want to zap away, record will probably fail")
218 def timeChanged(self):
219 old_prepare = self.start_prepare
220 self.start_prepare = self.begin - self.prepare_time
223 if old_prepare != self.start_prepare:
224 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
226 def createTimer(xml):
227 begin = int(xml.getAttribute("begin"))
228 end = int(xml.getAttribute("end"))
229 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
230 description = xml.getAttribute("description").encode("utf-8")
231 repeated = xml.getAttribute("repeated").encode("utf-8")
232 disabled = long(xml.getAttribute("disabled") or "0")
233 justplay = long(xml.getAttribute("justplay") or "0")
234 afterevent = str(xml.getAttribute("afterevent") or "nothing")
235 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
236 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
237 eit = long(xml.getAttribute("eit"))
241 name = xml.getAttribute("name").encode("utf-8")
242 #filename = xml.getAttribute("filename").encode("utf-8")
243 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
244 entry.repeated = int(repeated)
246 for l in elementsWithTag(xml.childNodes, "log"):
247 time = int(l.getAttribute("time"))
248 code = int(l.getAttribute("code"))
249 msg = mergeText(l.childNodes).strip().encode("utf-8")
250 entry.log_entries.append((time, code, msg))
254 class RecordTimer(timer.Timer):
256 timer.Timer.__init__(self)
258 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
263 print "unable to load timers from file!"
265 def isRecording(self):
267 for timer in self.timer_list:
268 if timer.isRunning() and not timer.justplay:
274 doc = xml.dom.minidom.parse(self.Filename)
276 root = doc.childNodes[0]
277 for timer in elementsWithTag(root.childNodes, "timer"):
278 self.record(createTimer(timer))
281 #doc = xml.dom.minidom.Document()
282 #root_element = doc.createElement('timers')
283 #doc.appendChild(root_element)
284 #root_element.appendChild(doc.createTextNode("\n"))
286 #for timer in self.timer_list + self.processed_timers:
287 # some timers (instant records) don't want to be saved.
291 #t = doc.createTextNode("\t")
292 #root_element.appendChild(t)
293 #t = doc.createElement('timer')
294 #t.setAttribute("begin", str(int(timer.begin)))
295 #t.setAttribute("end", str(int(timer.end)))
296 #t.setAttribute("serviceref", str(timer.service_ref))
297 #t.setAttribute("repeated", str(timer.repeated))
298 #t.setAttribute("name", timer.name)
299 #t.setAttribute("description", timer.description)
300 #t.setAttribute("eit", str(timer.eit))
302 #for time, code, msg in timer.log_entries:
303 #t.appendChild(doc.createTextNode("\t\t"))
304 #l = doc.createElement('log')
305 #l.setAttribute("time", str(time))
306 #l.setAttribute("code", str(code))
307 #l.appendChild(doc.createTextNode(msg))
309 #t.appendChild(doc.createTextNode("\n"))
311 #root_element.appendChild(t)
312 #t = doc.createTextNode("\n")
313 #root_element.appendChild(t)
316 #file = open(self.Filename, "w")
323 list.append('<?xml version="1.0" ?>\n')
324 list.append('<timers>\n')
326 for timer in self.timer_list + self.processed_timers:
330 list.append('<timer')
331 list.append(' begin="' + str(int(timer.begin)) + '"')
332 list.append(' end="' + str(int(timer.end)) + '"')
333 list.append(' serviceref="' + str(timer.service_ref) + '"')
334 list.append(' repeated="' + str(int(timer.repeated)) + '"')
335 list.append(' name="' + str(stringToXML(timer.name)) + '"')
336 list.append(' description="' + str(stringToXML(timer.description)) + '"')
337 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
338 if timer.eit is not None:
339 list.append(' eit="' + str(timer.eit) + '"')
340 list.append(' disabled="' + str(int(timer.disabled)) + '"')
341 list.append(' justplay="' + str(int(timer.justplay)) + '"')
344 #for time, code, msg in timer.log_entries:
346 #list.append(' code="' + str(code) + '"')
347 #list.append(' time="' + str(time) + '"')
349 #list.append(str(msg))
350 #list.append('</log>\n')
353 list.append('</timer>\n')
355 list.append('</timers>\n')
357 file = open(self.Filename, "w")
362 def record(self, entry):
364 print "[Timer] Record " + str(entry)
366 self.addTimerEntry(entry)
368 def isInTimer(self, eventid, begin, duration, service):
372 chktimecmp_end = None
373 end = begin + duration
374 for x in self.timer_list:
375 if str(x.service_ref) == str(service):
376 #if x.eit is not None and x.repeated == 0:
377 # if x.eit == eventid:
381 chktime = localtime(begin)
382 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
383 chktimecmp_end = chktimecmp + (duration / 60)
384 time = localtime(x.begin)
386 if x.repeated & (2 ** y):
387 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
388 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
389 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
390 elif chktimecmp <= timecmp < chktimecmp_end:
391 time_match = (chktimecmp_end - timecmp) * 60
392 else: #if x.eit is None:
393 if begin <= x.begin <= end:
395 if time_match < diff:
397 elif x.begin <= begin <= x.end:
399 if time_match < diff:
403 def removeEntry(self, entry):
404 print "[Timer] Remove " + str(entry)
407 entry.repeated = False
410 # this sets the end time to current time, so timer will be stopped.
413 if entry.state != entry.StateEnded:
414 self.timeChanged(entry)
416 print "state: ", entry.state
417 print "in processed: ", entry in self.processed_timers
418 print "in running: ", entry in self.timer_list
419 # now the timer should be in the processed_timers list. remove it from there.
420 self.processed_timers.remove(entry)