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")
164 self.start_prepare = time.time() + self.backoff
166 elif next_state == self.StateRunning:
167 # if this timer has been cancelled, just go to "end" state.
172 self.log(11, "zapping")
173 NavigationInstance.instance.playService(self.service_ref.ref)
176 self.log(11, "start recording")
177 record_res = self.record_service.start()
180 self.log(13, "start record returned %d" % record_res)
183 self.begin = time.time() + self.backoff
187 elif next_state == self.StateEnded:
188 self.log(12, "stop recording")
189 if not self.justplay:
190 self.record_service.stop()
191 self.record_service = None
192 if self.afterEvent == AFTEREVENT.STANDBY:
193 if self.session is not None:
194 self.session.open(Standby, self)
195 elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
199 def getNextActivation(self):
200 if self.state == self.StateEnded:
203 next_state = self.state + 1
205 return {self.StatePrepared: self.start_prepare,
206 self.StateRunning: self.begin,
207 self.StateEnded: self.end }[next_state]
209 def failureCB(self, answer):
211 self.log(13, "ok, zapped away")
212 #NavigationInstance.instance.stopUserServices()
213 NavigationInstance.instance.playService(self.service_ref.ref)
215 self.log(14, "user didn't want to zap away, record will probably fail")
217 def timeChanged(self):
218 old_prepare = self.start_prepare
219 self.start_prepare = self.begin - self.prepare_time
222 if old_prepare != self.start_prepare:
223 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
225 def createTimer(xml):
226 begin = int(xml.getAttribute("begin"))
227 end = int(xml.getAttribute("end"))
228 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
229 description = xml.getAttribute("description").encode("utf-8")
230 repeated = xml.getAttribute("repeated").encode("utf-8")
231 disabled = long(xml.getAttribute("disabled") or "0")
232 justplay = long(xml.getAttribute("justplay") or "0")
233 afterevent = str(xml.getAttribute("afterevent") or "nothing")
234 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
235 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
236 eit = long(xml.getAttribute("eit"))
240 name = xml.getAttribute("name").encode("utf-8")
241 #filename = xml.getAttribute("filename").encode("utf-8")
242 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
243 entry.repeated = int(repeated)
245 for l in elementsWithTag(xml.childNodes, "log"):
246 time = int(l.getAttribute("time"))
247 code = int(l.getAttribute("code"))
248 msg = mergeText(l.childNodes).strip().encode("utf-8")
249 entry.log_entries.append((time, code, msg))
253 class RecordTimer(timer.Timer):
255 timer.Timer.__init__(self)
257 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
262 print "unable to load timers from file!"
264 def isRecording(self):
266 for timer in self.timer_list:
267 if timer.isRunning() and not timer.justplay:
273 doc = xml.dom.minidom.parse(self.Filename)
275 root = doc.childNodes[0]
276 for timer in elementsWithTag(root.childNodes, "timer"):
277 self.record(createTimer(timer))
280 #doc = xml.dom.minidom.Document()
281 #root_element = doc.createElement('timers')
282 #doc.appendChild(root_element)
283 #root_element.appendChild(doc.createTextNode("\n"))
285 #for timer in self.timer_list + self.processed_timers:
286 # some timers (instant records) don't want to be saved.
290 #t = doc.createTextNode("\t")
291 #root_element.appendChild(t)
292 #t = doc.createElement('timer')
293 #t.setAttribute("begin", str(int(timer.begin)))
294 #t.setAttribute("end", str(int(timer.end)))
295 #t.setAttribute("serviceref", str(timer.service_ref))
296 #t.setAttribute("repeated", str(timer.repeated))
297 #t.setAttribute("name", timer.name)
298 #t.setAttribute("description", timer.description)
299 #t.setAttribute("eit", str(timer.eit))
301 #for time, code, msg in timer.log_entries:
302 #t.appendChild(doc.createTextNode("\t\t"))
303 #l = doc.createElement('log')
304 #l.setAttribute("time", str(time))
305 #l.setAttribute("code", str(code))
306 #l.appendChild(doc.createTextNode(msg))
308 #t.appendChild(doc.createTextNode("\n"))
310 #root_element.appendChild(t)
311 #t = doc.createTextNode("\n")
312 #root_element.appendChild(t)
315 #file = open(self.Filename, "w")
322 list.append('<?xml version="1.0" ?>\n')
323 list.append('<timers>\n')
325 for timer in self.timer_list + self.processed_timers:
329 list.append('<timer')
330 list.append(' begin="' + str(int(timer.begin)) + '"')
331 list.append(' end="' + str(int(timer.end)) + '"')
332 list.append(' serviceref="' + str(timer.service_ref) + '"')
333 list.append(' repeated="' + str(int(timer.repeated)) + '"')
334 list.append(' name="' + str(stringToXML(timer.name)) + '"')
335 list.append(' description="' + str(stringToXML(timer.description)) + '"')
336 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
337 if timer.eit is not None:
338 list.append(' eit="' + str(timer.eit) + '"')
339 list.append(' disabled="' + str(int(timer.disabled)) + '"')
340 list.append(' justplay="' + str(int(timer.justplay)) + '"')
343 #for time, code, msg in timer.log_entries:
345 #list.append(' code="' + str(code) + '"')
346 #list.append(' time="' + str(time) + '"')
348 #list.append(str(msg))
349 #list.append('</log>\n')
352 list.append('</timer>\n')
354 list.append('</timers>\n')
356 file = open(self.Filename, "w")
361 def record(self, entry):
363 print "[Timer] Record " + str(entry)
365 self.addTimerEntry(entry)
367 def isInTimer(self, eventid, begin, duration, service):
371 chktimecmp_end = None
372 end = begin + duration
373 for x in self.timer_list:
374 if str(x.service_ref) == str(service):
375 #if x.eit is not None and x.repeated == 0:
376 # if x.eit == eventid:
380 chktime = localtime(begin)
381 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
382 chktimecmp_end = chktimecmp + (duration / 60)
383 time = localtime(x.begin)
385 if x.repeated & (2 ** y):
386 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
387 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
388 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
389 elif chktimecmp <= timecmp < chktimecmp_end:
390 time_match = (chktimecmp_end - timecmp) * 60
391 else: #if x.eit is None:
392 if begin <= x.begin <= end:
394 if time_match < diff:
396 elif x.begin <= begin <= x.end:
398 if time_match < diff:
402 def removeEntry(self, entry):
403 print "[Timer] Remove " + str(entry)
406 entry.repeated = False
409 # this sets the end time to current time, so timer will be stopped.
412 if entry.state != entry.StateEnded:
413 self.timeChanged(entry)
415 print "state: ", entry.state
416 print "in processed: ", entry in self.processed_timers
417 print "in running: ", entry in self.timer_list
418 # now the timer should be in the processed_timers list. remove it from there.
419 self.processed_timers.remove(entry)