Mercurial > danboorufs
view danboorufs.py @ 0:215d51f2a82f draft
Initial commit.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Wed, 04 Jul 2012 13:22:01 +0200 |
parents | |
children | 63ccd8b0d615 |
line wrap: on
line source
#!/usr/bin/env python2 # -*- encoding: utf-8 -*- from __future__ import with_statement from errno import ENOENT, ENOTDIR from os.path import realpath from sys import argv, exit from threading import Lock from time import time import os from fuse import FUSE, FuseOSError, Operations, LoggingMixIn class Danbooru(LoggingMixIn, Operations): ''' Represent a list of images as a filesystem tree, with nice tag filtering. ''' def __init__(self, tagfiles, root): ''' Takes a list of files containing the tags. They have to be named as the image, with ".tags" at the end. ''' self.paths = {} self.files = {} self.tags = {} self.cache = {} start = time() for name in tagfiles: filename = name.replace('.tags', '') basename = os.path.basename(filename) self.paths[basename] = filename tags = [] self.files[basename] = tags with open(name, 'r') as file: for line in file: for tag in line.split(): tag = tag.decode('UTF-8') if '/' in tag: tag = tag.replace('/', u'�') #XXX tags.append(tag) self.tags.setdefault(tag, []).append(basename) print('[%d] Index done.' % (time() - start)) self.root = root self.rwlock = Lock() def _split_path(self, path): if path == '/': return (None, None) path = path[1:].split('/') if filter(lambda tag: tag not in self.tags, path[:-1]): raise FuseOSError(ENOENT) if path[-1] in self.tags: return (path, None) if path[-1] not in self.paths: raise FuseOSError(ENOENT) return (path[:-1], self.paths[path[-1]]) def access(self, path, mode): self._split_path(path) def getattr(self, path, fh=None): tags, file = self._split_path(path) path = file if file else self.root st = os.lstat(path) return dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime', 'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid')) getxattr = None listxattr = None def open(self, path, flags): tags, file = self._split_path(path) return os.open(file, flags) def read(self, path, size, offset, fh): with self.rwlock: os.lseek(fh, offset, 0) return os.read(fh, size) def readdir(self, path, fh): if path == '/': return ['.', '..'] + self.tags.keys() + self.files.keys() tags, file = self._split_path(path) if file: return FuseOSError(ENOTDIR) tags = set(tags) l = ' '.join(tags) if l in self.cache: return ['.', '..'] + self.cache[l] # Get the list of the files corresponding to those tags. files = reduce((lambda s, t: s.intersection(self.tags[t])), tags, set(self.files)) # Get the tags of those files. taglist = reduce((lambda s, f: s.union(self.files[f])), files, set()) taglist -= tags # Remove the tags that can’t precise the file list anymore. rm = reduce((lambda s, f: s.intersection(self.files[f])), files, taglist) taglist -= rm self.cache[l] = list(taglist) + list(files) return ['.', '..'] + self.cache[l] readlink = os.readlink def release(self, path, fh): return os.close(fh) def statfs(self, path): tags, file = self._split_path(path) path = file if file else self.root stv = os.statvfs(path) return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree', 'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag', 'f_frsize', 'f_namemax')) utimens = os.utime if __name__ == '__main__': if len(argv) < 3: print('usage: %s <tag file> [<tag file>...] <mountpoint>' % argv[0]) exit(1) mountpoint = argv.pop() fuse = FUSE(Danbooru(argv[1:], os.path.dirname(mountpoint)), mountpoint, foreground=True)