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
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):
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
57 def log(self, code, msg):
58 self.log_entries.append((int(time.time()), code, msg))
62 self.state = self.StateWaiting
63 self.first_try_prepare = True
66 def calculateFilename(self):
67 service_name = self.service_ref.getServiceName()
68 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
70 print "begin_date: ", begin_date
71 print "service_name: ", service_name
72 print "name:", self.name
73 print "description: ", self.description
75 filename = begin_date + " - " + service_name
77 filename += " - " + self.name
79 self.Filename = Directories.getRecordingFilename(filename)
80 self.log(0, "Filename calculated as: '%s'" % self.Filename)
81 #begin_date + " - " + service_name + description)
84 self.calculateFilename()
85 self.record_service = NavigationInstance.instance.recordService(self.service_ref)
86 if self.record_service == None:
87 self.log(1, "'record service' failed")
93 prep_res = self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id )
95 self.log(2, "'prepare' failed: error %d" % prep_res)
96 self.record_service = None
99 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
101 f = open(self.Filename + ".ts.meta", "w")
102 f.write(str(self.service_ref) + "\n")
103 f.write(self.name + "\n")
104 f.write(self.description + "\n")
105 f.write(str(self.begin) + "\n")
108 self.log(4, "failed to write meta information")
111 def do_backoff(self):
112 if self.backoff == 0:
116 if self.backoff > 100:
118 self.log(10, "backoff: retry in %d seconds" % self.backoff)
121 next_state = self.state + 1
122 self.log(5, "activating state %d" % next_state)
124 if next_state == self.StatePrepared:
125 if self.tryPrepare():
126 self.log(6, "prepare ok, waiting for begin")
127 # fine. it worked, resources are allocated.
128 self.next_activation = self.begin
132 self.log(7, "prepare failed")
133 if self.first_try_prepare:
134 self.first_try_prepare = False
135 if config.recording.asktozap.value == 0:
136 self.log(8, "asking user to zap away")
137 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"))
138 else: # zap without asking
139 self.log(9, "zap without asking")
144 self.start_prepare = time.time() + self.backoff
146 elif next_state == self.StateRunning:
147 self.log(11, "start recording")
148 record_res = self.record_service.start()
151 self.log(13, "start record returned %d" % record_res)
154 self.begin = time.time() + self.backoff
158 elif next_state == self.StateEnded:
159 self.log(12, "stop recording")
160 self.record_service.stop()
161 self.record_service = None
164 def getNextActivation(self):
165 if self.state == self.StateEnded:
168 next_state = self.state + 1
170 return {self.StatePrepared: self.start_prepare,
171 self.StateRunning: self.begin,
172 self.StateEnded: self.end }[next_state]
174 def failureCB(self, answer):
176 self.log(13, "ok, zapped away")
177 #NavigationInstance.instance.stopUserServices()
178 NavigationInstance.instance.playService(self.service_ref.ref)
180 self.log(14, "user didn't want to zap away, record will probably fail")
182 def timeChanged(self):
183 old_prepare = self.start_prepare
184 self.start_prepare = self.begin - self.prepare_time
187 if old_prepare != self.start_prepare:
188 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
190 def createTimer(xml):
191 begin = int(xml.getAttribute("begin"))
192 end = int(xml.getAttribute("end"))
193 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
194 description = xml.getAttribute("description").encode("utf-8")
195 repeated = xml.getAttribute("repeated").encode("utf-8")
196 disabled = long(xml.getAttribute("disabled") or "0")
197 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
198 eit = long(xml.getAttribute("eit"))
202 name = xml.getAttribute("name").encode("utf-8")
203 #filename = xml.getAttribute("filename").encode("utf-8")
204 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled)
205 entry.repeated = int(repeated)
207 for l in elementsWithTag(xml.childNodes, "log"):
208 time = int(l.getAttribute("time"))
209 code = int(l.getAttribute("code"))
210 msg = mergeText(l.childNodes).strip().encode("utf-8")
211 entry.log_entries.append((time, code, msg))
215 class RecordTimer(timer.Timer):
217 timer.Timer.__init__(self)
219 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
224 print "unable to load timers from file!"
226 def isRecording(self):
228 for timer in self.timer_list:
229 if timer.isRunning():
235 doc = xml.dom.minidom.parse(self.Filename)
237 root = doc.childNodes[0]
238 for timer in elementsWithTag(root.childNodes, "timer"):
239 self.record(createTimer(timer))
241 def strToXML(self, str):
242 return str.replace('&', '&').replace('<', '<').replace('>', '>').replace("'", '''). replace('"', '&qout;')
245 #doc = xml.dom.minidom.Document()
246 #root_element = doc.createElement('timers')
247 #doc.appendChild(root_element)
248 #root_element.appendChild(doc.createTextNode("\n"))
250 #for timer in self.timer_list + self.processed_timers:
251 # some timers (instant records) don't want to be saved.
255 #t = doc.createTextNode("\t")
256 #root_element.appendChild(t)
257 #t = doc.createElement('timer')
258 #t.setAttribute("begin", str(int(timer.begin)))
259 #t.setAttribute("end", str(int(timer.end)))
260 #t.setAttribute("serviceref", str(timer.service_ref))
261 #t.setAttribute("repeated", str(timer.repeated))
262 #t.setAttribute("name", timer.name)
263 #t.setAttribute("description", timer.description)
264 #t.setAttribute("eit", str(timer.eit))
266 #for time, code, msg in timer.log_entries:
267 #t.appendChild(doc.createTextNode("\t\t"))
268 #l = doc.createElement('log')
269 #l.setAttribute("time", str(time))
270 #l.setAttribute("code", str(code))
271 #l.appendChild(doc.createTextNode(msg))
273 #t.appendChild(doc.createTextNode("\n"))
275 #root_element.appendChild(t)
276 #t = doc.createTextNode("\n")
277 #root_element.appendChild(t)
280 #file = open(self.Filename, "w")
287 list.append('<?xml version="1.0" ?>\n')
288 list.append('<timers>\n')
290 for timer in self.timer_list + self.processed_timers:
294 list.append('<timer')
295 list.append(' begin="' + str(int(timer.begin)) + '"')
296 list.append(' end="' + str(int(timer.end)) + '"')
297 list.append(' serviceref="' + str(timer.service_ref) + '"')
298 list.append(' repeated="' + str(int(timer.repeated)) + '"')
299 list.append(' name="' + str(self.strToXML(timer.name)) + '"')
300 list.append(' description="' + str(self.strToXML(timer.description)) + '"')
301 if timer.eit is not None:
302 list.append(' eit="' + str(timer.eit) + '"')
303 list.append(' disabled="' + str(int(timer.disabled)) + '"')
306 for time, code, msg in timer.log_entries:
308 list.append(' code="' + str(code) + '"')
309 list.append(' time="' + str(time) + '"')
311 list.append(str(msg))
312 list.append('</log>\n')
315 list.append('</timer>\n')
317 list.append('</timers>\n')
319 file = open(self.Filename, "w")
324 def record(self, entry):
326 print "[Timer] Record " + str(entry)
328 self.addTimerEntry(entry)
330 def isInTimer(self, eventid, begin, duration, service):
332 for x in self.timer_list:
333 if str(x.service_ref) == str(service):
334 #if x.eit is not None and x.repeated == 0:
335 # if x.eit == eventid:
338 chktime = localtime(begin)
339 time = localtime(x.begin)
340 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
342 if x.repeated & (2 ** y):
343 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
344 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
345 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
346 elif chktimecmp <= timecmp < (chktimecmp + (duration / 60)):
347 time_match = ((chktimecmp + (duration / 60)) - timecmp) * 60
349 end = begin + duration
350 if begin <= x.begin <= end:
352 if time_match < diff:
354 elif x.begin <= begin <= x.end:
356 if time_match < diff:
364 def removeEntry(self, entry):
365 print "[Timer] Remove " + str(entry)
368 entry.repeated = False
371 # this sets the end time to current time, so timer will be stopped.
374 if entry.state != entry.StateEnded:
375 self.timeChanged(entry)
377 print "state: ", entry.state
378 print "in processed: ", entry in self.processed_timers
379 print "in running: ", entry in self.timer_list
380 # now the timer should be in the processed_timers list. remove it from there.
381 self.processed_timers.remove(entry)