Fix default recording path and usb recording.
authorhschang <chang@dev3>
Thu, 22 Feb 2018 05:14:01 +0000 (14:14 +0900)
committerhschang <chang@dev3>
Thu, 22 Feb 2018 05:14:01 +0000 (14:14 +0900)
RecordTimer.py
lib/python/Components/Harddisk.py
lib/python/Components/SystemInfo.py
lib/python/Components/UsageConfig.py
lib/python/Screens/InfoBarGenerics.py
lib/python/Screens/RecordPaths.py
lib/python/Tools/Directories.py
timer.py

index 32fb923..c3e9de7 100755 (executable)
@@ -18,6 +18,8 @@ from ServiceReference import ServiceReference
 from time import localtime, strftime, ctime, time
 from bisect import insort
 
+import os
+
 # ok, for descriptions etc we have:
 # service reference  (to get the service name)
 # name               (title)
@@ -47,6 +49,25 @@ class AFTEREVENT:
        DEEPSTANDBY = 2
        AUTO = 3
 
+def findSafeRecordPath(dirname):
+       if not dirname:
+               return None
+
+       from Components import Harddisk
+       dirname = os.path.realpath(dirname)
+       mountpoint = Harddisk.findMountPoint(dirname)
+       if mountpoint in ('/', '/media'):
+               print '[RecordTimer] media is not mounted:', dirname
+               return None
+       if not os.path.isdir(dirname):
+               try:
+                       os.makedirs(dirname)
+               except Exception, ex:
+                       print '[RecordTimer] Failed to create dir "%s":' % dirname, ex
+                       return None
+
+       return dirname
+
 # please do not translate log messages
 class RecordTimerEntry(timer.TimerEntry, object):
 ######### the following static methods and members are only in use when the box is in (soft) standby
@@ -164,21 +185,31 @@ class RecordTimerEntry(timer.TimerEntry, object):
                if config.recording.ascii_filenames.value:
                        filename = ASCIItranslit.legacyEncode(filename)
 
-               if not self.dirname or not Directories.fileExists(self.dirname, 'w'):
-                       if self.dirname:
-                               self.dirnameHadToFallback = True
-                       dirname = defaultMoviePath()
+               if not self.dirname:
+                       dirname = findSafeRecordPath(defaultMoviePath())
                else:
-                       dirname = self.dirname
+                       dirname = findSafeRecordPath(self.dirname)
+                       if dirname is None:
+                               dirname = findSafeRecordPath(defaultMoviePath())
+                               self.dirnameHadToFallback = True
+
+               if not dirname:
+                       return None
+
                self.Filename = Directories.getRecordingFilename(filename, dirname)
                self.log(0, "Filename calculated as: '%s'" % self.Filename)
                #begin_date + " - " + service_name + description)
+               return self.Filename
 
        def tryPrepare(self):
                if self.justplay:
                        return True
                else:
-                       self.calculateFilename()
+                       if not self.calculateFilename():
+                               self.do_backoff()
+                               self.start_prepare = time() + self.backoff
+                               return False
+
                        rec_ref = self.service_ref and self.service_ref.ref
                        if rec_ref and rec_ref.flags & eServiceReference.isGroup:
                                rec_ref = getBestPlayableServiceReference(rec_ref, eServiceReference())
@@ -410,6 +441,18 @@ class RecordTimerEntry(timer.TimerEntry, object):
 
        record_service = property(lambda self: self.__record_service, setRecordService)
 
+       def isUsbRecordingPath(self):
+               dirname = None
+
+               if self.dirname:
+                       dirname = findSafeRecordPath(self.dirname)
+
+               if dirname is None:
+                       dirname = findSafeRecordPath(defaultMoviePath())
+
+               from Components import Harddisk
+               return Harddisk.isUsbStorage(dirname)
+
 def createTimer(xml):
        begin = int(xml.get("begin"))
        end = int(xml.get("end"))
@@ -466,6 +509,18 @@ class RecordTimer(timer.Timer):
                        print "unable to load timers from file!"
 
        def doActivate(self, w):
+               if w.state == RecordTimerEntry.StateWaiting:
+                       from Components.SystemInfo import SystemInfo
+                       if SystemInfo.get("DisableUsbRecord", True) and w.isUsbRecordingPath():
+                               service_name = w.service_ref.getServiceName()
+                               self.timer_list.remove(w)
+                               if w.dontSave is False:
+                                       w.resetState()
+                                       w.disable()
+                                       self.addTimerEntry(w)
+                               Notifications.AddNotification(MessageBox, _("Can not recording on a USB storage.\nService name : %s"% service_name), MessageBox.TYPE_ERROR)
+                               return
+
                # when activating a timer which has already passed,
                # simply abort the timer. don't run trough all the stages.
                if w.shouldSkip():
index 0e73dee..9b3a684 100755 (executable)
@@ -58,6 +58,56 @@ def enableUdevEvent(enable = True):
        print "CMD : ", cmd
        system(cmd)
 
+def findMountPoint(path):
+       'Example: findMountPoint("/media/hdd/some/file") returns "/media/hdd"'
+       path = os.path.abspath(path)
+       while not os.path.ismount(path):
+               path = os.path.dirname(path)
+       return path
+
+def getDeviceFile(dev_path):
+       for parts in getProcMounts():
+               if os.path.realpath(parts[1]).startswith(dev_path):
+                       return parts[0]
+
+def getMountPath(mountPath):
+       mountPath = os.path.realpath(mountPath)
+       while not os.path.ismount(mountPath):
+               mountPath = os.path.dirname(mountPath)
+
+       return mountPath
+
+def getDeviceInterface(mountPath):
+       mountPath = getMountPath(mountPath)
+
+       if mountPath == '/':
+               return None
+
+       from Components.Harddisk import getDeviceFile
+       dev = getDeviceFile(mountPath)
+
+       if dev and dev.startswith("/dev/sd"):
+               dev = os.path.basename(dev)
+               phyPath = os.path.realpath('/sys/block/' + dev[:3])
+
+               # check usb
+               for x in glob.glob("/sys/bus/usb/devices/usb*"):
+                       if phyPath.find(os.path.realpath(x)) != -1:
+                               from Tools.HardwareInfo import HardwareInfo
+                               if (HardwareInfo().get_vu_device_name() == "zero4k") and (phyPath.find("f0b00500.ehci_v2/usb2/2-1") != -1):
+                                       return "ata"
+                               else:
+                                       return "usb"
+
+               # check ata
+               if phyPath.find("/ata") != -1:
+                       return "ata"
+
+       return None
+
+def isUsbStorage(dirname):
+       return getDeviceInterface(dirname) in (None, "usb")
+
 DEVTYPE_UDEV = 0
 DEVTYPE_DEVFS = 1
 
index e4bae61..5b01c4c 100644 (file)
@@ -34,4 +34,5 @@ SystemInfo["WOWLSupport"] = HardwareInfo().get_vu_device_name() == "ultimo4k"
 SystemInfo["ScrambledPlayback"] = HardwareInfo().get_vu_device_name() in ("solo4k", "ultimo4k", "uno4kse", "zero4k")
 SystemInfo["FastChannelChange"] =  fileExists("/proc/stb/frontend/fbc/fcc")
 SystemInfo["MiniTV"] = fileExists("/proc/stb/lcd/live_enable")
+SystemInfo["DisableUsbRecord"] = HardwareInfo().get_vu_device_name() in ("solo4k", "uno4kse", "zero4k")
 
index d262448..36db2ad 100644 (file)
@@ -1,6 +1,7 @@
 from Components.Harddisk import harddiskmanager
 from Components.NimManager import nimmanager
 from config import ConfigSubsection, ConfigYesNo, config, ConfigSelection, ConfigText, ConfigNumber, ConfigSet, ConfigLocations
+from Tools.Directories import defaultRecordingLocation
 from Tools.Directories import resolveFilename, SCOPE_HDD
 from enigma import Misc_Options, eEnv
 from enigma import setTunerTypePriorityOrder, setPreferredTuner
@@ -35,7 +36,7 @@ def InitUsageConfig():
                ("standard", _("standard")), ("swap", _("swap PiP and main picture")),
                ("swapstop", _("move PiP to main picture")), ("stop", _("stop PiP")) ])
 
-       config.usage.default_path = ConfigText(default = resolveFilename(SCOPE_HDD))
+       config.usage.default_path = ConfigText(default = "")
        config.usage.timer_path = ConfigText(default = "<default>")
        config.usage.instantrec_path = ConfigText(default = "<default>")
        config.usage.timeshift_path = ConfigText(default = "/media/hdd/")
@@ -226,7 +227,7 @@ def updateChoices(sel, choices):
                sel.setChoices(map(str, choices), defval)
 
 def preferredPath(path):
-       if config.usage.setup_level.index < 2 or path == "<default>":
+       if config.usage.setup_level.index < 2 or path == "<default>" or not path:
                return None  # config.usage.default_path.value, but delay lookup until usage
        elif path == "<current>":
                return config.movielist.last_videodir.value
@@ -242,5 +243,5 @@ def preferredInstantRecordPath():
        return preferredPath(config.usage.instantrec_path.value)
 
 def defaultMoviePath():
-       return config.usage.default_path.value
+       return defaultRecordingLocation(config.usage.default_path.value)
 
index cce091e..0b01bf8 100755 (executable)
@@ -39,7 +39,7 @@ from time import time, localtime, strftime
 from os import stat as os_stat, system as os_system
 from bisect import insort
 
-from RecordTimer import RecordTimerEntry, RecordTimer
+from RecordTimer import RecordTimerEntry, RecordTimer, findSafeRecordPath
 
 # hack alert!
 from Menu import MainMenu, mdom
@@ -1223,6 +1223,13 @@ class InfoBarTimeshift:
                if self.timeshift_enabled:
                        print "hu, timeshift already enabled?"
                else:
+                       from Components import Harddisk
+                       if Harddisk.getMountPath(config.usage.timeshift_path.value) != '/' and \
+                               SystemInfo.get("DisableUsbRecord", True) and \
+                               Harddisk.isUsbStorage(config.usage.timeshift_path.value):
+                               self.session.open(MessageBox, _("Timeshift not possible on a USB storage."), MessageBox.TYPE_ERROR)
+                               return 0
+
                        if not ts.startTimeshift():
                                self.timeshift_enabled = 1
 
@@ -1663,18 +1670,14 @@ class InfoBarInstantRecord:
                        self.session.nav.RecordTimer.timeChanged(entry)
 
        def instantRecord(self):
-               dir = preferredInstantRecordPath()
-               if not dir or not fileExists(dir, 'w'):
-                       dir = defaultMoviePath()
-
                if not fileExists("/hdd", 0):
                        print "not found /hdd"
                        os_system("ln -s /media/hdd /hdd")
-#
-               try:
-                       stat = os_stat(dir)
-               except:
-                       # XXX: this message is a little odd as we might be recording to a remote device
+
+               recPath = preferredInstantRecordPath()
+               if not findSafeRecordPath(recPath) and not findSafeRecordPath(defaultMoviePath()):
+                       if not recPath:
+                               recPath = ""
                        self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
                        return
 
index 22ca9fc..55159d9 100644 (file)
@@ -36,16 +36,16 @@ class RecordPathsSettings(Screen,ConfigListScreen):
                }, -2)
 
        def checkReadWriteDir(self, configele):
+               value = configele.value
                print "checkReadWrite: ", configele.value
-               if configele.value in [x[0] for x in self.styles] or fileExists(configele.value, "w"):
-                       configele.last_value = configele.value
+               if not value or value in [x[0] for x in self.styles] or fileExists(value, "w"):
+                       configele.last_value = value
                        return True
                else:
-                       dir = configele.value
                        configele.value = configele.last_value
                        self.session.open(
                                MessageBox,
-                               _("The directory %s is not writable.\nMake sure you select a writable directory instead.")%dir,
+                               _("The directory %s is not writable.\nMake sure you select a writable directory instead.") % value,
                                type = MessageBox.TYPE_ERROR
                                )
                        return False
@@ -55,11 +55,11 @@ class RecordPathsSettings(Screen,ConfigListScreen):
                styles_keys = [x[0] for x in self.styles]
                tmp = config.movielist.videodirs.value
                default = config.usage.default_path.value
-               if default not in tmp:
+               if default and default not in tmp:
                        tmp = tmp[:]
                        tmp.append(default)
                print "DefaultPath: ", default, tmp
-               self.default_dirname = ConfigSelection(default = default, choices = tmp)
+               self.default_dirname = ConfigSelection(default = default, choices = [("", _("<Default movie location>"))] + tmp)
                tmp = config.movielist.videodirs.value
                default = config.usage.timer_path.value
                if default not in tmp and default not in styles_keys:
@@ -148,10 +148,10 @@ class RecordPathsSettings(Screen,ConfigListScreen):
                                styles_keys = [x[0] for x in self.styles]
                                tmp = config.movielist.videodirs.value
                                default = self.default_dirname.value
-                               if default not in tmp:
+                               if default and default not in tmp:
                                        tmp = tmp[:]
                                        tmp.append(default)
-                               self.default_dirname.setChoices(tmp, default=default)
+                               self.default_dirname.setChoices([("", _("<Default movie location>"))] + tmp, default=default)
                                tmp = config.movielist.videodirs.value
                                default = self.timer_dirname.value
                                if default not in tmp and default not in styles_keys:
index f0ef0de..b886459 100755 (executable)
@@ -17,6 +17,8 @@ try:
 except:
        have_utime = False
 
+import os
+
 SCOPE_TRANSPONDERDATA = 0
 SCOPE_SYSETC = 1
 SCOPE_FONTS = 2
@@ -151,11 +153,64 @@ def resolveFilename(scope, base = "", path_prefix = None):
        return path + base
        # this is only the BASE - an extension must be added later.
 
-def pathExists(path):
-       return os_path.exists(path)
+pathExists = os.path.exists
+isMount = os.path.ismount
+
+def bestRecordingLocation(candidates):
+       path = ''
+
+       from Components import Harddisk
+       ata_devices = [candidate for candidate in candidates if Harddisk.getDeviceInterface(candidate[1]) == "ata"]
+
+       if len(ata_devices) == 1:
+               path = ata_devices[0][1]
+
+       elif len(ata_devices):
+               best = ""
+               for device in ata_devices:
+                       dev = os.path.basename(device[0])
+                       if not best or (best > dev):
+                               best = dev
+                               path = device[1]
+       else: # Find the largest usb disk
+               biggest = 0
+               for candidate in candidates:
+                       try:
+                               stat = os.statvfs(candidate[1])
+                               # must have some free space (i.e. not read-only)
+                               if stat.f_bavail:
+                                       # Free space counts double
+                                       size = (stat.f_blocks + stat.f_bavail) * stat.f_bsize
+                                       if size > biggest:
+                                               path = candidate[1]
+                                               biggest = size
+                       except Exception, e:
+                               print "[DRL]", e
 
-def isMount(path):
-       return os_path.ismount(path)
+       return path
+
+def defaultRecordingLocation(candidate=None):
+       if candidate and os.path.exists(candidate):
+               return candidate
+       # First, try whatever /hdd points to, or /media/hdd
+       try:
+               path = os.path.realpath('/hdd')
+       except:
+               path = '/media/hdd'
+       if not os.path.exists(path) or not os.path.ismount(path):
+               path = ''
+               from Components import Harddisk
+               mounts = [m for m in Harddisk.getProcMounts() if m[1].startswith('/media/')]
+               path = bestRecordingLocation([m for m in mounts if m[0].startswith('/dev/')])
+       if path:
+               # If there's a movie subdir, we'd probably want to use that.
+               movie = os.path.join(path, 'movie')
+               if os.path.isdir(movie):
+                       path = movie
+               if not path.endswith('/'):
+                       path += '/' # Bad habits die hard, old code relies on this
+
+       return path
 
 def createDir(path, makeParents = False):
        try:
@@ -188,6 +243,9 @@ def fileExists(f, mode='r'):
        return access(f, acc_mode)
 
 def getRecordingFilename(basename, dirname = None):
+       if not dirname.endswith('/'):
+               dirname += '/'
+
        # filter out non-allowed characters
        non_allowed_characters = "/.\\:*?<>|\""
        filename = ""
index aaae0b2..d58a83e 100644 (file)
--- a/timer.py
+++ b/timer.py
@@ -232,6 +232,10 @@ class Timer:
                self.setNextActivation(min)
        
        def timeChanged(self, timer):
+               if timer not in (self.processed_timers + self.timer_list):
+                       print "timer not found"
+                       return
+
                print "time changed"
                timer.timeChanged()
                if timer.state == TimerEntry.StateEnded: