Webif: fix backwards compat for timer creation and editing when eit param is missing
[enigma2-plugins.git] / webinterface / src / WebComponents / Sources / Timer.py
1 Version = '$Header$';
2
3 from enigma import eServiceReference, eEPGCache
4 from Components.config import config
5 from Components.Sources.Source import Source
6 from Components.TimerSanityCheck import TimerSanityCheck
7 from Components.UsageConfig import preferredInstantRecordPath, preferredTimerPath
8 from ServiceReference import ServiceReference
9 from RecordTimer import RecordTimerEntry, RecordTimer, AFTEREVENT, parseEvent
10
11 from xml.sax.saxutils import unescape
12 from time import time, strftime, localtime, mktime
13
14 class Timer(Source):
15         LIST = 0
16         ADDBYID = 1
17         ADD = 2
18         DEL = 3
19         TVBROWSER = 4
20         CHANGE = 5
21         WRITE = 6
22         RECNOW = 7
23         CLEANUP = 8
24
25         def __init__(self, session, func=LIST):
26                 self.func = func
27                 Source.__init__(self)
28                 self.session = session
29                 self.recordtimer = session.nav.RecordTimer
30                 self.epgcache = eEPGCache.getInstance()
31                 self.res = ( False, "unknown command" )
32
33         def handleCommand(self, cmd):
34                 if self.func is self.ADDBYID:
35                         self.res = self.addTimerByEventID(cmd)
36                         self.writeTimerList()
37
38                 elif self.func is self.ADD:
39                         self.res = self.editTimer(cmd)
40                         self.writeTimerList()
41
42                 elif self.func is self.TVBROWSER:
43                         self.res = self.tvBrowser(cmd)
44                         self.writeTimerList()
45
46                 elif self.func is self.DEL:
47                         self.res = self.delTimer(cmd)
48                         self.writeTimerList()
49
50                 elif self.func is self.CHANGE:
51                         self.res = self.editTimer(cmd)
52                         self.writeTimerList()
53
54                 elif self.func is self.WRITE:
55                         self.res = self.writeTimerList(force=True)
56
57                 elif self.func is self.RECNOW:
58                         self.res = self.recordNow(cmd)
59
60                 elif self.func is self.CLEANUP:
61                         self.res = self.cleanupTimer()
62
63                 else:
64                         self.res = ( False, "Unknown function: '%s'" % (self.func) )
65
66         def cleanupTimer(self):
67                 print "[WebComponents.Timer] cleanupTimer"
68
69                 self.session.nav.RecordTimer.cleanup()
70                 return ( True, "List of Timers has been cleaned" )
71
72
73         def delTimer(self, param):
74                 print "[WebComponents.Timer] delTimer"
75
76                 if 'sRef' in param:
77                         service_ref = ServiceReference(param['sRef'])
78                 else:
79                         return ( False, "Missing Parameter: sRef" )
80
81                 if 'begin' in param:
82                         begin = int(float(param['begin']))
83                 else:
84                         return ( False, "Missing Parameter: begin" )
85
86                 if 'end' in param:
87                         end = int(float(param['end']))
88                 else:
89                         return ( False, "Missing Parameter: end" )
90
91                 try:
92                         for timer in self.recordtimer.timer_list + self.recordtimer.processed_timers:
93                                 if str(timer.service_ref) == str(service_ref) and int(timer.begin) == begin and int(timer.end) == end:
94                                         self.recordtimer.removeEntry(timer)
95                                         return True, "The timer '%s' has been deleted successfully" % (timer.name)
96                 except Exception:
97                         return ( False, "The timer has NOT been deleted" )
98
99                 return False, "No matching Timer found"
100
101         def tvBrowser(self, param):
102                 """ The URL's for the tvBrowser-Capture-Driver are:
103
104                         http://dreambox/web/tvbrowser? +
105
106                 To add something:
107                         &command=add&&year={year}&month={month}&day={day}&shour={start_hour}&smin={start_minute}&ehour={end_hour}&emin={end_minute}&sRef={urlencode(channel_name_external, "utf8")}&name={urlencode(title, "utf8")}&description={urlencode(descr, "utf8")}&dirname={dirname}&tags={urlencode("tag1 tag2...", "utf8")}&afterevent=0&eit=&disabled=0&justplay=0&repeated=0
108
109                 to zap for some time:
110                         &command=add&&year={year}&month={month}&day={day}&shour={start_hour}&smin={start_minute}&ehour={end_hour}&emin={end_minute}&sRef={urlencode(channel_name_external, "utf8")}&name={urlencode(title, "utf8")}&description={urlencode(descr, "utf8")}&dirname={dirname}&tags={urlencode("tag1 tag2...", "utf8")}&afterevent=0&eit=&disabled=0&justplay=1&repeated=0
111
112                 to delete something:
113                         &command=del&&year={year}&month={month}&day={day}&shour={start_hour}&smin={start_minute}&ehour={end_hour}&emin={end_minute}&sRef={urlencode(channel_name_external, "utf8")}
114                 """
115                 print "[WebComponents.Timer] tvbrowser"
116
117                 listDate = ('year', 'month', 'day', 'shour', 'smin', 'ehour', 'emin')
118                 for element in listDate:
119                         if param[element] is None:
120                                 if param['s' + element] is None:
121                                         return ( False, "%s missing" % element )
122                                 else:
123                                         param[element] = int(param['s' + element])
124                         else:
125                                 param[element] = int(param[element])
126                 param['begin'] = int(mktime((param['year'], param['month'], param['day'], param['shour'], param['smin'], 0, 0, 0, -1)))
127                 param['end']     = int(mktime((param['year'], param['month'], param['day'], param['ehour'], param['emin'], 0, 0, 0, -1)))
128                 if param['end'] < param['begin']:
129                         param['end'] += 86400
130                 for element in listDate:
131                         del param[element]
132
133                 if param['sRef'] is None:
134                         return ( False, "Missing Parameter: sRef" )
135                 else:
136                         takeApart = param['sRef'].split('|')
137                         if len(takeApart) > 1:
138                                 param['sRef'] = takeApart[1]
139
140                 repeated = int(param.get('repeated') or 0)
141                 if repeated == 0:
142                         for element in ("mo", "tu", "we", "th", "fr", "sa", "su", "ms", "mf"):
143                                 if element in param:
144                                         number = param[element] or 0
145                                         del param[element]
146                                         repeated = repeated + int(number)
147                         if repeated > 127:
148                                 repeated = 127
149                 param['repeated'] = repeated
150
151                 if param['command'] == "add":
152                         del param['command']
153                         return self.editTimer(param)
154                 elif param['command'] == "del":
155                         del param['command']
156                         return self.delTimer(param)
157                 elif param['command'] == "change":
158                         del param['command']
159                         return self.editTimer(param)
160                 else:
161                         return ( False, "Unknown command: '%s'" % param['command'] )
162
163         def recordNow(self, param):
164                 limitEvent = True
165                 if param == "undefinitely" or param == "infinite":
166                         ret = (True, "Infinite Instant recording started")
167                         limitEvent = False
168                 else:
169                         ret = ( True, "Instant record for current Event started" )
170
171                 serviceref = ServiceReference(self.session.nav.getCurrentlyPlayingServiceReference().toString())
172
173                 event = None
174
175                 try:
176                         service = self.session.nav.getCurrentService()
177                         event = service.info().getEvent(0)
178                 except Exception:
179                         print "[Webcomponents.Timer] recordNow Exception!"
180
181                 begin = time()
182                 end = begin + 3600 * 10
183                 name = "instant record"
184                 description = ""
185                 eventid = 0
186
187                 if event is not None:
188                         curEvent = parseEvent(event)
189                         name = curEvent[2]
190                         description = curEvent[3]
191                         eventid = curEvent[4]
192                         if limitEvent:
193                                 end = curEvent[1]
194                 else:
195                         if limitEvent:
196                                 ret = ( False, "No event found! Not recording!" )
197
198                 if ret[0]:
199                         location = preferredInstantRecordPath()
200                         timer = RecordTimerEntry(serviceref, begin, end, name, description, eventid, False, False, 0, dirname=location)
201                         timer.dontSave = True
202                         recRet = self.recordtimer.record(timer)
203                         if recRet is not None:
204                                 # a conflict is rather unlikely, but this can also indicate a non-recordable service
205                                 ret = (False, "Timer conflict detected! Not recording!" )
206
207                 return ret
208
209
210 #===============================================================================
211 # This Function can add a new or edit an exisiting Timer.
212 # When the Parameter "deleteOldOnSave" is not set, a new Timer will be added.
213 # Otherwise, and if the parameters channelOld, beginOld and endOld are set,
214 # an existing timer with corresponding values will be changed.
215 #===============================================================================
216         def editTimer(self, param):
217                 print "[WebComponents.Timer] editTimer"
218
219                 #OK first we need to parse all of your Parameters
220                 #For some of them (like afterEvent or justplay) we can use default values
221                 #for others (the serviceReference or the Begin/End time of the timer
222                 #we have to quit if they are not set/have illegal values
223
224                 if 'sRef' not in param:
225                         return ( False, "Missing Parameter: sRef" )
226                 service_ref = ServiceReference(param['sRef'])
227
228                 repeated = int(param.get('repeated') or 0)
229
230                 if 'begin' not in param:
231                         return ( False, "Missing Parameter: begin" )
232                 begin = int(float(param['begin']))
233
234                 if 'end' not in param:
235                         return ( False, "Missing Parameter: end" )
236                 end = int(float(param['end']))
237
238                 tm = time()
239                 if tm <= begin:
240                         pass
241                 elif tm > begin and tm < end and repeated == 0:
242                         begin = time()
243                 elif repeated == 0:
244                         return ( False, "Illegal Parameter value for Parameter begin : '%s'" % begin )
245
246                 if 'name' not in param:
247                         return ( False, "Missing Parameter: name" )
248                 name = param['name']
249
250                 if 'description' not in param:
251                         return ( False, "Missing Parameter: description" )
252                 description = param['description'].replace("\n", " ")
253
254                 eit = param.get("eit", None)
255                 if eit is None:
256                         eit = ""
257
258                 if eit.strip() == "":
259                         eit = 0
260                 else:
261                         eit = int(eit)
262
263                 print "[WebComponents.Sources.Timer]: eit=%s" %eit
264                 if eit != 0:
265                         #check if the given event exists, if it doesn't return an error
266                         epgcache = eEPGCache.getInstance()
267                         event = epgcache.lookupEventId(eServiceReference(param['sRef']), eit)
268                         if event is None:
269                                 return ( False, "Event with id %s not found" %eit)
270                         eit = event.getEventId()
271
272                 disabled = False #Default to: Enabled
273                 if 'disabled' in param:
274                         if param['disabled'] == "1":
275                                 disabled = True
276                         else:
277                                 #TODO - maybe we can give the user some useful hint here
278                                 pass
279
280                 justplay = False #Default to: Record
281                 if 'justplay' in param:
282                         if param['justplay'] == "1":
283                                 justplay = True
284
285                 afterEvent = 3 #Default to Afterevent: Auto
286                 if 'afterevent' in param:
287                         if (param['afterevent'] == "0") or (param['afterevent'] == "1") or (param['afterevent'] == "2"):
288                                 afterEvent = int(param['afterevent'])
289
290                 dirname = preferredTimerPath()
291                 if 'dirname' in param and param['dirname']:
292                         dirname = param['dirname']
293
294                 tags = []
295                 if 'tags' in param and param['tags']:
296                         tags = unescape(param['tags']).split(' ')
297
298                 delold = 0
299                 if 'deleteOldOnSave' in param:
300                         delold = int(param['deleteOldOnSave'])
301
302                 #Try to edit an existing Timer
303                 if delold:
304                         if 'channelOld' in param and param['channelOld']:
305                                 channelOld = ServiceReference(param['channelOld'])
306                         else:
307                                 return ( False, "Missing Parameter: channelOld" )
308                         # We do need all of the following Parameters, too, for being able of finding the Timer.
309                         # Therefore so we can neither use default values in this part nor can we
310                         # continue if a parameter is missing
311                         if 'beginOld' not in param:
312                                 return ( False, "Missing Parameter: beginOld" )
313                         beginOld = int(param['beginOld'])
314
315                         if 'endOld' not in param:
316                                 return ( False, "Missing Parameter: endOld" )
317                         endOld = int(param['endOld'])
318
319                         #let's try to find the timer
320                         try:
321                                 for timer in self.recordtimer.timer_list + self.recordtimer.processed_timers:
322                                         if str(timer.service_ref) == str(channelOld):
323                                                 if int(timer.begin) == beginOld:
324                                                         if int(timer.end) == endOld: #we've found the timer we've been searching for
325                                                                 #set the new data
326                                                                 timer.service_ref = service_ref
327                                                                 timer.begin = begin
328                                                                 timer.end = end
329                                                                 timer.name = name
330                                                                 timer.description = description
331                                                                 timer.eit = eit
332                                                                 timer.disabled = disabled
333                                                                 timer.justplay = justplay
334                                                                 timer.afterEvent = afterEvent
335                                                                 timer.dirname = dirname
336                                                                 timer.tags = tags
337                                                                 timer.repeated = repeated
338                                                                 timer.processRepeated()
339
340                                                                 #sanity check
341                                                                 timersanitycheck = TimerSanityCheck(self.session.nav.RecordTimer.timer_list, timer)
342                                                                 conflicts = None
343                                                                 if not timersanitycheck.check():
344                                                                         conflicts = timersanitycheck.getSimulTimerList()
345                                                                         if conflicts is not None:
346                                                                                 for x in conflicts:
347                                                                                         if x.setAutoincreaseEnd(entry):
348                                                                                                 self.session.nav.RecordTimer.timeChanged(x)
349                                                                                 if not timersanitycheck.check():
350                                                                                         conflicts = timersanitycheck.getSimulTimerList()
351
352                                                                 if conflicts is None:
353                                                                         self.recordtimer.timeChanged(timer) #go and save it
354                                                                         print "[WebComponents.Timer] editTimer: Timer changed!"
355                                                                         return ( True, "Timer '%s' changed" %(timer.name) )
356                                                                 else:
357                                                                         print "[WebComponents.Timer] editTimer conflicting Timers: %s" %(conflicts)
358                                                                         msg = ""
359                                                                         for t in conflicts:
360                                                                                 msg = "%s / %s" %(msg, t.name)
361
362                                                                         return (False, "Conflicting Timer(s) detected! %s" %(msg))
363
364                         except Exception as e:
365                                 #obviously some value was not good, return an error
366                                 print e
367                                 return ( False, "Changing the timer for '%s' failed!" % name )
368
369
370                         return ( False, "Could not find timer '%s' with given start and end time!" % name )
371
372                 #Try adding a new Timer
373
374                 try:
375                         #Create a new instance of recordtimerentry
376                         timer = RecordTimerEntry(service_ref, begin, end, name, description, eit, disabled, justplay, afterEvent, dirname=dirname, tags=tags)
377                         timer.repeated = repeated
378                         #add the new timer
379                         conflicts = self.recordtimer.record(timer)
380                         if conflicts is None:
381                                 return ( True, "Timer '%s' added" %(timer.name) )
382                         else:
383                                 print "[WebComponents.Timer] editTimer conflicting Timers: %s" %(conflicts)
384                                 msg = ""
385                                 for timer in conflicts:
386                                         msg = "%s / %s" %(msg, timer.name)
387
388                                 return (False, "Conflicting Timer(s) detected! %s" %(msg))
389
390                 except Exception, e:
391                         #something went wrong, most possibly one of the given paramater-values was wrong
392                         print "[WebComponents.Timer] editTimer exception: %s" %(e)
393                         return ( False, "Could not add timer '%s'!" % name )
394
395                 return ( False, "Unexpected Error" )
396
397         def addTimerByEventID(self, param):
398                 print "[WebComponents.Timer] addTimerByEventID", param
399                 if param['sRef'] is None:
400                         return ( False, "Missing Parameter: sRef" )
401                 if param['eventid'] is None:
402                         return ( False, "Missing Parameter: eventid" )
403
404                 justplay = False
405                 if param['justplay'] is not None:
406                         if param['justplay'] == "1":
407                                 justplay = True
408
409                 location = preferredTimerPath()
410                 if 'dirname' in param and param['dirname']:
411                         location = param['dirname']
412
413                 tags = []
414                 if 'tags' in param and param['tags']:
415                         tags = unescape(param['tags']).split(' ')
416
417                 epgcache = eEPGCache.getInstance()
418                 event = epgcache.lookupEventId(eServiceReference(param['sRef']), int(param['eventid']))
419                 if event is None:
420                         return ( False, "EventId not found" )
421
422                 (begin, end, name, description, eit) = parseEvent(event)
423
424                 timer = RecordTimerEntry(ServiceReference(param['sRef']), begin , end, name, description, eit, False, justplay, AFTEREVENT.AUTO, dirname=location, tags=tags)
425
426                 conflicts = self.recordtimer.record(timer)
427                 if conflicts is None:
428                         return ( True, "Timer '%s' added" %(timer.name) )
429                 else:
430                         print "[WebComponents.Timer] editTimer conflicting Timers: %s" %(conflicts)
431                         msg = ""
432                         for timer in conflicts:
433                                 msg = "%s / %s" %(msg, timer.name)
434
435                         return (False, "Conflicting Timer(s) detected! %s" %(msg))
436
437
438         def writeTimerList(self, force=False):
439                 # is there an easier and better way? :\
440                 if config.plugins.Webinterface.autowritetimer.value or force:
441                         print "Timer.py writing timer to flash"
442                         self.session.nav.RecordTimer.saveTimer()
443                         return ( True, "TimerList has been saved " )
444                 else:
445                         return ( False, "TimerList has not been saved " )
446
447
448         def getResult(self):
449                 return self.res
450
451         result = property(getResult)
452
453         ## part for listfiller requests
454         def getList(self):
455                 timerlist = []
456
457                 for item in self.recordtimer.timer_list + self.recordtimer.processed_timers:
458                         try:
459                                 filename = item.Filename
460                         except AttributeError:
461                                 filename = ""
462
463                         try:
464                                 next_activation = item.next_activation
465                         except AttributeError:
466                                 next_activation = ""
467
468                         if item.eit is not None:
469                                 event = self.epgcache.lookupEvent(['EX', ("%s" % item.service_ref , 2, item.eit)])
470                                 if event and event[0][0] is not None:
471                                         extdesc = event[0][0]
472                                 else:
473                                         extdesc = "N/A"
474                         else:
475                                 extdesc = "N/A"
476
477                         #toggleDisabled
478                         if item.disabled:
479                                 disabled = "1"
480                                 toggleDisabled = "0"
481                                 toggleDisabledImg = "on"
482                         else:
483                                 disabled = "0"
484                                 toggleDisabled = "1"
485                                 toggleDisabledImg = "off"
486
487                         timerlist.append((
488                                 item.service_ref,
489                                 item.service_ref.getServiceName(),
490                                 item.eit,
491                                 item.name,
492                                 item.description,
493                                 disabled,
494                                 item.begin,
495                                 item.end,
496                                 item.end - item.begin,
497                                 item.start_prepare,
498                                 1 if item.justplay else 0,
499                                 item.afterEvent,
500                                 item.dirname,
501                                 " ".join(item.tags),
502                                 item.log_entries,
503                                 item.backoff,
504                                 item.first_try_prepare,
505                                 item.state,
506                                 item.repeated,
507                                 1 if item.dontSave else 0,
508                                 item.cancelled,
509                                 filename,
510                                 next_activation,
511                                 extdesc,
512                                 toggleDisabled,
513                                 toggleDisabledImg,
514                         ))
515
516                 return timerlist
517
518         list = property(getList)
519         lut = {
520                                 "ServiceReference":0,
521                                 "ServiceName": 1,
522                                 "EIT":2,
523                                 "Name":3,
524                                 "Description":4,
525                                 "Disabled":5,
526                                 "TimeBegin":6,
527                                 "TimeEnd":7,
528                                 "Duration":8,
529                                 "startPrepare":9,
530                                 "justPlay":10,
531                                 "afterEvent":11,
532                                 "Location":12,
533                                 "Tags":13,
534                                 "LogEntries":14,
535                                 "Backoff":15,
536                                 "firstTryPrepare":16,
537                                 "State":17,
538                                 "Repeated":18,
539                                 "dontSave":19,
540                                 "Cancled":20,
541                                 "Filename":21,
542                                 "nextActivation":22,
543                                 "DescriptionExtended":23,
544                                 "toggleDisabled":24,
545                                 "toggleDisabledIMG":25,
546                         }