Mercurial > danboorufs
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) |