diff 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 diff
new file mode 100755
--- /dev/null
+++ b/danboorufs.py
@@ -0,0 +1,142 @@
+#!/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)