3 #from time import datetime
4 from Tools import Directories, Notifications
6 from Components.config import config
10 from Screens.MessageBox import MessageBox
11 from Screens.SubserviceSelection import SubserviceSelection
12 import NavigationInstance
13 from time import localtime
15 from Tools.XMLTools import elementsWithTag, mergeText, stringToXML
16 from ServiceReference import ServiceReference
18 # ok, for descriptions etc we have:
19 # service reference (to get the service name)
21 # description (description)
22 # event data (ONLY for time adjustments etc.)
25 # parses an event, and gives out a (begin, end, name, duration, eit)-tuple.
26 # begin and end will be corrected
28 name = ev.getEventName()
29 description = ev.getShortDescription()
30 begin = ev.getBeginTime()
31 end = begin + ev.getDuration()
33 begin -= config.recording.margin_before.value[0] * 60
34 end += config.recording.margin_after.value[0] * 60
35 return (begin, end, name, description, eit)
37 # please do not translate log messages
38 class RecordTimerEntry(timer.TimerEntry):
39 def __init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False):
40 timer.TimerEntry.__init__(self, int(begin), int(end))
42 assert isinstance(serviceref, ServiceReference)
44 self.service_ref = serviceref
48 self.description = description
49 self.disabled = disabled
51 self.record_service = None
52 self.start_prepare = 0
53 self.justplay = justplay
58 def log(self, code, msg):
59 self.log_entries.append((int(time.time()), code, msg))
63 self.state = self.StateWaiting
64 self.first_try_prepare = True
67 def calculateFilename(self):
68 service_name = self.service_ref.getServiceName()
69 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
71 print "begin_date: ", begin_date
72 print "service_name: ", service_name
73 print "name:", self.name
74 print "description: ", self.description
76 filename = begin_date + " - " + service_name
78 filename += " - " + self.name
80 self.Filename = Directories.getRecordingFilename(filename)
81 self.log(0, "Filename calculated as: '%s'" % self.Filename)
82 #begin_date + " - " + service_name + description)
88 self.calculateFilename()
89 self.record_service = NavigationInstance.instance.recordService(self.service_ref)
90 if self.record_service == None:
91 self.log(1, "'record service' failed")
97 prep_res = self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id )
99 self.log(2, "'prepare' failed: error %d" % prep_res)
100 self.record_service = None
103 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
105 f = open(self.Filename + ".ts.meta", "w")
106 f.write(str(self.service_ref) + "\n")
107 f.write(self.name + "\n")
108 f.write(self.description + "\n")
109 f.write(str(self.begin) + "\n")
112 self.log(4, "failed to write meta information")
115 def do_backoff(self):
116 if self.backoff == 0:
120 if self.backoff > 100:
122 self.log(10, "backoff: retry in %d seconds" % self.backoff)
125 next_state = self.state + 1
126 self.log(5, "activating state %d" % next_state)
128 if next_state == self.StatePrepared:
129 if self.tryPrepare():
130 self.log(6, "prepare ok, waiting for begin")
131 # fine. it worked, resources are allocated.
132 self.next_activation = self.begin
136 self.log(7, "prepare failed")
137 if self.first_try_prepare:
138 self.first_try_prepare = False
139 if config.recording.asktozap.value == 0:
140 self.log(8, "asking user to zap away")
141 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"))
142 else: # zap without asking
143 self.log(9, "zap without asking")
148 self.start_prepare = time.time() + self.backoff
150 elif next_state == self.StateRunning:
152 self.log(11, "zapping")
153 NavigationInstance.instance.playService(self.service_ref.ref)
156 self.log(11, "start recording")
157 record_res = self.record_service.start()
160 self.log(13, "start record returned %d" % record_res)
163 self.begin = time.time() + self.backoff
167 elif next_state == self.StateEnded:
168 self.log(12, "stop recording")
169 self.record_service.stop()
170 self.record_service = None
173 def getNextActivation(self):
174 if self.state == self.StateEnded:
177 next_state = self.state + 1
179 return {self.StatePrepared: self.start_prepare,
180 self.StateRunning: self.begin,
181 self.StateEnded: self.end }[next_state]
183 def failureCB(self, answer):
185 self.log(13, "ok, zapped away")
186 #NavigationInstance.instance.stopUserServices()
187 NavigationInstance.instance.playService(self.service_ref.ref)
189 self.log(14, "user didn't want to zap away, record will probably fail")
191 def timeChanged(self):
192 old_prepare = self.start_prepare
193 self.start_prepare = self.begin - self.prepare_time
196 if old_prepare != self.start_prepare:
197 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
199 def createTimer(xml):
200 begin = int(xml.getAttribute("begin"))
201 end = int(xml.getAttribute("end"))
202 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
203 description = xml.getAttribute("description").encode("utf-8")
204 repeated = xml.getAttribute("repeated").encode("utf-8")
205 disabled = long(xml.getAttribute("disabled") or "0")
206 justplay = long(xml.getAttribute("justplay") or "0")
207 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
208 eit = long(xml.getAttribute("eit"))
212 name = xml.getAttribute("name").encode("utf-8")
213 #filename = xml.getAttribute("filename").encode("utf-8")
214 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay)
215 entry.repeated = int(repeated)
217 for l in elementsWithTag(xml.childNodes, "log"):
218 time = int(l.getAttribute("time"))
219 code = int(l.getAttribute("code"))
220 msg = mergeText(l.childNodes).strip().encode("utf-8")
221 entry.log_entries.append((time, code, msg))
225 class RecordTimer(timer.Timer):
227 timer.Timer.__init__(self)
229 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
234 print "unable to load timers from file!"
236 def isRecording(self):
238 for timer in self.timer_list:
239 if timer.isRunning():
245 doc = xml.dom.minidom.parse(self.Filename)
247 root = doc.childNodes[0]
248 for timer in elementsWithTag(root.childNodes, "timer"):
249 self.record(createTimer(timer))
252 #doc = xml.dom.minidom.Document()
253 #root_element = doc.createElement('timers')
254 #doc.appendChild(root_element)
255 #root_element.appendChild(doc.createTextNode("\n"))
257 #for timer in self.timer_list + self.processed_timers:
258 # some timers (instant records) don't want to be saved.
262 #t = doc.createTextNode("\t")
263 #root_element.appendChild(t)
264 #t = doc.createElement('timer')
265 #t.setAttribute("begin", str(int(timer.begin)))
266 #t.setAttribute("end", str(int(timer.end)))
267 #t.setAttribute("serviceref", str(timer.service_ref))
268 #t.setAttribute("repeated", str(timer.repeated))
269 #t.setAttribute("name", timer.name)
270 #t.setAttribute("description", timer.description)
271 #t.setAttribute("eit", str(timer.eit))
273 #for time, code, msg in timer.log_entries:
274 #t.appendChild(doc.createTextNode("\t\t"))
275 #l = doc.createElement('log')
276 #l.setAttribute("time", str(time))
277 #l.setAttribute("code", str(code))
278 #l.appendChild(doc.createTextNode(msg))
280 #t.appendChild(doc.createTextNode("\n"))
282 #root_element.appendChild(t)
283 #t = doc.createTextNode("\n")
284 #root_element.appendChild(t)
287 #file = open(self.Filename, "w")
294 list.append('<?xml version="1.0" ?>\n')
295 list.append('<timers>\n')
297 for timer in self.timer_list + self.processed_timers:
301 list.append('<timer')
302 list.append(' begin="' + str(int(timer.begin)) + '"')
303 list.append(' end="' + str(int(timer.end)) + '"')
304 list.append(' serviceref="' + str(timer.service_ref) + '"')
305 list.append(' repeated="' + str(int(timer.repeated)) + '"')
306 list.append(' name="' + str(stringToXML(timer.name)) + '"')
307 list.append(' description="' + str(stringToXML(timer.description)) + '"')
308 if timer.eit is not None:
309 list.append(' eit="' + str(timer.eit) + '"')
310 list.append(' disabled="' + str(int(timer.disabled)) + '"')
311 list.append(' justplay="' + str(int(timer.justplay)) + '"')
314 #for time, code, msg in timer.log_entries:
316 #list.append(' code="' + str(code) + '"')
317 #list.append(' time="' + str(time) + '"')
319 #list.append(str(msg))
320 #list.append('</log>\n')
323 list.append('</timer>\n')
325 list.append('</timers>\n')
327 file = open(self.Filename, "w")
332 def record(self, entry):
334 print "[Timer] Record " + str(entry)
336 self.addTimerEntry(entry)
338 def isInTimer(self, eventid, begin, duration, service):
340 for x in self.timer_list:
341 if str(x.service_ref) == str(service):
342 #if x.eit is not None and x.repeated == 0:
343 # if x.eit == eventid:
346 chktime = localtime(begin)
347 time = localtime(x.begin)
348 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
350 if x.repeated & (2 ** y):
351 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
352 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
353 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
354 elif chktimecmp <= timecmp < (chktimecmp + (duration / 60)):
355 time_match = ((chktimecmp + (duration / 60)) - timecmp) * 60
356 else: #if x.eit is None:
357 end = begin + duration
358 if begin <= x.begin <= end:
360 if time_match < diff:
362 elif x.begin <= begin <= x.end:
364 if time_match < diff:
372 def removeEntry(self, entry):
373 print "[Timer] Remove " + str(entry)
376 entry.repeated = False
379 # this sets the end time to current time, so timer will be stopped.
382 if entry.state != entry.StateEnded:
383 self.timeChanged(entry)
385 print "state: ", entry.state
386 print "in processed: ", entry in self.processed_timers
387 print "in running: ", entry in self.timer_list
388 # now the timer should be in the processed_timers list. remove it from there.
389 self.processed_timers.remove(entry)