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 if not self.justplay:
170 self.record_service.stop()
171 self.record_service = None
174 def getNextActivation(self):
175 if self.state == self.StateEnded:
178 next_state = self.state + 1
180 return {self.StatePrepared: self.start_prepare,
181 self.StateRunning: self.begin,
182 self.StateEnded: self.end }[next_state]
184 def failureCB(self, answer):
186 self.log(13, "ok, zapped away")
187 #NavigationInstance.instance.stopUserServices()
188 NavigationInstance.instance.playService(self.service_ref.ref)
190 self.log(14, "user didn't want to zap away, record will probably fail")
192 def timeChanged(self):
193 old_prepare = self.start_prepare
194 self.start_prepare = self.begin - self.prepare_time
197 if old_prepare != self.start_prepare:
198 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
200 def createTimer(xml):
201 begin = int(xml.getAttribute("begin"))
202 end = int(xml.getAttribute("end"))
203 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
204 description = xml.getAttribute("description").encode("utf-8")
205 repeated = xml.getAttribute("repeated").encode("utf-8")
206 disabled = long(xml.getAttribute("disabled") or "0")
207 justplay = long(xml.getAttribute("justplay") or "0")
208 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
209 eit = long(xml.getAttribute("eit"))
213 name = xml.getAttribute("name").encode("utf-8")
214 #filename = xml.getAttribute("filename").encode("utf-8")
215 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay)
216 entry.repeated = int(repeated)
218 for l in elementsWithTag(xml.childNodes, "log"):
219 time = int(l.getAttribute("time"))
220 code = int(l.getAttribute("code"))
221 msg = mergeText(l.childNodes).strip().encode("utf-8")
222 entry.log_entries.append((time, code, msg))
226 class RecordTimer(timer.Timer):
228 timer.Timer.__init__(self)
230 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
235 print "unable to load timers from file!"
237 def isRecording(self):
239 for timer in self.timer_list:
240 if timer.isRunning():
246 doc = xml.dom.minidom.parse(self.Filename)
248 root = doc.childNodes[0]
249 for timer in elementsWithTag(root.childNodes, "timer"):
250 self.record(createTimer(timer))
253 #doc = xml.dom.minidom.Document()
254 #root_element = doc.createElement('timers')
255 #doc.appendChild(root_element)
256 #root_element.appendChild(doc.createTextNode("\n"))
258 #for timer in self.timer_list + self.processed_timers:
259 # some timers (instant records) don't want to be saved.
263 #t = doc.createTextNode("\t")
264 #root_element.appendChild(t)
265 #t = doc.createElement('timer')
266 #t.setAttribute("begin", str(int(timer.begin)))
267 #t.setAttribute("end", str(int(timer.end)))
268 #t.setAttribute("serviceref", str(timer.service_ref))
269 #t.setAttribute("repeated", str(timer.repeated))
270 #t.setAttribute("name", timer.name)
271 #t.setAttribute("description", timer.description)
272 #t.setAttribute("eit", str(timer.eit))
274 #for time, code, msg in timer.log_entries:
275 #t.appendChild(doc.createTextNode("\t\t"))
276 #l = doc.createElement('log')
277 #l.setAttribute("time", str(time))
278 #l.setAttribute("code", str(code))
279 #l.appendChild(doc.createTextNode(msg))
281 #t.appendChild(doc.createTextNode("\n"))
283 #root_element.appendChild(t)
284 #t = doc.createTextNode("\n")
285 #root_element.appendChild(t)
288 #file = open(self.Filename, "w")
295 list.append('<?xml version="1.0" ?>\n')
296 list.append('<timers>\n')
298 for timer in self.timer_list + self.processed_timers:
302 list.append('<timer')
303 list.append(' begin="' + str(int(timer.begin)) + '"')
304 list.append(' end="' + str(int(timer.end)) + '"')
305 list.append(' serviceref="' + str(timer.service_ref) + '"')
306 list.append(' repeated="' + str(int(timer.repeated)) + '"')
307 list.append(' name="' + str(stringToXML(timer.name)) + '"')
308 list.append(' description="' + str(stringToXML(timer.description)) + '"')
309 if timer.eit is not None:
310 list.append(' eit="' + str(timer.eit) + '"')
311 list.append(' disabled="' + str(int(timer.disabled)) + '"')
312 list.append(' justplay="' + str(int(timer.justplay)) + '"')
315 #for time, code, msg in timer.log_entries:
317 #list.append(' code="' + str(code) + '"')
318 #list.append(' time="' + str(time) + '"')
320 #list.append(str(msg))
321 #list.append('</log>\n')
324 list.append('</timer>\n')
326 list.append('</timers>\n')
328 file = open(self.Filename, "w")
333 def record(self, entry):
335 print "[Timer] Record " + str(entry)
337 self.addTimerEntry(entry)
339 def isInTimer(self, eventid, begin, duration, service):
341 for x in self.timer_list:
342 if str(x.service_ref) == str(service):
343 #if x.eit is not None and x.repeated == 0:
344 # if x.eit == eventid:
347 chktime = localtime(begin)
348 time = localtime(x.begin)
349 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
351 if x.repeated & (2 ** y):
352 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
353 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
354 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
355 elif chktimecmp <= timecmp < (chktimecmp + (duration / 60)):
356 time_match = ((chktimecmp + (duration / 60)) - timecmp) * 60
357 else: #if x.eit is None:
358 end = begin + duration
359 if begin <= x.begin <= end:
361 if time_match < diff:
363 elif x.begin <= begin <= x.end:
365 if time_match < diff:
373 def removeEntry(self, entry):
374 print "[Timer] Remove " + str(entry)
377 entry.repeated = False
380 # this sets the end time to current time, so timer will be stopped.
383 if entry.state != entry.StateEnded:
384 self.timeChanged(entry)
386 print "state: ", entry.state
387 print "in processed: ", entry in self.processed_timers
388 print "in running: ", entry in self.timer_list
389 # now the timer should be in the processed_timers list. remove it from there.
390 self.processed_timers.remove(entry)