Hi, > I tried to set up a dlna server with an earlier version of vdr-nfofs a few > months ago. It kinda worked but there were some performance problems in > regard to file system operations. I hacked a file descriptor cache into the > python code which helped a bit. Tobi might have included this in the new > version. Looks like my patches haven't been accepted for 0.7. You can try my patchset but I haven't worked on that for some months now. Patch is not minimal and not cleanly separated, so you should pick the tidbits you like. Cya, Ed
diff -ur vdrnfofs-0.6/vdrnfofs/concatenated_file_reader.py vdrnfofs-0.6.tyger1//vdrnfofs/concatenated_file_reader.py --- vdrnfofs-0.6/vdrnfofs/concatenated_file_reader.py 2011-04-14 23:59:21.000000000 +0200 +++ vdrnfofs-0.6.tyger1//vdrnfofs/concatenated_file_reader.py 2011-04-18 22:35:43.776907385 +0200 @@ -20,32 +20,37 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os +from threading import Lock +from cStringIO import StringIO class ConcatenatedFileReader: def __init__(self, filenames): self.files = [(f, os.path.getsize(f)) for f in filenames] self.current_filename = None self.current_file = None + self.rwlock = Lock() def read(self, offset, size): - buffer = "" + buffer = StringIO() ptr = offset - while (len(buffer) < size): - (filename, file_offset) = self.filename_from_offset(ptr) - if filename: - if (self.current_filename != filename): - if self.current_file: - self.current_file.close() - self.current_filename = filename - self.current_file = open(filename, 'r') - self.current_file.seek(file_offset) - buffer += self.current_file.read(size - len(buffer)) - ptr = offset + len(buffer) - else: - break - return buffer + with self.rwlock: + while (buffer.tell() < size): + (filename, file_offset) = self.filename_from_offset(ptr) + if filename: + if (self.current_filename != filename): + if self.current_file: + self.current_file.close() + self.current_filename = filename + self.current_file = open(filename, 'r') + self.current_file.seek(file_offset) + buffer.write(self.current_file.read(size - buffer.tell())) + ptr = offset + buffer.tell() + else: + break + return buffer.getvalue() - def release(self): + def __del__(self): + # print "CFR::DEL" if self.current_file: self.current_file.close() Only in vdrnfofs-0.6.tyger1//vdrnfofs: concatenated_file_reader.py~ diff -ur vdrnfofs-0.6/vdrnfofs/filesystemnodes.py vdrnfofs-0.6.tyger1//vdrnfofs/filesystemnodes.py --- vdrnfofs-0.6/vdrnfofs/filesystemnodes.py 2011-04-14 23:27:02.000000000 +0200 +++ vdrnfofs-0.6.tyger1//vdrnfofs/filesystemnodes.py 2011-04-18 22:39:19.540905997 +0200 @@ -26,6 +26,7 @@ from concatenated_file_reader import * from vdr import * +from threading import Lock class NodeAttributes(fuse.Stat): def __init__(self): @@ -40,40 +41,90 @@ self.st_mtime = 0 self.st_ctime = 0 + class MpgNode: + cachesize = 20 + cachelock = Lock() + cache = [] + + @classmethod + def create(cls, path, cache): + if not cache: + return cls(path) + with cls.cachelock: + index = next((i for i in xrange(len(cls.cache)-1, -1, -1) if cls.cache[i].key == path), None) + if index is not None: + # print "%s HIT" % cls.__name__ + node = cls.cache.pop(index) + else: + # print "%s MISS" % cls.__name__ + node = cls(path) + if len(cls.cache) > cls.cachesize: + del cls.cache[0] + cls.cache.append(node) + # print "%s cache size: %d" %(cls.__name__, len(cls.cache)) + return node + def __init__(self, path): + # print "MpgNode::init %s" % path + self.key = path self.path = os.path.normpath(path) self.mpeg_files = glob.glob(path + '/[0-9]*.vdr') if not self.mpeg_files: self.mpeg_files = glob.glob(path + '/[0-9]*.ts') self.mpeg_files.sort() - self.file_system_name = os.path.basename(os.path.abspath(path + '/..')) + '_' + os.path.basename(path) + '.mpg' + self.file_system_name ="%s_%s.mpg" % (os.path.basename(os.path.abspath(path + '/..')), os.path.basename(path)) self.reader = ConcatenatedFileReader(self.mpeg_files) - - def size(self): size = 0 for file in self.mpeg_files: size += os.path.getsize(file) - return size + self.filesize = size - def read(self, offset, size): - return self.reader.read(offset, size) + def size(self): + # print "MpgNode::size" + return self.filesize - def release(self): - return self.reader.release() + def read(self, offset, size): + # print "MpgNode::read %d" % offset + return self.reader.read(offset, size) def get_stat(self): + # print "MpgNode::get_stat" attr = NodeAttributes() - attr.st_mode = stat.S_IFREG | 644 + attr.st_mode = stat.S_IFREG | 0444 attr.st_nlink = 1 - attr.st_size = self.size() + attr.st_size = self.filesize return attr class NfoNode: + cachesize = 20 + cachelock = Lock() + cache = [] + + @classmethod + def create(cls, path, cache): + if not cache: + return cls(path) + with cls.cachelock: + index = next((i for i in xrange(len(cls.cache)-1, -1, -1) if cls.cache[i].key == path), None) + if index is not None: + # print "%s HIT" % cls.__name__ + node = cls.cache.pop(index) + else: + # print "%s MISS" % cls.__name__ + node = cls(path) + if len(cls.cache) > cls.cachesize: + del cls.cache[0] + cls.cache.append(node) + # print "%s cache size: %d" %(cls.__name__, len(cls.cache)) + return node + def __init__(self, path): + # print "NfoNode::init %s" % path + self.key = path self.path = os.path.normpath(path) - self.file_system_name = os.path.basename(os.path.abspath(path + '/..')) + '_' + os.path.basename(path) + '.nfo' + self.file_system_name ="%s_%s.nfo" % (os.path.basename(os.path.abspath(path + '/..')), os.path.basename(path)) if os.path.exists(path + '/info.vdr'): info_vdr = InfoVdr(path + '/info.vdr') elif os.path.exists(path + '/info'): @@ -86,39 +137,75 @@ <plot>%s</plot> </movie> """ % (info_vdr['T'], info_vdr['D']) + self.contentsize = len(self.nfo_content) def size(self): - return len(self.nfo_content) + return self.contentsize def read(self, offset, size): + # print "NfoNode::read %s" % offset return self.nfo_content[offset:offset+size] def get_stat(self): + # print "NfoNode::get_stat" attr = NodeAttributes() - attr.st_mode = stat.S_IFREG | 644 + attr.st_mode = stat.S_IFREG | 0444 attr.st_nlink = 1 - attr.st_size = self.size() + attr.st_size = self.contentsize return attr class DirNode: + cachesize = 100 + cachelock = Lock() + cache = [] + + @classmethod + def create(cls, path, cache): + if not cache: + return cls(path) + with cls.cachelock: + index = next((i for i in xrange(len(cls.cache)-1, -1, -1) if cls.cache[i].key == path), None) + if index is not None: + # print "%s HIT" % cls.__name__ + node = cls.cache.pop(index) + else: + # print "%s MISS" % cls.__name__ + node = cls(path) + if len(cls.cache) > cls.cachesize: + del cls.cache[0] + cls.cache.append(node) + # print "%s cache size: %d" %(cls.__name__, len(cls.cache)) + return node + def __init__(self, path): + # print "DirNode::init %s" % path + self.key = path self.path = os.path.normpath(path) self.file_system_name = os.path.basename(path) self.cache = [] + self.mysize = None def content(self): + # print "DirNode::content %s" % self.path if not self.cache: + # print "without cache" for entry in os.listdir(self.path): - entry = self.path + '/' + entry + entry = "/".join((self.path, entry)) if self.is_sub_folder(entry): - self.cache.append(DirNode(entry)) + self.cache.append(DirNode.create(entry, True)) for recording in glob.glob(entry + '/*.rec'): if os.path.exists(recording + '/info.vdr') or os.path.exists(recording + '/info'): - self.cache.append(MpgNode(recording)) - self.cache.append(NfoNode(recording)) + self.cache.append(MpgNode(recording)) # DONT CACHE! + self.cache.append(NfoNode(recording)) # DONT CACHE! return self.cache + def size(self): + # print "DirNode::size" + if not self.mysize: + self.mysize = len(self.content()) + return self.mysize + def is_sub_folder(self, dir): if not os.path.isdir(dir): return False @@ -130,7 +217,8 @@ return False def get_stat(self): + # print "DirNode::get_stat" attr = NodeAttributes() - attr.st_mode = stat.S_IFDIR | 0755 - attr.st_nlink = 2 + len(self.content()) + attr.st_mode = stat.S_IFDIR | 0555 + attr.st_nlink = 2 + self.size() return attr Only in vdrnfofs-0.6.tyger1//vdrnfofs: filesystemnodes.py~ diff -ur vdrnfofs-0.6/vdrnfofs/vdrnfofs.py vdrnfofs-0.6.tyger1//vdrnfofs/vdrnfofs.py --- vdrnfofs-0.6/vdrnfofs/vdrnfofs.py 2011-04-14 23:35:57.000000000 +0200 +++ vdrnfofs-0.6.tyger1//vdrnfofs/vdrnfofs.py 2011-04-18 22:38:40.540905398 +0200 @@ -29,30 +29,32 @@ from concatenated_file_reader import * from vdr import * from filesystemnodes import * +from traceback import format_exc fuse.fuse_python_api = (0, 2) -def get_node(video, path): +def get_node(video, path, cache=False): virtual_path, virtual_file_extension = os.path.splitext(path) if virtual_file_extension in ['.mpg', '.nfo']: p = virtual_path.rfind('_') if p > 0: - video_path = video + '/' + virtual_path[0:p] + '/' + virtual_path[p+1:] + video_path = "%s/%s/%s" % (video, virtual_path[0:p], virtual_path[p+1:]) if not os.path.isdir(video_path): return None elif virtual_file_extension == '.mpg': - return MpgNode(video_path) + return MpgNode.create(video_path, cache) elif virtual_file_extension == '.nfo': - return NfoNode(video_path) + return NfoNode.create(video_path, cache) else: - if os.path.isdir(video + '/' + path): - return DirNode(video + path) + dir_path = video + path + if os.path.isdir(dir_path): + return DirNode.create(dir_path, cache) return None class VdrNfoFsFile: def __init__(self, path, flags, *mode): self.path = path - self.node = get_node(VdrNfoFsFile.video_root, path) + self.node = get_node(VdrNfoFsFile.video_root, path, True) def read(self, size, offset): try: @@ -60,10 +62,10 @@ return -errno.ENOENT return self.node.read(offset, size) except: - syslog.syslog('VdrFuseFs: Unexpected error for read(%s)' % self.path) + syslog.syslog('VdrFuseFs: Unexpected error for read(%s): ' % (self.path, format_exc())) - def release(self, flags): - self.node.release() +# def release(self, flags): + # print "VdrNfoFsFile::release %s" % self.node.key # def write(self, buf, offset): # return 0 @@ -90,24 +92,26 @@ self.video = "" def getattr(self, path): + # print "VNF::getattr" try: - node = get_node(self.video, path) + node = get_node(self.video, path, False) if node: + # print "got node %s" % node.key return node.get_stat() return -errno.ENOENT except: - syslog.syslog('VdrFuseFs: Unexpected error for getattr(%s): %s' % path) + syslog.syslog('VdrFuseFs: Unexpected error for getattr(%s): %s' % (path, format_exc())) def readdir(self, path, offset): try: yield fuse.Direntry('.') yield fuse.Direntry('..') - node = get_node(self.video, path) + node = get_node(self.video, path, True) if node: for item in node.content(): yield fuse.Direntry(item.file_system_name) except: - syslog.syslog('VdrFuseFs: Unexpected error for readdir(%s)' % path) + syslog.syslog('VdrFuseFs: Unexpected error for readdir(%s): %s' % (path, format_exc())) def main(self, *a, **kw): VdrNfoFsFile.video_root = self.video Only in vdrnfofs-0.6.tyger1//vdrnfofs: vdrnfofs.py~ diff -ur vdrnfofs-0.6/vdrnfofs/vdr.py vdrnfofs-0.6.tyger1//vdrnfofs/vdr.py --- vdrnfofs-0.6/vdrnfofs/vdr.py 2011-04-14 23:27:14.000000000 +0200 +++ vdrnfofs-0.6.tyger1//vdrnfofs/vdr.py 2011-04-18 16:50:09.040905717 +0200 @@ -23,10 +23,10 @@ def __init__(self, filename = None): self.values = {'T' : 'Unknown', 'D': 'No Description'} if filename: - file = open(filename, 'r') - for line in file: - line = line.rstrip("\r\n") - self.values[line[0]] = line[2:] + with open(filename, 'r') as file: + for line in file: + line = line.rstrip("\r\n") + self.values[line[0]] = line[2:] def __getitem__(self, key): return self.values[key] if self.values.has_key(key) else '' Only in vdrnfofs-0.6.tyger1//vdrnfofs: vdr.py~
_______________________________________________ vdr mailing list vdr@xxxxxxxxxxx http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr