Chris@44
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@44
|
2
|
Chris@44
|
3 #include "FeatureFileIndex.h"
|
Chris@44
|
4 #include "TypeRegistrar.h"
|
Chris@44
|
5
|
Chris@44
|
6 #include <QMutexLocker>
|
Chris@44
|
7 #include <QDir>
|
Chris@44
|
8
|
Chris@44
|
9 #include "base/TempDirectory.h"
|
Chris@44
|
10 #include "base/Exceptions.h"
|
Chris@44
|
11
|
Chris@44
|
12 using namespace Dataquay;
|
Chris@44
|
13
|
Chris@44
|
14
|
Chris@44
|
15 namespace ClassicalData {
|
Chris@44
|
16
|
Chris@44
|
17 FeatureFileIndex *
|
Chris@44
|
18 FeatureFileIndex::getInstance()
|
Chris@44
|
19 {
|
Chris@44
|
20 static FeatureFileIndex instance;
|
Chris@44
|
21 return &instance;
|
Chris@44
|
22 }
|
Chris@44
|
23
|
Chris@44
|
24 FeatureFileIndex::FeatureFileIndex() :
|
Chris@45
|
25 m_bs(0),
|
Chris@44
|
26 m_index(0)
|
Chris@44
|
27 {
|
Chris@44
|
28 try {
|
Chris@44
|
29 m_indexFileName = getIndexFileName();
|
Chris@44
|
30 } catch (DirectoryCreationFailed f) {
|
Chris@44
|
31 std::cerr << "FeatureFileIndex: ERROR: Failed to find or create index directory: " << f.what() << std::endl;
|
Chris@44
|
32 return;
|
Chris@44
|
33 }
|
Chris@44
|
34
|
Chris@45
|
35 m_bs = new BasicStore;
|
Chris@52
|
36 m_bs->setBaseUri(Uri(QUrl::fromLocalFile(m_indexFileName)));
|
Chris@52
|
37
|
Chris@45
|
38 m_index = new TransactionalStore(m_bs);
|
Chris@44
|
39
|
Chris@45
|
40 TypeRegistrar::addMappings(m_bs, 0);
|
Chris@44
|
41
|
Chris@44
|
42 if (QFile(m_indexFileName).exists()) {
|
Chris@45
|
43 m_bs->import(QUrl::fromLocalFile(m_indexFileName),
|
Chris@45
|
44 BasicStore::ImportIgnoreDuplicates);
|
Chris@44
|
45 //!!! catch
|
Chris@44
|
46 }
|
Chris@44
|
47 }
|
Chris@44
|
48
|
Chris@44
|
49 FeatureFileIndex::~FeatureFileIndex()
|
Chris@44
|
50 {
|
Chris@45
|
51 delete m_index;
|
Chris@45
|
52 delete m_bs;
|
Chris@44
|
53 }
|
Chris@44
|
54
|
Chris@44
|
55 QString
|
Chris@44
|
56 FeatureFileIndex::getIndexFileName()
|
Chris@44
|
57 {
|
Chris@44
|
58 QDir d = TempDirectory::getInstance()->getContainingPath();
|
Chris@44
|
59 QString n("index");
|
Chris@44
|
60 QFileInfo fi(d.filePath(n));
|
Chris@44
|
61
|
Chris@44
|
62 if ((fi.exists() && !fi.isDir()) ||
|
Chris@44
|
63 (!fi.exists() && !d.mkdir(n))) {
|
Chris@44
|
64 throw DirectoryCreationFailed(fi.filePath());
|
Chris@44
|
65 }
|
Chris@44
|
66
|
Chris@44
|
67 return QDir(fi.filePath()).filePath("features.ttl");
|
Chris@44
|
68 }
|
Chris@44
|
69
|
Chris@44
|
70 QString
|
Chris@44
|
71 FeatureFileIndex::getFeatureDirectoryName()
|
Chris@44
|
72 {
|
Chris@44
|
73 QDir d = TempDirectory::getInstance()->getContainingPath();
|
Chris@44
|
74 QString n("features");
|
Chris@44
|
75 QFileInfo fi(d.filePath(n));
|
Chris@44
|
76
|
Chris@44
|
77 if ((fi.exists() && !fi.isDir()) ||
|
Chris@44
|
78 (!fi.exists() && !d.mkdir(n))) {
|
Chris@44
|
79 throw DirectoryCreationFailed(fi.filePath());
|
Chris@44
|
80 }
|
Chris@44
|
81
|
Chris@44
|
82 return fi.filePath();
|
Chris@44
|
83 }
|
Chris@44
|
84
|
Chris@44
|
85 void
|
Chris@45
|
86 FeatureFileIndex::loadFor(AudioFile *tf, Store *store)
|
Chris@44
|
87 {
|
Chris@44
|
88 if (!m_index) {
|
Chris@44
|
89 std::cerr << "FeatureFileIndex::loadFor: No index!" << std::endl;
|
Chris@44
|
90 return;
|
Chris@44
|
91 }
|
Chris@44
|
92 updateIndex();
|
Chris@44
|
93
|
Chris@46
|
94 QSet<Uri> fileUris;
|
Chris@46
|
95
|
Chris@46
|
96 // The same file may be referred to with more than one URI; we
|
Chris@46
|
97 // want to load any or all of: the URI in our file object; encoded
|
Chris@46
|
98 // version of same; and any other file that is recorded as having
|
Chris@46
|
99 // the same hash (i.e. it is the same file).
|
Chris@46
|
100
|
Chris@46
|
101 fileUris.insert(tf->uri());
|
Chris@46
|
102
|
Chris@46
|
103 // and again with encoded version of file URI
|
Chris@46
|
104 QByteArray enc = QUrl(tf->uri().toString()).toEncoded();
|
Chris@46
|
105 fileUris.insert(Uri(QString::fromUtf8(enc)));
|
Chris@46
|
106
|
Chris@46
|
107 // and again with anything else having the same hash
|
Chris@46
|
108 if (tf->hash() != "") {
|
Chris@52
|
109 Triple pattern(Node(), store->expand("foaf:sha1"), Node(tf->hash()));
|
Chris@46
|
110 Triples results = m_index->match(pattern);
|
Chris@46
|
111 std::cerr << "FeatureFileIndex::loadFor: " << results.size() << " audio file(s) found with hash " << tf->hash().toStdString() << std::endl;
|
Chris@46
|
112 foreach (Triple t, results) {
|
Chris@46
|
113 fileUris.insert(Uri(t.a.value));
|
Chris@46
|
114 }
|
Chris@46
|
115 }
|
Chris@46
|
116
|
Chris@46
|
117 foreach (Uri u, fileUris) {
|
Chris@46
|
118 loadFor(tf->uri(), u, tf->hash(), store);
|
Chris@46
|
119 }
|
Chris@46
|
120 }
|
Chris@46
|
121
|
Chris@46
|
122 bool
|
Chris@46
|
123 FeatureFileIndex::loadFor(Uri canonicalUri, Uri afuri,
|
Chris@46
|
124 QString hash, Store *store)
|
Chris@46
|
125 {
|
Chris@45
|
126 // The AudioFile object has a URI and a hash. Feature files
|
Chris@45
|
127 // generated for this AudioFile should ideally have a matching
|
Chris@45
|
128 // hash; if they have no hash, then the URI should match. If the
|
Chris@45
|
129 // hash is present in the feature file but does not match, then it
|
Chris@45
|
130 // cannot be the right track even if the URI matches.
|
Chris@45
|
131
|
Chris@52
|
132 Triple pattern(Node(), store->expand("foaf:primaryTopic"), afuri);
|
Chris@46
|
133 Triples results = m_index->match(pattern);
|
Chris@46
|
134 std::cerr << "FeatureFileIndex::loadFor: " << results.size() << " feature file(s) for audio file " << afuri << std::endl;
|
Chris@45
|
135
|
Chris@46
|
136 bool loadedSomething = false;
|
Chris@45
|
137
|
Chris@46
|
138 foreach (Triple t, results) {
|
Chris@46
|
139 try {
|
Chris@46
|
140 BasicStore *b = BasicStore::load(QUrl(t.a.value));
|
Chris@46
|
141 Triples ts = b->match
|
Chris@52
|
142 (Triple(afuri, store->expand("a"), m_index->expand("mo:AudioFile")));
|
Chris@46
|
143 std::cerr << "FeatureFileIndex::loadFor: feature file "
|
Chris@46
|
144 << t.a << " has " << ts.size() << " type node(s) for this audio file" << std::endl;
|
Chris@46
|
145 bool someGood = false;
|
Chris@46
|
146 foreach (Triple t, ts) {
|
Chris@46
|
147 bool good = true;
|
Chris@46
|
148 if (hash != "") {
|
Chris@46
|
149 Triples hashts = b->match
|
Chris@46
|
150 (Triple(afuri, m_index->expand("foaf:sha1"), Node()));
|
Chris@46
|
151 std::cerr << "FeatureFileIndex::loadFor: feature file "
|
Chris@46
|
152 << t.a << " has " << hashts.size() << " hashes for this file" << std::endl;
|
Chris@46
|
153 if (!hashts.empty()) {
|
Chris@46
|
154 good = false;
|
Chris@46
|
155 foreach (Triple hasht, hashts) {
|
Chris@46
|
156 if (hasht.c.value == hash) {
|
Chris@46
|
157 std::cerr << "Hash " << hasht.c << " matches our hash " << hash.toStdString() << std::endl;
|
Chris@46
|
158 good = true;
|
Chris@46
|
159 break;
|
Chris@46
|
160 }
|
Chris@46
|
161 }
|
Chris@46
|
162 if (!good) {
|
Chris@46
|
163 std::cerr << "(no hash matches, eliminating file)" << std::endl;
|
Chris@46
|
164 }
|
Chris@46
|
165 } else {
|
Chris@46
|
166 std::cerr << "(so cannot eliminate file via hash)" << std::endl;
|
Chris@46
|
167 }
|
Chris@46
|
168 }
|
Chris@46
|
169 if (good) {
|
Chris@46
|
170 std::cerr << "...going to import this one" << std::endl;
|
Chris@46
|
171 someGood = true;
|
Chris@46
|
172 }
|
Chris@46
|
173 }
|
Chris@46
|
174 if (someGood) {
|
Chris@46
|
175 Triples all = b->match(Triple());
|
Chris@46
|
176 std::cerr << "Importing " << all.size() << " triple(s) into store" << std::endl;
|
Chris@46
|
177 // Replace instances of the audio file URI with our
|
Chris@46
|
178 // canonical URI (we want to make sure we're
|
Chris@46
|
179 // associating these facts with our own URI for this
|
Chris@46
|
180 // file, even if they originated from a different URI
|
Chris@46
|
181 // with the same hash)
|
Chris@52
|
182 Node from = Node(Uri(afuri.toString()));
|
Chris@52
|
183 Node to = Node(Uri(canonicalUri.toString()));
|
Chris@46
|
184 foreach (Triple t, all) {
|
Chris@46
|
185 if (t.a == from) t.a = to;
|
Chris@46
|
186 if (t.c == from) t.c = to;
|
Chris@46
|
187 store->add(t);
|
Chris@46
|
188 }
|
Chris@46
|
189 loadedSomething = true;
|
Chris@46
|
190 }
|
Chris@46
|
191 } catch (...) { }
|
Chris@45
|
192 }
|
Chris@45
|
193
|
Chris@46
|
194 return loadedSomething;
|
Chris@44
|
195 }
|
Chris@44
|
196
|
Chris@44
|
197 void
|
Chris@45
|
198 FeatureFileIndex::featureFileAdded(QString filepath)
|
Chris@45
|
199 {
|
Chris@45
|
200 index(QUrl::fromLocalFile(filepath));
|
Chris@45
|
201 }
|
Chris@45
|
202
|
Chris@45
|
203 void
|
Chris@44
|
204 FeatureFileIndex::updateIndex()
|
Chris@44
|
205 {
|
Chris@44
|
206 QMutexLocker locker(&m_mutex);
|
Chris@44
|
207 if (!m_index) return;
|
Chris@44
|
208
|
Chris@46
|
209 std::cerr << "Generating index..." << std::endl;
|
Chris@46
|
210
|
Chris@44
|
211 QDir featureDir;
|
Chris@44
|
212 try {
|
Chris@44
|
213 QString s = getFeatureDirectoryName();
|
Chris@44
|
214 featureDir = QDir(s);
|
Chris@44
|
215 } catch (DirectoryCreationFailed f) {
|
Chris@44
|
216 std::cerr << "FeatureFileIndex::updateIndex: ERROR: Failed to find or create feature directory: " << f.what() << std::endl;
|
Chris@44
|
217 return;
|
Chris@44
|
218 }
|
Chris@44
|
219
|
Chris@44
|
220 featureDir.setFilter(QDir::Files);
|
Chris@44
|
221
|
Chris@44
|
222 for (unsigned int i = 0; i < featureDir.count(); ++i) {
|
Chris@44
|
223 QFileInfo fi(featureDir.filePath(featureDir[i]));
|
Chris@44
|
224 if (fi.isFile() && fi.isReadable()) {
|
Chris@45
|
225 index(QUrl::fromLocalFile(fi.filePath()));
|
Chris@44
|
226 }
|
Chris@44
|
227 }
|
Chris@44
|
228
|
Chris@44
|
229 //!!! remove triples from index that refer to nonexistent files?
|
Chris@44
|
230
|
Chris@44
|
231 std::cerr << "Saving index to " << m_indexFileName.toStdString() << std::endl;
|
Chris@45
|
232 m_bs->save(m_indexFileName);
|
Chris@46
|
233
|
Chris@46
|
234 std::cerr << "Done" << std::endl;
|
Chris@45
|
235 }
|
Chris@45
|
236
|
Chris@45
|
237 void
|
Chris@45
|
238 FeatureFileIndex::index(QUrl fileUrl)
|
Chris@45
|
239 {
|
Chris@52
|
240 Triple typeTriple(Uri(fileUrl), m_index->expand("a"), m_index->expand("foaf:Document"));
|
Chris@45
|
241
|
Chris@45
|
242 if (m_index->contains(typeTriple)) {
|
Chris@45
|
243 return;
|
Chris@45
|
244 }
|
Chris@45
|
245
|
Chris@45
|
246 Transaction *tx = m_index->startTransaction();
|
Chris@45
|
247 tx->add(typeTriple);
|
Chris@45
|
248
|
Chris@45
|
249 try {
|
Chris@45
|
250 BasicStore *b = BasicStore::load(fileUrl);
|
Chris@45
|
251 Triples ts = b->match
|
Chris@52
|
252 (Triple(Node(), m_index->expand("a"), m_index->expand("mo:AudioFile")));
|
Chris@45
|
253 foreach (Triple t, ts) {
|
Chris@52
|
254 tx->add(Triple(Uri(fileUrl), m_index->expand("foaf:primaryTopic"), t.a));;
|
Chris@46
|
255 Triples hashts = b->match
|
Chris@46
|
256 (Triple(t.a, m_index->expand("foaf:sha1"), Node()));
|
Chris@46
|
257 foreach (Triple hasht, hashts) {
|
Chris@46
|
258 tx->add(hasht);
|
Chris@46
|
259 }
|
Chris@45
|
260 }
|
Chris@46
|
261 } catch (std::exception &e) {
|
Chris@46
|
262 std::cerr << "Caught exception: \"" << e.what() << "\" while indexing "
|
Chris@46
|
263 << Uri(fileUrl) << ", skipping" << std::endl;
|
Chris@46
|
264 }
|
Chris@45
|
265
|
Chris@48
|
266 tx->commit();
|
Chris@45
|
267 delete tx;
|
Chris@44
|
268 }
|
Chris@44
|
269
|
Chris@44
|
270
|
Chris@44
|
271 }
|
Chris@44
|
272
|