Support scrambled playback.
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / PvrDescrambleConvert / PvrDescrambleConvert.py
1 from Components.config import config, ConfigSubsection, ConfigSelection
2 from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
3
4 from enigma import eTimer, eServiceReference, eServiceCenter, iServiceInformation, iRecordableService
5 from RecordTimer import RecordTimerEntry, RecordTimer
6 from Tools.Directories import fileExists
7 from ServiceReference import ServiceReference
8
9 from Tools import Notifications
10 from Screens.MessageBox import MessageBox
11
12 import os
13 import glob
14 import time
15
16 config.plugins.pvrdesconvertsetup = ConfigSubsection()
17 config.plugins.pvrdesconvertsetup.activate = ConfigSelection(default = "disable", choices = [ ("enable", _("Enable")), ("disable", _("Disable"))] )
18
19 # lib/dvb/pmt.h
20 SERVICETYPE_PVR_DESCRAMBLE = 11
21
22 def fileExist(fileName, flag = os.W_OK):
23         return os.access(fileName, flag)
24
25 def sync():
26         if hasattr(os, 'sync'):
27                 os.sync()
28         else:
29                 import ctypes
30                 libc = ctypes.CDLL("libc.so.6")
31                 libc.sync()
32
33 # iStaticServiceInformation
34 class StubInfo:
35         def getName(self, sref):
36                 return os.path.split(sref.getPath())[1]
37         def getLength(self, sref):
38                 return -1
39         def getEvent(self, sref, *args):
40                 return None
41         def isPlayable(self):
42                 return True
43         def getInfo(self, sref, w):
44                 if w == iServiceInformation.sTimeCreate:
45                         return os.stat(sref.getPath()).st_ctime
46                 if w == iServiceInformation.sFileSize:
47                         return os.stat(sref.getPath()).st_size
48                 if w == iServiceInformation.sDescription:
49                         return sref.getPath()
50                 return 0
51         def getInfoString(self, sref, w):
52                 return ''
53 stubInfo = StubInfo()
54
55 class PVRDescrambleConvertInfos:
56         def __init__(self):
57                 self.navigation = None
58
59         def getNavigation(self):
60                 if not self.navigation:
61                         import NavigationInstance
62                         if NavigationInstance:
63                                 self.navigation = NavigationInstance.instance
64
65                 return self.navigation
66
67         def getRecordings(self):
68                 recordings = []
69                 nav = self.getNavigation()
70                 if nav:
71                         recordings = nav.getRecordings()
72                         print "getRecordings : ", recordings
73
74                 return recordings
75
76         def getInstandby(self):
77                 from Screens.Standby import inStandby
78                 return inStandby
79
80         def getCurrentMoviePath(self):
81                 if not fileExists(config.movielist.last_videodir.value):
82                         config.movielist.last_videodir.value = defaultMoviePath()
83                         config.movielist.last_videodir.save()
84
85                 curMovieRef = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + config.movielist.last_videodir.value)
86                 return curMovieRef
87
88 class PVRDescrambleConvert(PVRDescrambleConvertInfos):
89         def __init__(self):
90                 PVRDescrambleConvertInfos.__init__(self)
91                 config.misc.standbyCounter.addNotifier(self.enterStandby, initial_call = False)
92
93                 self.convertTimer = eTimer()
94                 self.convertTimer.callback.append(self.startConvert)
95
96                 self.stopConvertTimer = eTimer()
97                 self.stopConvertTimer.callback.append(self.stopConvert)
98
99                 self.converting = None
100                 self.convertFilename = None
101                 self.currentPvr = None
102
103                 self.pvrLists = []
104
105                 self.oldService = None
106
107         def enterStandby(self, configElement):
108                 if config.plugins.pvrdesconvertsetup.activate.value == "enable":
109                         instandby = self.getInstandby()
110                         instandby.onClose.append(self.leaveStandby)
111
112                         self.loadScrambledPvrList()
113
114                         # register record callback
115                         self.appendRecordEventCB()
116
117                         self.startConvertTimer()
118
119         def leaveStandby(self):
120                 self.removeRecordEventCB()
121                 self.convertTimer.stop()
122                 self.stopConvert()
123
124         def startConvertTimer(self):
125                 self.convertTimer.start(3000, True)
126
127         def startStopConvertTimer(self):
128                 self.stopConvertTimer.start(500, True)
129
130         def appendRecordEventCB(self):
131                 nav = self.getNavigation()
132                 if nav:
133                         if self.gotRecordEvent not in nav.record_event:
134                                 nav.record_event.append(self.gotRecordEvent)
135
136         def removeRecordEventCB(self):
137                 nav = self.getNavigation()
138                 if nav:
139                         if self.gotRecordEvent in nav.record_event:
140                                 nav.record_event.remove(self.gotRecordEvent)
141
142         def gotRecordEvent(self, service, event):
143                 if service.getServiceType() == SERVICETYPE_PVR_DESCRAMBLE:
144                         if event == iRecordableService.evEnd:
145                                 if self.getInstandby():
146                                         self.startConvertTimer()
147                         elif event == iRecordableService.evPvrEof:
148                                 self.stopConvert(convertFinished = True)
149                         elif event == iRecordableService.evRecordFailed:
150                                 self.startStopConvertTimer()
151                 else:
152                         if event in (iRecordableService.evPvrTuneStart, iRecordableService.evTuneStart):
153                                 if self.currentPvr:
154                                         self.pvrLists.insert(0, self.currentPvr)
155                                         self.currentPvr = None
156                                         self.startStopConvertTimer()
157                         elif event == iRecordableService.evEnd:
158                                 if self.getInstandby():
159                                         self.startConvertTimer()
160
161         def loadScrambledPvrList(self):
162                 self.pvrLists = []
163
164                 serviceHandler = eServiceCenter.getInstance()
165                 curMovieRef = self.getCurrentMoviePath()
166                 movieRefList = serviceHandler.list(curMovieRef)
167
168                 if movieRefList is None:
169                         print "Load pvr list failed!"
170                         return
171
172                 while 1:
173                         sref = movieRefList.getNext()
174                         if not sref.valid():
175                                 break
176
177                         if config.ParentalControl.servicepinactive.value and config.ParentalControl.storeservicepin.value != "never":
178                                 from Components.ParentalControl import parentalControl
179                                 if not parentalControl.sessionPinCached and parentalControl.isProtected(sref):
180                                         continue
181
182                         if sref.flags & eServiceReference.mustDescent:
183                                 continue
184
185                         if not sref.getPath():
186                                 return
187
188                         info = serviceHandler.info(sref)
189
190                         real_sref = "1:0:0:0:0:0:0:0:0:0:"
191                         if info is not None:
192                                 real_sref = info.getInfoString(sref, iServiceInformation.sServiceref)
193
194                         if info is None:
195                                 info = stubInfo
196
197                         begin = info.getInfo(sref, iServiceInformation.sTimeCreate)
198
199                         # convert separe-separated list of tags into a set
200                         name = info.getName(sref)
201                         scrambled = info.getInfo(sref, iServiceInformation.sIsScrambled)
202                         length = info.getLength(sref)
203
204                         if scrambled == 1:
205                                 #print "====" * 30
206                                 #print "[loadScrambledPvrList] sref.toString() : ", sref.toString()
207                                 #print "[loadScrambledPvrList] sref.getPath() : ", sref.getPath()
208                                 #print "[loadScrambledPvrList] name : ", name
209                                 #print "[loadScrambledPvrList] begin : ", begin
210                                 #print "[loadScrambledPvrList] length : ", length
211                                 #print "[loadScrambledPvrList] scrambled : ", scrambled
212                                 #print ""
213                                 rec = (begin, sref, name, length, real_sref)
214                                 if rec not in self.pvrLists:
215                                         self.pvrLists.append( rec )
216
217                 self.pvrLists.sort()
218
219         def checkBeforeStartConvert(self):
220                 return self.pvrLists and (not bool(self.getRecordings())) and (not self.converting) and self.getInstandby()
221
222         def startConvert(self):
223                 if not self.checkBeforeStartConvert():
224                         return
225
226                 self.currentPvr = self.pvrLists.pop(0)
227                 if self.currentPvr is None:
228                         return
229
230                 (_begin, sref, name, length, real_ref) = self.currentPvr
231
232                 m_path = sref.getPath()
233                 sref = eServiceReference(real_ref + m_path)
234
235                 begin = int(time.time())
236                 end = begin + 3600      # dummy
237                 #end = begin + int(length) + 2
238                 description = ""
239                 eventid = None
240
241                 if isinstance(sref, eServiceReference):
242                         sref = ServiceReference(sref)
243
244                 if m_path.endswith('.ts'):
245                         m_path = m_path[:-3]
246
247                 filename = m_path + "_pvrdesc"
248
249                 recording = RecordTimerEntry(sref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath(), filename=filename)
250                 recording.dontSave = True
251                 recording.autoincrease = True
252                 recording.setAutoincreaseEnd()
253                 recording.pvrConvert = True # do not handle evStart event
254
255                 nav = self.getNavigation()
256                 simulTimerList = nav.RecordTimer.record(recording)
257                 if simulTimerList is None:      # no conflict
258                         recordings = self.getRecordings()
259                         if len(recordings) == 1:
260                                 self.converting = recording
261                                 self.convertFilename = (sref.getPath(), filename + ".ts")
262                         else:
263                                 print "[PVRDescrambleConvert] error, wrong recordings info."
264                 else:
265                         self.currentPvr = None
266                         self.startConvertTimer()
267
268                         if len(simulTimerList) > 1: # with other recording
269                                 print "[PVRDescrambleConvert] conflicts !"
270                         else:
271                                 print "[PVRDescrambleConvert] Couldn't record due to invalid service %s" % sref
272                         recording.autoincrease = False
273
274                 print "[PVRDescrambleConvert] startConvert, self.converting : ", self.converting
275
276         def removeStr(self, fileName, s):
277                 if fileName.find(s) == -1:
278                         return fileName
279
280                 sp = fileName.split(s)
281
282                 return sp[0] + sp[1]
283
284         def renameDelPvr(self, pvrName, subName):
285                 targetName = pvrName + subName
286                 outName = self.removeStr(pvrName, ".ts") + "_del" + ".ts" + subName
287
288                 if fileExist(targetName):
289                         #print "RENAME %s -> %s" % (targetName, outName)
290                         os.rename(targetName, outName)
291                         return outName
292
293                 return None
294
295         def renameConvertPvr(self, pvrName, subName):
296                 targetName = pvrName + subName
297                 outName = self.removeStr(pvrName, "_pvrdesc") + subName
298
299                 if fileExist(targetName):
300                         #print "RENAME %s -> %s" % (targetName, outName)
301                         os.rename(targetName, outName)
302                         return outName
303
304                 return None
305
306         def renamePvr(self, pvr_ori, pvr_convert):
307                 pvr_ori_del = self.renameDelPvr(pvr_ori, "")
308                 if not pvr_ori_del:
309                         return None
310
311                 self.renameDelPvr(pvr_ori, ".meta")
312                 self.renameDelPvr(pvr_ori, ".ap")
313                 self.renameDelPvr(pvr_ori, ".sc")
314                 self.renameDelPvr(pvr_ori, ".cuts")
315
316                 pvr_convert_fixed = self.renameConvertPvr(pvr_convert, "")
317                 if not pvr_convert_fixed:
318                         return None
319
320                 self.renameConvertPvr(pvr_convert, ".meta")
321                 self.renameConvertPvr(pvr_convert, ".ap")
322                 self.renameConvertPvr(pvr_convert, ".sc")
323                 self.renameConvertPvr(pvr_convert, ".cuts")
324
325                 if os.path.exists(pvr_convert[:-3] + '.eit'):
326                         os.remove(pvr_convert[:-3] + '.eit')
327
328                 return pvr_ori_del
329
330         def stopConvert(self, convertFinished = False):
331                 name = "Unknown"
332                 if self.currentPvr:
333                         (_begin, sref, name, length, real_ref) = self.currentPvr
334                         self.currentPvr = None
335
336                 if self.converting:
337                         nav = self.getNavigation()
338                         nav.RecordTimer.removeEntry(self.converting)
339                         convertFilename = self.convertFilename
340                         self.converting = None
341                         self.convertFilename = None
342
343                         if convertFilename:
344                                 (pvr_ori, pvr_convert) = convertFilename
345                                 if convertFinished:
346                                         # check size
347                                         if fileExist(pvr_convert, os.F_OK) and os.stat(pvr_convert).st_size:
348                                                 pvr_ori_del = self.renamePvr(pvr_ori, pvr_convert)
349                                                 if pvr_ori_del:
350                                                         self.deletePvr(pvr_ori_del)
351                                                 self.addNotification(_("A PVR descramble converting is finished.\n%s") % name)
352                                         else:
353                                                 self.deletePvr(pvr_convert)
354                                 else:
355                                         self.deletePvr(pvr_convert)
356
357                 sync()
358
359         def deletePvr(self, filename):
360                 serviceHandler = eServiceCenter.getInstance()
361                 ref = eServiceReference(1, 0, filename)
362                 offline = serviceHandler.offlineOperations(ref)
363                 if offline.deleteFromDisk(0):
364                         print "[PVRDescrambleConvert] delete failed : ", filename
365
366         def addNotification(self, text):
367                 Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=5)
368
369 pvr_descramble_convert = PVRDescrambleConvert()
370