Support scrambled playback.
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / PvrDescrambleConvert / PvrDescrambleConvert.py
diff --git a/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/PvrDescrambleConvert.py b/lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/PvrDescrambleConvert.py
new file mode 100644 (file)
index 0000000..56e0220
--- /dev/null
@@ -0,0 +1,370 @@
+from Components.config import config, ConfigSubsection, ConfigSelection
+from Components.UsageConfig import preferredInstantRecordPath, defaultMoviePath
+
+from enigma import eTimer, eServiceReference, eServiceCenter, iServiceInformation, iRecordableService
+from RecordTimer import RecordTimerEntry, RecordTimer
+from Tools.Directories import fileExists
+from ServiceReference import ServiceReference
+
+from Tools import Notifications
+from Screens.MessageBox import MessageBox
+
+import os
+import glob
+import time
+
+config.plugins.pvrdesconvertsetup = ConfigSubsection()
+config.plugins.pvrdesconvertsetup.activate = ConfigSelection(default = "disable", choices = [ ("enable", _("Enable")), ("disable", _("Disable"))] )
+
+# lib/dvb/pmt.h
+SERVICETYPE_PVR_DESCRAMBLE = 11
+
+def fileExist(fileName, flag = os.W_OK):
+       return os.access(fileName, flag)
+
+def sync():
+       if hasattr(os, 'sync'):
+               os.sync()
+       else:
+               import ctypes
+               libc = ctypes.CDLL("libc.so.6")
+               libc.sync()
+
+# iStaticServiceInformation
+class StubInfo:
+       def getName(self, sref):
+               return os.path.split(sref.getPath())[1]
+       def getLength(self, sref):
+               return -1
+       def getEvent(self, sref, *args):
+               return None
+       def isPlayable(self):
+               return True
+       def getInfo(self, sref, w):
+               if w == iServiceInformation.sTimeCreate:
+                       return os.stat(sref.getPath()).st_ctime
+               if w == iServiceInformation.sFileSize:
+                       return os.stat(sref.getPath()).st_size
+               if w == iServiceInformation.sDescription:
+                       return sref.getPath()
+               return 0
+       def getInfoString(self, sref, w):
+               return ''
+stubInfo = StubInfo()
+
+class PVRDescrambleConvertInfos:
+       def __init__(self):
+               self.navigation = None
+
+       def getNavigation(self):
+               if not self.navigation:
+                       import NavigationInstance
+                       if NavigationInstance:
+                               self.navigation = NavigationInstance.instance
+
+               return self.navigation
+
+       def getRecordings(self):
+               recordings = []
+               nav = self.getNavigation()
+               if nav:
+                       recordings = nav.getRecordings()
+                       print "getRecordings : ", recordings
+
+               return recordings
+
+       def getInstandby(self):
+               from Screens.Standby import inStandby
+               return inStandby
+
+       def getCurrentMoviePath(self):
+               if not fileExists(config.movielist.last_videodir.value):
+                       config.movielist.last_videodir.value = defaultMoviePath()
+                       config.movielist.last_videodir.save()
+
+               curMovieRef = eServiceReference("2:0:1:0:0:0:0:0:0:0:" + config.movielist.last_videodir.value)
+               return curMovieRef
+
+class PVRDescrambleConvert(PVRDescrambleConvertInfos):
+       def __init__(self):
+               PVRDescrambleConvertInfos.__init__(self)
+               config.misc.standbyCounter.addNotifier(self.enterStandby, initial_call = False)
+
+               self.convertTimer = eTimer()
+               self.convertTimer.callback.append(self.startConvert)
+
+               self.stopConvertTimer = eTimer()
+               self.stopConvertTimer.callback.append(self.stopConvert)
+
+               self.converting = None
+               self.convertFilename = None
+               self.currentPvr = None
+
+               self.pvrLists = []
+
+               self.oldService = None
+
+       def enterStandby(self, configElement):
+               if config.plugins.pvrdesconvertsetup.activate.value == "enable":
+                       instandby = self.getInstandby()
+                       instandby.onClose.append(self.leaveStandby)
+
+                       self.loadScrambledPvrList()
+
+                       # register record callback
+                       self.appendRecordEventCB()
+
+                       self.startConvertTimer()
+
+       def leaveStandby(self):
+               self.removeRecordEventCB()
+               self.convertTimer.stop()
+               self.stopConvert()
+
+       def startConvertTimer(self):
+               self.convertTimer.start(3000, True)
+
+       def startStopConvertTimer(self):
+               self.stopConvertTimer.start(500, True)
+
+       def appendRecordEventCB(self):
+               nav = self.getNavigation()
+               if nav:
+                       if self.gotRecordEvent not in nav.record_event:
+                               nav.record_event.append(self.gotRecordEvent)
+
+       def removeRecordEventCB(self):
+               nav = self.getNavigation()
+               if nav:
+                       if self.gotRecordEvent in nav.record_event:
+                               nav.record_event.remove(self.gotRecordEvent)
+
+       def gotRecordEvent(self, service, event):
+               if service.getServiceType() == SERVICETYPE_PVR_DESCRAMBLE:
+                       if event == iRecordableService.evEnd:
+                               if self.getInstandby():
+                                       self.startConvertTimer()
+                       elif event == iRecordableService.evPvrEof:
+                               self.stopConvert(convertFinished = True)
+                       elif event == iRecordableService.evRecordFailed:
+                               self.startStopConvertTimer()
+               else:
+                       if event in (iRecordableService.evPvrTuneStart, iRecordableService.evTuneStart):
+                               if self.currentPvr:
+                                       self.pvrLists.insert(0, self.currentPvr)
+                                       self.currentPvr = None
+                                       self.startStopConvertTimer()
+                       elif event == iRecordableService.evEnd:
+                               if self.getInstandby():
+                                       self.startConvertTimer()
+
+       def loadScrambledPvrList(self):
+               self.pvrLists = []
+
+               serviceHandler = eServiceCenter.getInstance()
+               curMovieRef = self.getCurrentMoviePath()
+               movieRefList = serviceHandler.list(curMovieRef)
+
+               if movieRefList is None:
+                       print "Load pvr list failed!"
+                       return
+
+               while 1:
+                       sref = movieRefList.getNext()
+                       if not sref.valid():
+                               break
+
+                       if config.ParentalControl.servicepinactive.value and config.ParentalControl.storeservicepin.value != "never":
+                               from Components.ParentalControl import parentalControl
+                               if not parentalControl.sessionPinCached and parentalControl.isProtected(sref):
+                                       continue
+
+                       if sref.flags & eServiceReference.mustDescent:
+                               continue
+
+                       if not sref.getPath():
+                               return
+
+                       info = serviceHandler.info(sref)
+
+                       real_sref = "1:0:0:0:0:0:0:0:0:0:"
+                       if info is not None:
+                               real_sref = info.getInfoString(sref, iServiceInformation.sServiceref)
+
+                       if info is None:
+                               info = stubInfo
+
+                       begin = info.getInfo(sref, iServiceInformation.sTimeCreate)
+
+                       # convert separe-separated list of tags into a set
+                       name = info.getName(sref)
+                       scrambled = info.getInfo(sref, iServiceInformation.sIsScrambled)
+                       length = info.getLength(sref)
+
+                       if scrambled == 1:
+                               #print "====" * 30
+                               #print "[loadScrambledPvrList] sref.toString() : ", sref.toString()
+                               #print "[loadScrambledPvrList] sref.getPath() : ", sref.getPath()
+                               #print "[loadScrambledPvrList] name : ", name
+                               #print "[loadScrambledPvrList] begin : ", begin
+                               #print "[loadScrambledPvrList] length : ", length
+                               #print "[loadScrambledPvrList] scrambled : ", scrambled
+                               #print ""
+                               rec = (begin, sref, name, length, real_sref)
+                               if rec not in self.pvrLists:
+                                       self.pvrLists.append( rec )
+
+               self.pvrLists.sort()
+
+       def checkBeforeStartConvert(self):
+               return self.pvrLists and (not bool(self.getRecordings())) and (not self.converting) and self.getInstandby()
+
+       def startConvert(self):
+               if not self.checkBeforeStartConvert():
+                       return
+
+               self.currentPvr = self.pvrLists.pop(0)
+               if self.currentPvr is None:
+                       return
+
+               (_begin, sref, name, length, real_ref) = self.currentPvr
+
+               m_path = sref.getPath()
+               sref = eServiceReference(real_ref + m_path)
+
+               begin = int(time.time())
+               end = begin + 3600      # dummy
+               #end = begin + int(length) + 2
+               description = ""
+               eventid = None
+
+               if isinstance(sref, eServiceReference):
+                       sref = ServiceReference(sref)
+
+               if m_path.endswith('.ts'):
+                       m_path = m_path[:-3]
+
+               filename = m_path + "_pvrdesc"
+
+               recording = RecordTimerEntry(sref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath(), filename=filename)
+               recording.dontSave = True
+               recording.autoincrease = True
+               recording.setAutoincreaseEnd()
+               recording.pvrConvert = True # do not handle evStart event
+
+               nav = self.getNavigation()
+               simulTimerList = nav.RecordTimer.record(recording)
+               if simulTimerList is None:      # no conflict
+                       recordings = self.getRecordings()
+                       if len(recordings) == 1:
+                               self.converting = recording
+                               self.convertFilename = (sref.getPath(), filename + ".ts")
+                       else:
+                               print "[PVRDescrambleConvert] error, wrong recordings info."
+               else:
+                       self.currentPvr = None
+                       self.startConvertTimer()
+
+                       if len(simulTimerList) > 1: # with other recording
+                               print "[PVRDescrambleConvert] conflicts !"
+                       else:
+                               print "[PVRDescrambleConvert] Couldn't record due to invalid service %s" % sref
+                       recording.autoincrease = False
+
+               print "[PVRDescrambleConvert] startConvert, self.converting : ", self.converting
+
+       def removeStr(self, fileName, s):
+               if fileName.find(s) == -1:
+                       return fileName
+
+               sp = fileName.split(s)
+
+               return sp[0] + sp[1]
+
+       def renameDelPvr(self, pvrName, subName):
+               targetName = pvrName + subName
+               outName = self.removeStr(pvrName, ".ts") + "_del" + ".ts" + subName
+
+               if fileExist(targetName):
+                       #print "RENAME %s -> %s" % (targetName, outName)
+                       os.rename(targetName, outName)
+                       return outName
+
+               return None
+
+       def renameConvertPvr(self, pvrName, subName):
+               targetName = pvrName + subName
+               outName = self.removeStr(pvrName, "_pvrdesc") + subName
+
+               if fileExist(targetName):
+                       #print "RENAME %s -> %s" % (targetName, outName)
+                       os.rename(targetName, outName)
+                       return outName
+
+               return None
+
+       def renamePvr(self, pvr_ori, pvr_convert):
+               pvr_ori_del = self.renameDelPvr(pvr_ori, "")
+               if not pvr_ori_del:
+                       return None
+
+               self.renameDelPvr(pvr_ori, ".meta")
+               self.renameDelPvr(pvr_ori, ".ap")
+               self.renameDelPvr(pvr_ori, ".sc")
+               self.renameDelPvr(pvr_ori, ".cuts")
+
+               pvr_convert_fixed = self.renameConvertPvr(pvr_convert, "")
+               if not pvr_convert_fixed:
+                       return None
+
+               self.renameConvertPvr(pvr_convert, ".meta")
+               self.renameConvertPvr(pvr_convert, ".ap")
+               self.renameConvertPvr(pvr_convert, ".sc")
+               self.renameConvertPvr(pvr_convert, ".cuts")
+
+               if os.path.exists(pvr_convert[:-3] + '.eit'):
+                       os.remove(pvr_convert[:-3] + '.eit')
+
+               return pvr_ori_del
+
+       def stopConvert(self, convertFinished = False):
+               name = "Unknown"
+               if self.currentPvr:
+                       (_begin, sref, name, length, real_ref) = self.currentPvr
+                       self.currentPvr = None
+
+               if self.converting:
+                       nav = self.getNavigation()
+                       nav.RecordTimer.removeEntry(self.converting)
+                       convertFilename = self.convertFilename
+                       self.converting = None
+                       self.convertFilename = None
+
+                       if convertFilename:
+                               (pvr_ori, pvr_convert) = convertFilename
+                               if convertFinished:
+                                       # check size
+                                       if fileExist(pvr_convert, os.F_OK) and os.stat(pvr_convert).st_size:
+                                               pvr_ori_del = self.renamePvr(pvr_ori, pvr_convert)
+                                               if pvr_ori_del:
+                                                       self.deletePvr(pvr_ori_del)
+                                               self.addNotification(_("A PVR descramble converting is finished.\n%s") % name)
+                                       else:
+                                               self.deletePvr(pvr_convert)
+                               else:
+                                       self.deletePvr(pvr_convert)
+
+               sync()
+
+       def deletePvr(self, filename):
+               serviceHandler = eServiceCenter.getInstance()
+               ref = eServiceReference(1, 0, filename)
+               offline = serviceHandler.offlineOperations(ref)
+               if offline.deleteFromDisk(0):
+                       print "[PVRDescrambleConvert] delete failed : ", filename
+
+       def addNotification(self, text):
+               Notifications.AddNotification(MessageBox, text, type=MessageBox.TYPE_INFO, timeout=5)
+
+pvr_descramble_convert = PVRDescrambleConvert()
+