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.cancelled = False
65 self.first_try_prepare = True
68 def calculateFilename(self):
69 service_name = self.service_ref.getServiceName()
70 begin_date = time.strftime("%Y%m%d %H%M", time.localtime(self.begin))
72 print "begin_date: ", begin_date
73 print "service_name: ", service_name
74 print "name:", self.name
75 print "description: ", self.description
77 filename = begin_date + " - " + service_name
79 filename += " - " + self.name
81 self.Filename = Directories.getRecordingFilename(filename)
82 self.log(0, "Filename calculated as: '%s'" % self.Filename)
83 #begin_date + " - " + service_name + description)
89 self.calculateFilename()
90 self.record_service = NavigationInstance.instance.recordService(self.service_ref)
91 if self.record_service == None:
92 self.log(1, "'record service' failed")
98 prep_res = self.record_service.prepare(self.Filename + ".ts", self.begin, self.end, event_id )
100 self.log(2, "'prepare' failed: error %d" % prep_res)
101 self.record_service = None
104 self.log(3, "prepare ok, writing meta information to %s" % self.Filename)
106 f = open(self.Filename + ".ts.meta", "w")
107 f.write(str(self.service_ref) + "\n")
108 f.write(self.name + "\n")
109 f.write(self.description + "\n")
110 f.write(str(self.begin) + "\n")
113 self.log(4, "failed to write meta information")
116 def do_backoff(self):
117 if self.backoff == 0:
121 if self.backoff > 100:
123 self.log(10, "backoff: retry in %d seconds" % self.backoff)
126 next_state = self.state + 1
127 self.log(5, "activating state %d" % next_state)
129 if next_state == self.StatePrepared:
130 if self.tryPrepare():
131 self.log(6, "prepare ok, waiting for begin")
132 # fine. it worked, resources are allocated.
133 self.next_activation = self.begin
137 self.log(7, "prepare failed")
138 if self.first_try_prepare:
139 self.first_try_prepare = False
140 if config.recording.asktozap.value == 0:
141 self.log(8, "asking user to zap away")
142 Notifications.AddNotificationWithCallback(self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n"))
143 else: # zap without asking
144 self.log(9, "zap without asking")
149 self.start_prepare = time.time() + self.backoff
151 elif next_state == self.StateRunning:
152 # if this timer has been cancelled, just go to "end" state.
157 self.log(11, "zapping")
158 NavigationInstance.instance.playService(self.service_ref.ref)
161 self.log(11, "start recording")
162 record_res = self.record_service.start()
165 self.log(13, "start record returned %d" % record_res)
168 self.begin = time.time() + self.backoff
172 elif next_state == self.StateEnded:
173 self.log(12, "stop recording")
174 if not self.justplay:
175 self.record_service.stop()
176 self.record_service = None
179 def getNextActivation(self):
180 if self.state == self.StateEnded:
183 next_state = self.state + 1
185 return {self.StatePrepared: self.start_prepare,
186 self.StateRunning: self.begin,
187 self.StateEnded: self.end }[next_state]
189 def failureCB(self, answer):
191 self.log(13, "ok, zapped away")
192 #NavigationInstance.instance.stopUserServices()
193 NavigationInstance.instance.playService(self.service_ref.ref)
195 self.log(14, "user didn't want to zap away, record will probably fail")
197 def timeChanged(self):
198 old_prepare = self.start_prepare
199 self.start_prepare = self.begin - self.prepare_time
202 if old_prepare != self.start_prepare:
203 self.log(15, "record time changed, start prepare is now: %s" % time.ctime(self.start_prepare))
205 def createTimer(xml):
206 begin = int(xml.getAttribute("begin"))
207 end = int(xml.getAttribute("end"))
208 serviceref = ServiceReference(str(xml.getAttribute("serviceref")))
209 description = xml.getAttribute("description").encode("utf-8")
210 repeated = xml.getAttribute("repeated").encode("utf-8")
211 disabled = long(xml.getAttribute("disabled") or "0")
212 justplay = long(xml.getAttribute("justplay") or "0")
213 if xml.hasAttribute("eit") and xml.getAttribute("eit") != "None":
214 eit = long(xml.getAttribute("eit"))
218 name = xml.getAttribute("name").encode("utf-8")
219 #filename = xml.getAttribute("filename").encode("utf-8")
220 entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay)
221 entry.repeated = int(repeated)
223 for l in elementsWithTag(xml.childNodes, "log"):
224 time = int(l.getAttribute("time"))
225 code = int(l.getAttribute("code"))
226 msg = mergeText(l.childNodes).strip().encode("utf-8")
227 entry.log_entries.append((time, code, msg))
231 class RecordTimer(timer.Timer):
233 timer.Timer.__init__(self)
235 self.Filename = Directories.resolveFilename(Directories.SCOPE_CONFIG, "timers.xml")
240 print "unable to load timers from file!"
242 def isRecording(self):
244 for timer in self.timer_list:
245 if timer.isRunning():
251 doc = xml.dom.minidom.parse(self.Filename)
253 root = doc.childNodes[0]
254 for timer in elementsWithTag(root.childNodes, "timer"):
255 self.record(createTimer(timer))
258 #doc = xml.dom.minidom.Document()
259 #root_element = doc.createElement('timers')
260 #doc.appendChild(root_element)
261 #root_element.appendChild(doc.createTextNode("\n"))
263 #for timer in self.timer_list + self.processed_timers:
264 # some timers (instant records) don't want to be saved.
268 #t = doc.createTextNode("\t")
269 #root_element.appendChild(t)
270 #t = doc.createElement('timer')
271 #t.setAttribute("begin", str(int(timer.begin)))
272 #t.setAttribute("end", str(int(timer.end)))
273 #t.setAttribute("serviceref", str(timer.service_ref))
274 #t.setAttribute("repeated", str(timer.repeated))
275 #t.setAttribute("name", timer.name)
276 #t.setAttribute("description", timer.description)
277 #t.setAttribute("eit", str(timer.eit))
279 #for time, code, msg in timer.log_entries:
280 #t.appendChild(doc.createTextNode("\t\t"))
281 #l = doc.createElement('log')
282 #l.setAttribute("time", str(time))
283 #l.setAttribute("code", str(code))
284 #l.appendChild(doc.createTextNode(msg))
286 #t.appendChild(doc.createTextNode("\n"))
288 #root_element.appendChild(t)
289 #t = doc.createTextNode("\n")
290 #root_element.appendChild(t)
293 #file = open(self.Filename, "w")
300 list.append('<?xml version="1.0" ?>\n')
301 list.append('<timers>\n')
303 for timer in self.timer_list + self.processed_timers:
307 list.append('<timer')
308 list.append(' begin="' + str(int(timer.begin)) + '"')
309 list.append(' end="' + str(int(timer.end)) + '"')
310 list.append(' serviceref="' + str(timer.service_ref) + '"')
311 list.append(' repeated="' + str(int(timer.repeated)) + '"')
312 list.append(' name="' + str(stringToXML(timer.name)) + '"')
313 list.append(' description="' + str(stringToXML(timer.description)) + '"')
314 if timer.eit is not None:
315 list.append(' eit="' + str(timer.eit) + '"')
316 list.append(' disabled="' + str(int(timer.disabled)) + '"')
317 list.append(' justplay="' + str(int(timer.justplay)) + '"')
320 #for time, code, msg in timer.log_entries:
322 #list.append(' code="' + str(code) + '"')
323 #list.append(' time="' + str(time) + '"')
325 #list.append(str(msg))
326 #list.append('</log>\n')
329 list.append('</timer>\n')
331 list.append('</timers>\n')
333 file = open(self.Filename, "w")
338 def record(self, entry):
340 print "[Timer] Record " + str(entry)
342 self.addTimerEntry(entry)
344 def isInTimer(self, eventid, begin, duration, service):
346 for x in self.timer_list:
347 if str(x.service_ref) == str(service):
348 #if x.eit is not None and x.repeated == 0:
349 # if x.eit == eventid:
352 chktime = localtime(begin)
353 time = localtime(x.begin)
354 chktimecmp = chktime.tm_wday * 1440 + chktime.tm_hour * 60 + chktime.tm_min
356 if x.repeated & (2 ** y):
357 timecmp = y * 1440 + time.tm_hour * 60 + time.tm_min
358 if timecmp <= chktimecmp < (timecmp + ((x.end - x.begin) / 60)):
359 time_match = ((timecmp + ((x.end - x.begin) / 60)) - chktimecmp) * 60
360 elif chktimecmp <= timecmp < (chktimecmp + (duration / 60)):
361 time_match = ((chktimecmp + (duration / 60)) - timecmp) * 60
362 else: #if x.eit is None:
363 end = begin + duration
364 if begin <= x.begin <= end:
366 if time_match < diff:
368 elif x.begin <= begin <= x.end:
370 if time_match < diff:
374 def removeEntry(self, entry):
375 print "[Timer] Remove " + str(entry)
378 entry.repeated = False
381 # this sets the end time to current time, so timer will be stopped.
384 if entry.state != entry.StateEnded:
385 self.timeChanged(entry)
387 print "state: ", entry.state
388 print "in processed: ", entry in self.processed_timers
389 print "in running: ", entry in self.timer_list
390 # now the timer should be in the processed_timers list. remove it from there.
391 self.processed_timers.remove(entry)