3 #from time import datetime
4 from Tools import Directories, Notifications
6 from Components.config import config
10 from enigma import quitMainloop, eEPGCache, eEPGCachePtr
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
120 epgcache = eEPGCache.getInstance()
121 queryTime=self.begin+(self.end-self.begin)/2
122 evt = epgcache.lookupEventTime(self.service_ref.ref, queryTime)
124 self.description = evt.getShortDescription()
125 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
127 f = open(self.Filename + ".ts.meta", "w")
128 f.write(str(self.service_ref) + "\n")
129 f.write(self.name + "\n")
130 f.write(self.description + "\n")
131 f.write(str(self.begin) + "\n")
134 self.log(4, "failed to write meta information")
137 def do_backoff(self):
138 if self.backoff == 0:
142 if self.backoff > 100:
144 self.log(10, "backoff: retry in %d seconds" % self.backoff)
147 next_state = self.state + 1
148 self.log(5, "activating state %d" % next_state)
150 if next_state == self.StatePrepared:
151 if self.tryPrepare():
152 self.log(6, "prepare ok, waiting for begin")
153 # fine. it worked, resources are allocated.
154 self.next_activation = self.begin
158 self.log(7, "prepare failed")
159 if self.first_try_prepare:
160 self.first_try_prepare = False
161 if config.recording.asktozap.value == 0:
162 self.log(8, "asking user to zap away")
163 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"), timeout=20)
164 else: # zap without asking
165 self.log(9, "zap without asking")
166 Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20)
171 self.start_prepare = time.time() + self.backoff
173 elif next_state == self.StateRunning:
174 # if this timer has been cancelled, just go to "end" state.
179 self.log(11, "zapping")
180 NavigationInstance.instance.playService(self.service_ref.ref)
183 self.log(11, "start recording")
184 record_res = self.record_service.start()
187 self.log(13, "start record returned %d" % record_res)
190 self.begin = time.time() + self.backoff
194 elif next_state == self.StateEnded:
195 self.log(12, "stop recording")
196 if not self.justplay:
197 self.record_service.stop()
198 self.record_service = None
199 if self.afterEvent == AFTEREVENT.STANDBY:
200 if self.session is not None:
201 self.session.open(Standby, self)
202 elif self.afterEvent == AFTEREVENT.DEEPSTANDBY:
206 def getNextActivation(self):
207 if self.state == self.StateEnded:
210 next_state = self.state + 1
212 return {self.StatePrepared: self.start_prepare,
213 self.StateRunning: self.begin,
214 self.StateEnded: self.end }[next_state]
216 def failureCB(self, answer):
218 self.log(13, "ok, zapped away")
219 #NavigationInstance.instance.stopUserServices()
220 NavigationInstance.instance.playService(self.service_ref.ref)
222 self.log(14, "user didn't want to zap away, record will probably fail")
224 def timeChanged(self):
225 old_prepare = self.start_prepare
226 self.start_prepare = self.begin - self.prepare_time
229 if old_prepare != self.start_prepare:
230 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
232 def createTimer(xml):
233 begin = int(xml.getAttribute("begin"))
234 end = int(xml.getAttribute("end"))
235 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
236 description = xml.getAttribute("description").encode("utf-8")
237 repeated = xml.getAttribute("repeated").encode("utf-8")
238 disabled = long(xml.getAttribute("disabled") or "0")
239 justplay = long(xml.getAttribute("justplay") or "0")
240 afterevent = str(xml.getAttribute("afterevent") or "nothing")
241 afterevent = { "nothing": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY }[afterevent]
242 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
243 eit = long(xml.getAttribute("eit"))
247 name = xml.getAttribute("name").encode("utf-8")
248 #filename = xml.getAttribute("filename").encode("utf-8")
249 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent)
250 entry.repeated = int(repeated)
252 for l in elementsWithTag(xml.childNodes, "log"):
253 time = int(l.getAttribute("time"))
254 code = int(l.getAttribute("code"))
255 msg = mergeText(l.childNodes).strip().encode("utf-8")
256 entry.log_entries.append((time, code, msg))
260 class RecordTimer(timer.Timer):
262 timer.Timer.__init__(self)
264 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
269 print "unable to load timers from file!"
271 def isRecording(self):
273 for timer in self.timer_list:
274 if timer.isRunning() and not timer.justplay:
280 doc = xml.dom.minidom.parse(self.Filename)
282 root = doc.childNodes[0]
283 for timer in elementsWithTag(root.childNodes, "timer"):
284 self.record(createTimer(timer))
287 #doc = xml.dom.minidom.Document()
288 #root_element = doc.createElement('timers')
289 #doc.appendChild(root_element)
290 #root_element.appendChild(doc.createTextNode("\n"))
292 #for timer in self.timer_list + self.processed_timers:
293 # some timers (instant records) don't want to be saved.
297 #t = doc.createTextNode("\t")
298 #root_element.appendChild(t)
299 #t = doc.createElement('timer')
300 #t.setAttribute("begin", str(int(timer.begin)))
301 #t.setAttribute("end", str(int(timer.end)))
302 #t.setAttribute("serviceref", str(timer.service_ref))
303 #t.setAttribute("repeated", str(timer.repeated))
304 #t.setAttribute("name", timer.name)
305 #t.setAttribute("description", timer.description)
306 #t.setAttribute("eit", str(timer.eit))
308 #for time, code, msg in timer.log_entries:
309 #t.appendChild(doc.createTextNode("\t\t"))
310 #l = doc.createElement('log')
311 #l.setAttribute("time", str(time))
312 #l.setAttribute("code", str(code))
313 #l.appendChild(doc.createTextNode(msg))
315 #t.appendChild(doc.createTextNode("\n"))
317 #root_element.appendChild(t)
318 #t = doc.createTextNode("\n")
319 #root_element.appendChild(t)
322 #file = open(self.Filename, "w")
329 list.append('<?xml version="1.0" ?>\n')
330 list.append('<timers>\n')
332 for timer in self.timer_list + self.processed_timers:
336 list.append('<timer')
337 list.append(' begin="' + str(int(timer.begin)) + '"')
338 list.append(' end="' + str(int(timer.end)) + '"')
339 list.append(' serviceref="' + str(timer.service_ref) + '"')
340 list.append(' repeated="' + str(int(timer.repeated)) + '"')
341 list.append(' name="' + str(stringToXML(timer.name)) + '"')
342 list.append(' description="' + str(stringToXML(timer.description)) + '"')
343 list.append(' afterevent="' + str(stringToXML({ AFTEREVENT.NONE: "nothing", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "deepstandby" }[timer.afterEvent])) + '"')
344 if timer.eit is not None:
345 list.append(' eit="' + str(timer.eit) + '"')
346 list.append(' disabled="' + str(int(timer.disabled)) + '"')
347 list.append(' justplay="' + str(int(timer.justplay)) + '"')
350 #for time, code, msg in timer.log_entries:
352 #list.append(' code="' + str(code) + '"')
353 #list.append(' time="' + str(time) + '"')
355 #list.append(str(msg))
356 #list.append('</log>\n')
359 list.append('</timer>\n')
361 list.append('</timers>\n')
363 file = open(self.Filename, "w")
368 def record(self, entry):
370 print "[Timer] Record " + str(entry)
372 self.addTimerEntry(entry)
374 def isInTimer(self, eventid, begin, duration, service):
378 chktimecmp_end = None
379 end = begin + duration
380 for x in self.timer_list:
381 if str(x.service_ref) == str(service):
382 #if x.eit is not None and x.repeated == 0:
383 # if x.eit == eventid:
387 chktime = localtime(begin)
388 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
389 chktimecmp_end = chktimecmp + (duration / 60)
390 time = localtime(x.begin)
392 if x.repeated & (2 ** y):
393 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
394 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
395 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
396 elif chktimecmp <= timecmp < chktimecmp_end:
397 time_match = (chktimecmp_end - timecmp) * 60
398 else: #if x.eit is None:
399 if begin <= x.begin <= end:
401 if time_match < diff:
403 elif x.begin <= begin <= x.end:
405 if time_match < diff:
409 def removeEntry(self, entry):
410 print "[Timer] Remove " + str(entry)
413 entry.repeated = False
416 # this sets the end time to current time, so timer will be stopped.
419 if entry.state != entry.StateEnded:
420 self.timeChanged(entry)
422 print "state: ", entry.state
423 print "in processed: ", entry in self.processed_timers
424 print "in running: ", entry in self.timer_list
425 # now the timer should be in the processed_timers list. remove it from there.
426 self.processed_timers.remove(entry)