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)
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
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())
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"))
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():
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
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")
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
("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/")
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
return preferredPath(config.usage.instantrec_path.value)
def defaultMoviePath():
- return config.usage.default_path.value
+ return defaultRecordingLocation(config.usage.default_path.value)
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
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
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
}, -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
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:
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:
except:
have_utime = False
+import os
+
SCOPE_TRANSPONDERDATA = 0
SCOPE_SYSETC = 1
SCOPE_FONTS = 2
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:
return access(f, acc_mode)
def getRecordingFilename(basename, dirname = None):
+ if not dirname.endswith('/'):
+ dirname += '/'
+
# filter out non-allowed characters
non_allowed_characters = "/.\\:*?<>|\""
filename = ""
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: