comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:215d51f2a82f
1 #!/usr/bin/env python2
2 # -*- encoding: utf-8 -*-
3
4 from __future__ import with_statement
5
6 from errno import ENOENT, ENOTDIR
7 from os.path import realpath
8 from sys import argv, exit
9 from threading import Lock
10 from time import time
11
12 import os
13
14 from fuse import FUSE, FuseOSError, Operations, LoggingMixIn
15
16
17 class Danbooru(LoggingMixIn, Operations):
18 '''
19 Represent a list of images as a filesystem tree, with nice tag filtering.
20 '''
21
22 def __init__(self, tagfiles, root):
23 '''
24 Takes a list of files containing the tags. They have to be named as the
25 image, with ".tags" at the end.
26 '''
27 self.paths = {}
28 self.files = {}
29 self.tags = {}
30 self.cache = {}
31
32 start = time()
33
34 for name in tagfiles:
35 filename = name.replace('.tags', '')
36 basename = os.path.basename(filename)
37 self.paths[basename] = filename
38 tags = []
39 self.files[basename] = tags
40 with open(name, 'r') as file:
41 for line in file:
42 for tag in line.split():
43 tag = tag.decode('UTF-8')
44 if '/' in tag:
45 tag = tag.replace('/', u'�') #XXX
46 tags.append(tag)
47 self.tags.setdefault(tag, []).append(basename)
48
49 print('[%d] Index done.' % (time() - start))
50
51 self.root = root
52 self.rwlock = Lock()
53
54 def _split_path(self, path):
55 if path == '/':
56 return (None, None)
57
58 path = path[1:].split('/')
59 if filter(lambda tag: tag not in self.tags, path[:-1]):
60 raise FuseOSError(ENOENT)
61
62 if path[-1] in self.tags:
63 return (path, None)
64
65 if path[-1] not in self.paths:
66 raise FuseOSError(ENOENT)
67
68 return (path[:-1], self.paths[path[-1]])
69
70 def access(self, path, mode):
71 self._split_path(path)
72
73 def getattr(self, path, fh=None):
74 tags, file = self._split_path(path)
75 path = file if file else self.root
76 st = os.lstat(path)
77 return dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime',
78 'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid'))
79
80 getxattr = None
81 listxattr = None
82 def open(self, path, flags):
83 tags, file = self._split_path(path)
84 return os.open(file, flags)
85
86 def read(self, path, size, offset, fh):
87 with self.rwlock:
88 os.lseek(fh, offset, 0)
89 return os.read(fh, size)
90
91 def readdir(self, path, fh):
92 if path == '/':
93 return ['.', '..'] + self.tags.keys() + self.files.keys()
94
95 tags, file = self._split_path(path)
96 if file:
97 return FuseOSError(ENOTDIR)
98
99 tags = set(tags)
100
101 l = ' '.join(tags)
102 if l in self.cache:
103 return ['.', '..'] + self.cache[l]
104
105 # Get the list of the files corresponding to those tags.
106 files = reduce((lambda s, t: s.intersection(self.tags[t])), tags, set(self.files))
107
108 # Get the tags of those files.
109 taglist = reduce((lambda s, f: s.union(self.files[f])), files, set())
110 taglist -= tags
111
112 # Remove the tags that can’t precise the file list anymore.
113 rm = reduce((lambda s, f: s.intersection(self.files[f])), files, taglist)
114 taglist -= rm
115
116 self.cache[l] = list(taglist) + list(files)
117 return ['.', '..'] + self.cache[l]
118
119 readlink = os.readlink
120
121 def release(self, path, fh):
122 return os.close(fh)
123
124 def statfs(self, path):
125 tags, file = self._split_path(path)
126 path = file if file else self.root
127 stv = os.statvfs(path)
128 return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree',
129 'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag',
130 'f_frsize', 'f_namemax'))
131
132 utimens = os.utime
133
134
135 if __name__ == '__main__':
136 if len(argv) < 3:
137 print('usage: %s <tag file> [<tag file>...] <mountpoint>' % argv[0])
138 exit(1)
139
140 mountpoint = argv.pop()
141
142 fuse = FUSE(Danbooru(argv[1:], os.path.dirname(mountpoint)), mountpoint, foreground=True)