72dbd5b40640924c026030ad46b54ed9d9d3738b
[lms] / python-lightmediascanner / lightmediascanner / lightmediascanner.c_lightmediascanner.pyx
1 # Copyright (C) 2007 by INdT
2 #
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 #
17 # @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
18
19 cdef class LightMediaScanner:
20     def __init__(self, char *db_path, parsers=None, charsets=None,
21                  slave_timeout=None, commit_interval=None):
22         if self.obj == NULL:
23             self.parsers = ()
24             self.obj = lms_new(db_path)
25             self.db_path = db_path
26             if self.obj == NULL:
27                 raise SystemError("Could not create LightMediaScanner.")
28             if parsers:
29                 for p in parsers:
30                     self.parser_find_and_add(p)
31             if charsets:
32                 for c in charsets:
33                     self.charset_add(c)
34             if slave_timeout is not None:
35                 self.set_slave_timeout(slave_timeout)
36             if commit_interval is not None:
37                 self.set_commit_interval(commit_interval)
38
39     def __dealloc__(self):
40         if self.obj != NULL:
41             if lms_free(self.obj) != 0:
42                 raise SystemError("Could not free internal object.")
43
44     def __str__(self):
45         parsers = []
46         for p in self.parsers:
47             parsers.append(str(p))
48         parsers = ", ".join(parsers)
49         return ("%s(db_path=%r, slave_timeout=%d, commit_interval=%d, "
50                 "parsers=[%s])") % (self.__class__.__name__, self.db_path,
51                                     self.slave_timeout, self.commit_interval,
52                                     parsers)
53
54     def __repr__(self):
55         parsers = []
56         for p in self.parsers:
57             parsers.append(repr(p))
58         parsers = ", ".join(parsers)
59         return ("%s(%#x, lms_t=%#x, db_path=%r, slave_timeout=%d, "
60                 "commit_interval=%d, parsers=[%s])") % \
61                 (self.__class__.__name__, <unsigned>self, <unsigned>self.obj,
62                  self.db_path, self.slave_timeout, self.commit_interval,
63                  parsers)
64
65     def process(self, char *top_path):
66         """Process directory recursively.
67
68         This operates on all files in all sub directories of top_path using
69         the added parsers.
70         """
71         if self.obj == NULL:
72             raise ValueError("LightMediaScanner is shallow.")
73         Py_BEGIN_ALLOW_THREADS
74         r = lms_process(self.obj, top_path)
75         Py_END_ALLOW_THREADS
76         return r
77
78     def check(self, char *top_path):
79         """Check (and update) files under directory.
80
81         This operates on all files in all sub directories of top_path using
82         the added parsers. If files are up to date, nothing is done, otherwise
83         they can be marked as deleted or updated if they still exists, but
84         with different size or modification time.
85         """
86         if self.obj == NULL:
87             raise ValueError("LightMediaScanner is shallow.")
88         Py_BEGIN_ALLOW_THREADS
89         r = lms_check(self.obj, top_path)
90         Py_END_ALLOW_THREADS
91         return r
92
93     def parser_find_and_add(self, char *name):
94         """Add a new plugin/parser based on it's name.
95
96         @rtype: L{Parser}
97         """
98         cdef lms_plugin_t *p
99         cdef Parser parser
100         if self.obj == NULL:
101             raise ValueError("LightMediaScanner is shallow.")
102         p = lms_parser_find_and_add(self.obj, name)
103         if p == NULL:
104             raise ValueError("LightMediaScanner cannot add parser %r" % name)
105         parser = Parser(self)
106         parser._set_obj(p)
107         self.parsers = self.parsers + (parser,)
108
109     def parser_add(self, char *so_path):
110         """Add a new plugin/parser based on it's whole path to shared object.
111
112         @rtype: L{Parser}
113         """
114         cdef lms_plugin_t *p
115         cdef Parser parser
116         if self.obj == NULL:
117             raise ValueError("LightMediaScanner is shallow.")
118         p = lms_parser_add(self.obj, so_path)
119         if p == NULL:
120             raise ValueError("LightMediaScanner cannot add parser %r" % so_path)
121         parser = Parser(self)
122         parser._set_obj(p)
123         self.parsers = self.parsers + (parser,)
124
125     def parser_del(self, Parser parser):
126         "Delete a plugin/parser."
127         if self.obj == NULL:
128             raise ValueError("LightMediaScanner is shallow.")
129         if lms_parser_del(self.obj, parser.obj) != 0:
130             raise SystemError("Could not delete parser %s" % parser)
131
132         parsers = []
133         for p in self.parsers:
134             if p != parser:
135                 parsers.append(p)
136         self.parsers = tuple(parsers)
137         parser._unset_obj()
138
139     def is_processing(self):
140         "@rtype: bool"
141         if self.obj == NULL:
142             raise ValueError("LightMediaScanner is shallow.")
143         return bool(lms_is_processing(self.obj))
144
145     def stop_processing(self):
146         "Stop process/check"
147         if self.obj == NULL:
148             raise ValueError("LightMediaScanner is shallow.")
149         lms_stop_processing(self.obj)
150
151     def get_slave_timeout(self):
152         "@rtype: int"
153         if self.obj == NULL:
154             raise ValueError("LightMediaScanner is shallow.")
155         return lms_get_slave_timeout(self.obj)
156
157     def set_slave_timeout(self, int ms):
158         """Set maximum time a parser may use.
159
160         This will be the timeout before killing the slave process running
161         some parser. If this happens, another slave process will be
162         started to continue from next file.
163         """
164         if self.obj == NULL:
165             raise ValueError("LightMediaScanner is shallow.")
166         lms_set_slave_timeout(self.obj, ms)
167
168     property slave_timeout:
169         def __get__(self):
170             return self.get_slave_timeout()
171
172         def __set__(self, int ms):
173             self.set_slave_timeout(ms)
174
175     def get_commit_interval(self):
176         "@rtype: int"
177         if self.obj == NULL:
178             raise ValueError("LightMediaScanner is shallow.")
179         return lms_get_commit_interval(self.obj)
180
181     def set_commit_interval(self, unsigned int transactions):
182         """Set the number of transactions between commits.
183
184         Sets how many transactions/files to handle in one commit, the more
185         the faster, but if one parser takes too long and it's killed due
186         slave_timeout being exceeded, then at most this number of transactions
187         will be lost.
188
189         Note that transaction here is not a single SQL statement, but it is
190         considered to be the processing of a file, which can be more than
191         just one.
192         """
193         if self.obj == NULL:
194             raise ValueError("LightMediaScanner is shallow.")
195         lms_set_commit_interval(self.obj, transactions)
196
197     property commit_interval:
198         def __get__(self):
199             return self.get_commit_interval()
200
201         def __set__(self, unsigned int transactions):
202             self.set_commit_interval(transactions)
203
204     def charset_add(self, char *charset):
205         """Add charset to list of supported input charsets/encoding.
206
207         If some string in analysed/parsed files are not UTF-8, then
208         it will try agains a list of charsets registered with this function.
209         """
210         if self.obj == NULL:
211             raise ValueError("LightMediaScanner is shallow.")
212         if lms_charset_add(self.obj, charset) != 0:
213             raise SystemError("LightMediaScanner cannot add charset %r" %
214                               charset)
215
216     def charset_del(self, char *charset):
217         "Del charset from list of supported input charsets/encoding."
218         if self.obj == NULL:
219             raise ValueError("LightMediaScanner is shallow.")
220         if lms_charset_del(self.obj, charset) != 0:
221             raise SystemError("LightMediaScanner cannot del charset %r" %
222                               charset)
223
224
225 cdef class Parser:
226     def __init__(self, scanner):
227         self.scanner = scanner
228
229     def __str__(self):
230         return "%s(name=%r)" % (self.__class__.__name__, self.name)
231
232     def __repr__(self):
233         return "%s(%#x, lms_plugin_t=%#x, name=%r)" % \
234                (self.__class__.__name__, <unsigned>self, <unsigned>self.obj,
235                 self.name)
236
237     cdef int _set_obj(self, lms_plugin_t *obj) except 0:
238         if self.obj != NULL:
239             raise ValueError("Parser already wraps an object.")
240         self.obj = obj
241         return 1
242
243     cdef int _unset_obj(self) except 0:
244         self.obj = NULL
245         return 1
246
247     def delete(self):
248         "Same as LightMediaScanner.parser_del(self)."
249         self.scanner.parser_del(self)
250
251     property name:
252         def __get__(self):
253             if self.obj == NULL:
254                 return None
255             if self.obj.name == NULL:
256                 return None
257             return self.obj.name