Merge lp:~schwann/gallery-app/gallery-datastructure-load into lp:gallery-app
- gallery-datastructure-load
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Günter Schwann |
Approved revision: | 768 |
Merged at revision: | 766 |
Proposed branch: | lp:~schwann/gallery-app/gallery-datastructure-load |
Merge into: | lp:gallery-app |
Diff against target: |
938 lines (+349/-114) 20 files modified
src/database/media-table.cpp (+25/-39) src/database/media-table.h (+7/-2) src/gallery-application.cpp (+3/-1) src/gallery-manager.cpp (+25/-28) src/gallery-manager.h (+1/-0) src/media-object-factory.cpp (+81/-3) src/media-object-factory.h (+15/-2) src/media/media-collection.cpp (+32/-1) src/media/media-collection.h (+5/-1) src/media/media-monitor.cpp (+77/-6) src/media/media-monitor.h (+15/-5) src/photo/photo-caches.cpp (+1/-1) src/qml/qml-media-collection-model.cpp (+1/-1) tests/autopilot/gallery_app/emulators/gallery_utils.py (+1/-6) tests/autopilot/gallery_app/tests/test_events_view.py (+7/-4) tests/autopilot/gallery_app/tests/test_photo_viewer.py (+8/-6) tests/autopilot/gallery_app/tests/test_photos_view.py (+7/-4) tests/unittests/mediaobjectfactory/tst_mediaobjectfactory.cpp (+29/-0) tests/unittests/stubs/gallery-manager_stub.cpp (+5/-0) tests/unittests/stubs/media-table_stub.cpp (+4/-4) |
To merge this branch: | bzr merge lp:~schwann/gallery-app/gallery-datastructure-load |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
Thomas Moenicke (community) | Approve | ||
Review via email: mp+172348@code.launchpad.net |
Commit message
Load the media object from the DB without checkin for the files (and separate queries)
Do a datastructure / file syetem consitency check in a background thread
Description of the change
Load the whole media collection in go from the DB.
Do a consistency check afterwards in the background.
Gallery now reacts properly when files are deleted on the file system.
Usually the "Loading..." Screen is now shown anymore.
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:763
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:765
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:766
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:766
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:767
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Thomas Moenicke (thomas-moenicke) wrote : | # |
looks like in addMedia the Video* pointer is not really needed
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:768
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:768
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:768
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Günter Schwann (schwann) wrote : | # |
Top approving again - now that jenkins seems to work ok again
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:768
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'src/database/media-table.cpp' |
2 | --- src/database/media-table.cpp 2013-06-28 12:33:19 +0000 |
3 | +++ src/database/media-table.cpp 2013-07-02 13:33:30 +0000 |
4 | @@ -34,45 +34,6 @@ |
5 | } |
6 | |
7 | /*! |
8 | - * \brief MediaTable::verifyFiles |
9 | - * Runs though the table, removes references to files |
10 | - * that have been deleted from disk. |
11 | - */ |
12 | -void MediaTable::verifyFiles() |
13 | -{ |
14 | - QSqlQuery query(*m_db->getDB()); |
15 | - QList<qint64> to_delete; |
16 | - query.prepare("SELECT id, filename FROM MediaTable"); |
17 | - if (!query.exec()) |
18 | - m_db->logSqlError(query); |
19 | - |
20 | - // Stat each file. Make a list of files that no longer exist. |
21 | - while (query.next()) { |
22 | - // stat'ing and sync'ing file info over even several hundred photos is an |
23 | - // expensive operation since it involves lots of I/O, so spin the event |
24 | - // loop so that the UI remains responsive |
25 | - QApplication::processEvents(); |
26 | - |
27 | - qint64 id = query.value(0).toLongLong(); |
28 | - QFile file(query.value(1).toString()); |
29 | - |
30 | - if (!file.exists()) |
31 | - to_delete.append(id); |
32 | - } |
33 | - |
34 | - // Delete any references to non-existent files. |
35 | - m_db->getDB()->transaction(); |
36 | - foreach (qint64 id, to_delete) { |
37 | - // spin the event loop so that the UI remains responsive |
38 | - QApplication::processEvents(); |
39 | - |
40 | - remove(id); |
41 | - } |
42 | - |
43 | - m_db->getDB()->commit(); |
44 | -} |
45 | - |
46 | -/*! |
47 | * \brief MediaTable::getIdForMedia Returns the row ID for the given photo. |
48 | * \param filename |
49 | * \return Returns the row ID for the given photo. If none exists, -1 will be returned. |
50 | @@ -263,6 +224,31 @@ |
51 | } |
52 | |
53 | /*! |
54 | + * \brief MediaTable::emitAllRows goes through the whole DB and emits a row() signal |
55 | + * for every single row with all the Database |
56 | + */ |
57 | +void MediaTable::emitAllRows() |
58 | +{ |
59 | + QSqlQuery query(*m_db->getDB()); |
60 | + query.prepare("SELECT * FROM MediaTable"); |
61 | + if (!query.exec()) |
62 | + m_db->logSqlError(query); |
63 | + |
64 | + while (query.next()) { |
65 | + qint64 id = query.value(0).toInt(); |
66 | + QString filename = query.value(1).toString(); |
67 | + QSize size(query.value(2).toInt(), query.value(3).toInt()); |
68 | + QDateTime timestamp; |
69 | + timestamp.setMSecsSinceEpoch(query.value(4).toLongLong()); |
70 | + QDateTime exposuretime; |
71 | + exposuretime.setMSecsSinceEpoch(query.value(5).toLongLong()); |
72 | + Orientation orientation = static_cast<Orientation>(query.value(6).toInt()); |
73 | + qint64 filesize = query.value(7).toInt(); |
74 | + emit row(id, filename, size, timestamp, exposuretime, orientation, filesize); |
75 | + } |
76 | +} |
77 | + |
78 | +/*! |
79 | * \brief MediaTable::getRow Gets a row that already exists |
80 | * \param mediaId |
81 | * \param size |
82 | |
83 | === modified file 'src/database/media-table.h' |
84 | --- src/database/media-table.h 2013-06-28 12:33:19 +0000 |
85 | +++ src/database/media-table.h 2013-07-02 13:33:30 +0000 |
86 | @@ -37,8 +37,6 @@ |
87 | public: |
88 | explicit MediaTable(Database* db, QObject *parent = 0); |
89 | |
90 | - void verifyFiles(); |
91 | - |
92 | qint64 getIdForMedia(const QString& filename); |
93 | |
94 | qint64 createIdForMedia(const QString& filename, const QDateTime& timestamp, |
95 | @@ -63,6 +61,13 @@ |
96 | |
97 | QDateTime getExposureTime(qint64 mediaId); |
98 | |
99 | + void emitAllRows(); |
100 | + |
101 | +signals: |
102 | + void row(qint64 mediaId, const QString& filename, const QSize& size, |
103 | + const QDateTime& timestamp, const QDateTime& exposureTime, |
104 | + Orientation originalOrientation, qint64 filesize); |
105 | + |
106 | private: |
107 | Database* m_db; |
108 | }; |
109 | |
110 | === modified file 'src/gallery-application.cpp' |
111 | --- src/gallery-application.cpp 2013-06-28 11:53:54 +0000 |
112 | +++ src/gallery-application.cpp 2013-07-02 13:33:30 +0000 |
113 | @@ -206,7 +206,10 @@ |
114 | filterType = MediaSource::Photo; |
115 | m_galleryManager->enableContentLoadFilter(filterType); |
116 | } |
117 | + QApplication::processEvents(); |
118 | + |
119 | m_galleryManager->postInit(); |
120 | + QApplication::processEvents(); |
121 | if (m_cmdLineParser->startupTimer()) |
122 | qDebug() << "GalleryManager initialized" << m_timer->elapsed() << "ms"; |
123 | |
124 | @@ -230,4 +233,3 @@ |
125 | |
126 | m_timer->restart(); |
127 | } |
128 | - |
129 | |
130 | === modified file 'src/gallery-manager.cpp' |
131 | --- src/gallery-manager.cpp 2013-07-01 07:40:13 +0000 |
132 | +++ src/gallery-manager.cpp 2013-07-02 13:33:30 +0000 |
133 | @@ -193,13 +193,12 @@ |
134 | m_database = new Database(m_resource->databaseDirectory(), |
135 | m_resource->getRcUrl("sql").path()); |
136 | m_mediaFactory->setMediaTable(m_database->getMediaTable()); |
137 | - m_database->getMediaTable()->verifyFiles(); |
138 | m_defaultTemplate = new AlbumDefaultTemplate(); |
139 | m_mediaCollection = new MediaCollection(); |
140 | |
141 | initPreviewManager(); |
142 | + fillMediaCollection(); |
143 | startFileMonitoring(); |
144 | - fillMediaCollection(); |
145 | |
146 | collectionsInitialised = true; |
147 | |
148 | @@ -229,7 +228,7 @@ |
149 | EventCollection *GalleryManager::eventCollection() |
150 | { |
151 | if (!m_eventCollection) |
152 | - m_eventCollection = new EventCollection; |
153 | + m_eventCollection = new EventCollection(); |
154 | |
155 | return m_eventCollection; |
156 | } |
157 | @@ -303,23 +302,10 @@ |
158 | { |
159 | Q_ASSERT(m_mediaCollection); |
160 | |
161 | - QSet<DataObject*> photos; |
162 | - foreach (const QString &dirName, m_resource->mediaDirectories()) { |
163 | - QDir mediaDir(dirName); |
164 | - mediaDir.setFilter(QDir::Files); |
165 | - mediaDir.setSorting(QDir::Name); |
166 | - |
167 | - const QStringList filenames = mediaDir.entryList(); |
168 | - foreach (const QString& filename, filenames) { |
169 | - QFileInfo file(mediaDir, filename); |
170 | - DataObject *media = m_mediaFactory->create(file); |
171 | - if (media) { |
172 | - photos.insert(media); |
173 | - } |
174 | - } |
175 | - } |
176 | - |
177 | - m_mediaCollection->addMany(photos); |
178 | + QSet<DataObject*> medias; |
179 | + medias = m_mediaFactory->mediasFromDB(); |
180 | + m_mediaCollection->addMany(medias); |
181 | + m_mediaFactory->clear(); |
182 | } |
183 | |
184 | /*! |
185 | @@ -334,7 +320,11 @@ |
186 | m_monitor = new MediaMonitor(); |
187 | QObject::connect(m_monitor, SIGNAL(mediaItemAdded(QString)), |
188 | this, SLOT(onMediaItemAdded(QString))); |
189 | + QObject::connect(m_monitor, SIGNAL(mediaItemRemoved(qint64)), |
190 | + this, SLOT(onMediaItemRemoved(qint64))); |
191 | + |
192 | m_monitor->startMonitoring(m_resource->mediaDirectories()); |
193 | + m_monitor->checkConsitency(m_mediaCollection); |
194 | } |
195 | |
196 | /*! |
197 | @@ -343,12 +333,19 @@ |
198 | */ |
199 | void GalleryManager::onMediaItemAdded(QString file) |
200 | { |
201 | - QFileInfo fi(file); |
202 | - MediaSource* media = m_mediaCollection->mediaFromFileinfo(fi); |
203 | - if (media == 0) { |
204 | - media = m_mediaFactory->create(fi); |
205 | - } |
206 | - if (media) { |
207 | - m_mediaCollection->add(media); |
208 | - } |
209 | + if (! m_mediaCollection->containsFile(file)) { |
210 | + QFileInfo fi(file); |
211 | + MediaSource *media = m_mediaFactory->create(fi); |
212 | + if (media) |
213 | + m_mediaCollection->add(media); |
214 | + } |
215 | +} |
216 | + |
217 | +/*! |
218 | + * \brief GalleryManager::onMediaItemRemoved |
219 | + * \param mediaId |
220 | + */ |
221 | +void GalleryManager::onMediaItemRemoved(qint64 mediaId) |
222 | +{ |
223 | + m_mediaCollection->destroy(mediaId); |
224 | } |
225 | |
226 | === modified file 'src/gallery-manager.h' |
227 | --- src/gallery-manager.h 2013-06-28 20:38:44 +0000 |
228 | +++ src/gallery-manager.h 2013-07-02 13:33:30 +0000 |
229 | @@ -84,6 +84,7 @@ |
230 | |
231 | private slots: |
232 | void onMediaItemAdded(QString file); |
233 | + void onMediaItemRemoved(qint64 mediaId); |
234 | |
235 | private: |
236 | GalleryManager(const GalleryManager&); |
237 | |
238 | === modified file 'src/media-object-factory.cpp' |
239 | --- src/media-object-factory.cpp 2013-06-28 12:33:19 +0000 |
240 | +++ src/media-object-factory.cpp 2013-07-02 13:33:30 +0000 |
241 | @@ -59,6 +59,43 @@ |
242 | } |
243 | |
244 | /*! |
245 | + * \brief MediaObjectFactory::photosFromDB creates a set with all photos and video |
246 | + * stored in the DB. |
247 | + * Someone else needs to take the responsibility to delete all the objects in the set. |
248 | + * You should call clear() afterwards, to remove temporary data. |
249 | + * \return All medias stored in the DB |
250 | + */ |
251 | +QSet<DataObject *> MediaObjectFactory::mediasFromDB() |
252 | +{ |
253 | + Q_ASSERT(m_mediaTable); |
254 | + |
255 | + m_mediasFromDB.clear(); |
256 | + |
257 | + connect(m_mediaTable, |
258 | + SIGNAL(row(qint64,QString,QSize,QDateTime,QDateTime,Orientation,qint64)), |
259 | + this, |
260 | + SLOT(addMedia(qint64,QString,QSize,QDateTime,QDateTime,Orientation,qint64))); |
261 | + |
262 | + m_mediaTable->emitAllRows(); |
263 | + |
264 | + disconnect(m_mediaTable, |
265 | + SIGNAL(row(qint64,QString,QSize,QDateTime,QDateTime,Orientation,qint64)), |
266 | + this, |
267 | + SLOT(addMedia(qint64,QString,QSize,QDateTime,QDateTime,Orientation,qint64))); |
268 | + |
269 | + return m_mediasFromDB; |
270 | +} |
271 | + |
272 | +/*! |
273 | + * \brief MediaObjectFactory::clear |
274 | + */ |
275 | +void MediaObjectFactory::clear() |
276 | +{ |
277 | + clearMetadata(); |
278 | + m_mediasFromDB.clear(); |
279 | +} |
280 | + |
281 | +/*! |
282 | * \brief MediaObjectFactory::create loads the data for a photo or video file. |
283 | * Creates / updates the database as well. |
284 | * \param file the file to load |
285 | @@ -89,13 +126,11 @@ |
286 | |
287 | MediaSource *media = 0; |
288 | Photo *photo = 0; |
289 | - Video *video = 0; |
290 | if (mediaType == MediaSource::Photo) { |
291 | photo = new Photo(file); |
292 | media = photo; |
293 | } else { |
294 | - video = new Video(file); |
295 | - media = video; |
296 | + media = new Video(file); |
297 | } |
298 | |
299 | if (id == INVALID_ID) { |
300 | @@ -129,6 +164,49 @@ |
301 | } |
302 | |
303 | /*! |
304 | + * \brief MediaObjectFactory::addMedia creates a media object, and adds it to the |
305 | + * internal set. This is used for mediasFromDB(). |
306 | + * \param mediaId |
307 | + * \param filename |
308 | + * \param size |
309 | + * \param timestamp |
310 | + * \param exposureTime |
311 | + * \param originalOrientation |
312 | + * \param filesize |
313 | + * \return |
314 | + */ |
315 | +void MediaObjectFactory::addMedia(qint64 mediaId, const QString &filename, |
316 | + const QSize &size, const QDateTime ×tamp, |
317 | + const QDateTime &exposureTime, |
318 | + Orientation originalOrientation, qint64 filesize) |
319 | +{ |
320 | + Q_UNUSED(filesize); |
321 | + |
322 | + QFileInfo file(filename); |
323 | + MediaSource::MediaType mediaType = MediaSource::Photo; |
324 | + if (Video::isCameraVideo(file)) |
325 | + mediaType = MediaSource::Video; |
326 | + |
327 | + MediaSource *media = 0; |
328 | + Photo *photo = 0; |
329 | + if (mediaType == MediaSource::Photo) { |
330 | + photo = new Photo(file); |
331 | + media = photo; |
332 | + } else { |
333 | + media = new Video(file); |
334 | + } |
335 | + |
336 | + media->setSize(size); |
337 | + media->setFileTimestamp(timestamp); |
338 | + media->setExposureDateTime(exposureTime); |
339 | + if (mediaType == MediaSource::Photo) |
340 | + photo->setOriginalOrientation(originalOrientation); |
341 | + media->setId(mediaId); |
342 | + |
343 | + m_mediasFromDB.insert(media); |
344 | +} |
345 | + |
346 | +/*! |
347 | * \brief MediaObjectFactory::clearMetadata resets all memeber variables |
348 | * regarding metadata |
349 | */ |
350 | |
351 | === modified file 'src/media-object-factory.h' |
352 | --- src/media-object-factory.h 2013-06-26 08:59:08 +0000 |
353 | +++ src/media-object-factory.h 2013-07-02 13:33:30 +0000 |
354 | @@ -25,6 +25,7 @@ |
355 | |
356 | #include <QDateTime> |
357 | #include <QFileInfo> |
358 | +#include <QObject> |
359 | #include <QSize> |
360 | |
361 | class MediaTable; |
362 | @@ -32,17 +33,27 @@ |
363 | /*! |
364 | * \brief The MediaObjectFactory creates phot and video objects |
365 | */ |
366 | -class MediaObjectFactory |
367 | +class MediaObjectFactory : public QObject |
368 | { |
369 | + Q_OBJECT |
370 | + |
371 | public: |
372 | explicit MediaObjectFactory(); |
373 | |
374 | void setMediaTable(MediaTable *mediaTable); |
375 | void enableContentLoadFilter(MediaSource::MediaType filterType); |
376 | |
377 | + QSet<DataObject*> mediasFromDB(); |
378 | + void clear(); |
379 | + |
380 | MediaSource *create(const QFileInfo& file); |
381 | |
382 | -private: |
383 | +private slots: |
384 | + void addMedia(qint64 mediaId, const QString& filename, const QSize& size, |
385 | + const QDateTime& timestamp, const QDateTime& exposureTime, |
386 | + Orientation originalOrientation, qint64 filesize); |
387 | + |
388 | +private: |
389 | void clearMetadata(); |
390 | bool readPhotoMetadata(const QFileInfo &file); |
391 | bool readVideoMetadata(const QFileInfo &file); |
392 | @@ -56,6 +67,8 @@ |
393 | |
394 | MediaSource::MediaType m_filterType; |
395 | |
396 | + QSet<DataObject*> m_mediasFromDB; |
397 | + |
398 | friend class tst_MediaObjectFactory; |
399 | }; |
400 | |
401 | |
402 | === modified file 'src/media/media-collection.cpp' |
403 | --- src/media/media-collection.cpp 2013-06-10 07:37:09 +0000 |
404 | +++ src/media/media-collection.cpp 2013-07-02 13:33:30 +0000 |
405 | @@ -137,12 +137,22 @@ |
406 | * \param file_to_load |
407 | * \return |
408 | */ |
409 | -MediaSource *MediaCollection::mediaFromFileinfo(const QFileInfo& file) |
410 | +const MediaSource *MediaCollection::mediaFromFileinfo(const QFileInfo& file) const |
411 | { |
412 | return m_fileMediaMap.value(file.absoluteFilePath(), 0); |
413 | } |
414 | |
415 | /*! |
416 | + * \brief MediaCollection::containsFile |
417 | + * \param filename |
418 | + * \return |
419 | + */ |
420 | +bool MediaCollection::containsFile(const QString &filename) const |
421 | +{ |
422 | + return m_fileMediaMap.contains(filename); |
423 | +} |
424 | + |
425 | +/*! |
426 | * \reimp |
427 | */ |
428 | void MediaCollection::addMany(const QSet<DataObject *> &objects) |
429 | @@ -154,3 +164,24 @@ |
430 | |
431 | DataCollection::addMany(objects); |
432 | } |
433 | + |
434 | +/*! |
435 | + * \brief MediaCollection::destroy |
436 | + * \param media |
437 | + */ |
438 | +void MediaCollection::destroy(MediaSource *media) |
439 | +{ |
440 | + SourceCollection::destroy(media, true, true); |
441 | +} |
442 | + |
443 | +/*! |
444 | + * \brief MediaCollection::remove |
445 | + * \param id |
446 | + */ |
447 | +void MediaCollection::destroy(qint64 id) |
448 | +{ |
449 | + if (m_idMap.contains(id)) { |
450 | + MediaSource *media = qobject_cast<MediaSource*>(m_idMap[id]); |
451 | + SourceCollection::destroy(media, true, true); |
452 | + } |
453 | +} |
454 | |
455 | === modified file 'src/media/media-collection.h' |
456 | --- src/media/media-collection.h 2013-06-10 06:32:08 +0000 |
457 | +++ src/media/media-collection.h 2013-07-02 13:33:30 +0000 |
458 | @@ -44,10 +44,14 @@ |
459 | static bool exposureDateTimeDescendingComparator(DataObject* a, DataObject* b); |
460 | |
461 | MediaSource* mediaForId(qint64 id); |
462 | - MediaSource* mediaFromFileinfo(const QFileInfo &file); |
463 | + const MediaSource* mediaFromFileinfo(const QFileInfo &file) const; |
464 | + bool containsFile(const QString& filename) const; |
465 | |
466 | virtual void addMany(const QSet<DataObject*>& objects); |
467 | |
468 | + void destroy(MediaSource *media); |
469 | + void destroy(qint64 id); |
470 | + |
471 | protected slots: |
472 | virtual void notifyContentsChanged(const QSet<DataObject*>* added, |
473 | const QSet<DataObject*>* removed); |
474 | |
475 | === modified file 'src/media/media-monitor.cpp' |
476 | --- src/media/media-monitor.cpp 2013-06-28 11:53:54 +0000 |
477 | +++ src/media/media-monitor.cpp 2013-07-02 13:33:30 +0000 |
478 | @@ -18,14 +18,17 @@ |
479 | */ |
480 | |
481 | #include "media-monitor.h" |
482 | +#include "media-collection.h" |
483 | +#include "media-source.h" |
484 | |
485 | #include <QDir> |
486 | +#include <QElapsedTimer> |
487 | +#include <QFileInfo> |
488 | #include <QSet> |
489 | #include <QString> |
490 | |
491 | /*! |
492 | * \brief MediaMonitor::MediaMonitor |
493 | - * \param targetDirectory |
494 | */ |
495 | MediaMonitor::MediaMonitor(QObject *parent) |
496 | : QObject(parent), |
497 | @@ -38,6 +41,8 @@ |
498 | |
499 | QObject::connect(m_worker, SIGNAL(mediaItemAdded(QString)), |
500 | this, SIGNAL(mediaItemAdded(QString)), Qt::QueuedConnection); |
501 | + QObject::connect(m_worker, SIGNAL(mediaItemRemoved(qint64)), |
502 | + this, SIGNAL(mediaItemRemoved(qint64)), Qt::QueuedConnection); |
503 | |
504 | m_workerThread.start(QThread::LowPriority); |
505 | } |
506 | @@ -52,7 +57,8 @@ |
507 | } |
508 | |
509 | /*! |
510 | - * \brief MediaMonitor::startMonitoring |
511 | + * \brief MediaMonitor::startMonitoring starts monitoring the given directories |
512 | + * new and delted files |
513 | * \param targetDirectories |
514 | */ |
515 | void MediaMonitor::startMonitoring(const QStringList &targetDirectories) |
516 | @@ -61,10 +67,20 @@ |
517 | Q_ARG(QStringList, targetDirectories)); |
518 | } |
519 | |
520 | +/*! |
521 | + * \brief MediaMonitor::checkConsitency checks the given datastructure, if it is |
522 | + * in sync with the file system (files got added, deleted meanwhile) |
523 | + * \param mediaCollection |
524 | + */ |
525 | +void MediaMonitor::checkConsitency(const MediaCollection *mediaCollection) |
526 | +{ |
527 | + m_worker->setMediaCollection(mediaCollection); |
528 | + QMetaObject::invokeMethod(m_worker, "checkConsitency", Qt::QueuedConnection); |
529 | +} |
530 | + |
531 | |
532 | /*! |
533 | * \brief MediaMonitor::MediaMonitor |
534 | - * \param targetDirectory |
535 | */ |
536 | MediaMonitorWorker::MediaMonitorWorker(QObject *parent) |
537 | : QObject(parent), |
538 | @@ -89,6 +105,15 @@ |
539 | } |
540 | |
541 | /*! |
542 | + * \brief MediaMonitorWorker::setMediaCollection |
543 | + * \param mediaCollection |
544 | + */ |
545 | +void MediaMonitorWorker::setMediaCollection(const MediaCollection *mediaCollection) |
546 | +{ |
547 | + m_mediaCollection = mediaCollection; |
548 | +} |
549 | + |
550 | +/*! |
551 | * \brief MediaMonitor::startMonitoring |
552 | * \param targetDirectories |
553 | */ |
554 | @@ -105,6 +130,16 @@ |
555 | } |
556 | |
557 | /*! |
558 | + * \brief MediaMonitorWorker::checkConsitency |
559 | + * \param mediaCollection |
560 | + */ |
561 | +void MediaMonitorWorker::checkConsitency() |
562 | +{ |
563 | + checkForRemovedMedias(); |
564 | + checkForNewMedias(); |
565 | +} |
566 | + |
567 | +/*! |
568 | * \brief MediaMonitor::onDirectoryEvent |
569 | * \param eventSource |
570 | */ |
571 | @@ -120,9 +155,17 @@ |
572 | { |
573 | QStringList new_manifest = getManifest(m_targetDirectories); |
574 | |
575 | - QStringList difference = subtractManifest(new_manifest, m_manifest); |
576 | - for (int i = 0; i < difference.size(); i++) |
577 | - emit mediaItemAdded(difference.at(i)); |
578 | + QStringList added = subtractManifest(new_manifest, m_manifest); |
579 | + for (int i = 0; i < added.size(); i++) |
580 | + emit mediaItemAdded(added.at(i)); |
581 | + |
582 | + QStringList removed = subtractManifest(m_manifest, new_manifest); |
583 | + for (int i = 0; i < removed.size(); i++) { |
584 | + QFileInfo file(removed.at(i)); |
585 | + const MediaSource *media = m_mediaCollection->mediaFromFileinfo(file); |
586 | + if (media) |
587 | + emit mediaItemRemoved(media->id()); |
588 | + } |
589 | |
590 | m_manifest = new_manifest; |
591 | } |
592 | @@ -158,3 +201,31 @@ |
593 | result.subtract(QSet<QString>::fromList(m2)); |
594 | return QStringList(result.toList()); |
595 | } |
596 | + |
597 | +/*! |
598 | + * \brief MediaMonitorWorker::checkForNewMedias checks for files in the filesystem |
599 | + * that are not in the datastructure |
600 | + * \param mediaCollection |
601 | + */ |
602 | +void MediaMonitorWorker::checkForNewMedias() |
603 | +{ |
604 | + foreach (const QString& file, m_manifest) { |
605 | + if (!m_mediaCollection->containsFile(file)) |
606 | + emit mediaItemAdded(file); |
607 | + } |
608 | +} |
609 | + |
610 | +/*! |
611 | + * \brief MediaMonitorWorker::checkForRemovedMedias checks if there are files in |
612 | + * the datastructure, but not in the file system |
613 | + */ |
614 | +void MediaMonitorWorker::checkForRemovedMedias() |
615 | +{ |
616 | + const QList<DataObject*> medias = m_mediaCollection->getAll(); |
617 | + foreach (const DataObject* obj, medias) { |
618 | + const MediaSource *media = qobject_cast<const MediaSource*>(obj); |
619 | + Q_ASSERT(media); |
620 | + if (!m_manifest.contains(media->file().absoluteFilePath())) |
621 | + emit mediaItemRemoved(media->id()); |
622 | + } |
623 | +} |
624 | |
625 | === modified file 'src/media/media-monitor.h' |
626 | --- src/media/media-monitor.h 2013-06-28 11:53:54 +0000 |
627 | +++ src/media/media-monitor.h 2013-07-02 13:33:30 +0000 |
628 | @@ -26,11 +26,13 @@ |
629 | #include <QThread> |
630 | #include <QTimer> |
631 | |
632 | +class MediaCollection; |
633 | class MediaMonitorWorker; |
634 | |
635 | /*! |
636 | - * \brief The MediaMonitor class monitor directories for added files |
637 | - * All it's time consuming tasks are automaticly run in a separate thread |
638 | + * \brief The MediaMonitor class monitor directories for added files. And does a |
639 | + * check if the files in the datastructure and on thef ile system are in sync. |
640 | + * All this is done in an extra thread. |
641 | */ |
642 | class MediaMonitor : public QObject |
643 | { |
644 | @@ -41,9 +43,11 @@ |
645 | virtual ~MediaMonitor(); |
646 | |
647 | void startMonitoring(const QStringList& targetDirectories); |
648 | + void checkConsitency(const MediaCollection *mediaCollection); |
649 | |
650 | signals: |
651 | void mediaItemAdded(QString newItem); |
652 | + void mediaItemRemoved(qint64 mediaId); |
653 | |
654 | private: |
655 | MediaMonitorWorker* m_worker; |
656 | @@ -63,25 +67,31 @@ |
657 | MediaMonitorWorker(QObject *parent=0); |
658 | virtual ~MediaMonitorWorker(); |
659 | |
660 | + void setMediaCollection(const MediaCollection *mediaCollection); |
661 | + |
662 | public slots: |
663 | void startMonitoring(const QStringList& targetDirectories); |
664 | + void checkConsitency(); |
665 | |
666 | signals: |
667 | void mediaItemAdded(QString newItem); |
668 | + void mediaItemRemoved(qint64 mediaId); |
669 | |
670 | private slots: |
671 | void onDirectoryEvent(const QString& eventSource); |
672 | void onFileActivityCeased(); |
673 | |
674 | private: |
675 | - static QStringList getManifest(const QStringList& dirs); |
676 | - static QStringList subtractManifest(const QStringList& m1, |
677 | - const QStringList& m2); |
678 | + QStringList getManifest(const QStringList& dirs); |
679 | + QStringList subtractManifest(const QStringList& m1, const QStringList& m2); |
680 | + void checkForNewMedias(); |
681 | + void checkForRemovedMedias(); |
682 | |
683 | QStringList m_targetDirectories; |
684 | QFileSystemWatcher m_watcher; |
685 | QStringList m_manifest; |
686 | QTimer m_fileActivityTimer; |
687 | + const MediaCollection *m_mediaCollection; |
688 | }; |
689 | |
690 | #endif // GALLERY_MEDIA_MONITOR_H_ |
691 | |
692 | === modified file 'src/photo/photo-caches.cpp' |
693 | --- src/photo/photo-caches.cpp 2013-06-10 07:59:26 +0000 |
694 | +++ src/photo/photo-caches.cpp 2013-07-02 13:33:30 +0000 |
695 | @@ -102,7 +102,7 @@ |
696 | |
697 | m_file.dir().mkdir(ORIGINAL_DIR); |
698 | |
699 | - return rename(m_file, m_originalFile); |
700 | + return copy(m_file, m_originalFile); |
701 | } |
702 | |
703 | /*! |
704 | |
705 | === modified file 'src/qml/qml-media-collection-model.cpp' |
706 | --- src/qml/qml-media-collection-model.cpp 2013-06-11 19:44:28 +0000 |
707 | +++ src/qml/qml-media-collection-model.cpp 2013-07-02 13:33:30 +0000 |
708 | @@ -93,7 +93,7 @@ |
709 | MediaSource* media = VariantToObject<MediaSource*>(vmedia); |
710 | |
711 | if (media != NULL) |
712 | - GalleryManager::instance()->mediaCollection()->destroy(media, true, true); |
713 | + GalleryManager::instance()->mediaCollection()->destroy(media); |
714 | } |
715 | |
716 | /*! |
717 | |
718 | === modified file 'tests/autopilot/gallery_app/emulators/gallery_utils.py' |
719 | --- tests/autopilot/gallery_app/emulators/gallery_utils.py 2013-06-21 16:00:49 +0000 |
720 | +++ tests/autopilot/gallery_app/emulators/gallery_utils.py 2013-07-02 13:33:30 +0000 |
721 | @@ -119,12 +119,7 @@ |
722 | def get_first_image_in_event_view(self): |
723 | """Returns the first photo of the gallery.""" |
724 | event = self.get_first_event() |
725 | - list_view = event.get_children_by_type("QQuickListView")[0] |
726 | - item = list_view.get_children_by_type("QQuickItem")[0] |
727 | - first_delegate = item.get_children_by_type("QQuickItem", |
728 | - objectName="eventPhoto")[0] |
729 | - first_photo = first_delegate.get_children_by_type("UbuntuShape")[0] |
730 | - return first_photo |
731 | + return event.select_many("OrganicItemInteraction")[1] |
732 | |
733 | def get_all_albums(self): |
734 | """Returns all albums in the albums view""" |
735 | |
736 | === modified file 'tests/autopilot/gallery_app/tests/test_events_view.py' |
737 | --- tests/autopilot/gallery_app/tests/test_events_view.py 2013-06-26 09:02:33 +0000 |
738 | +++ tests/autopilot/gallery_app/tests/test_events_view.py 2013-07-02 13:33:30 +0000 |
739 | @@ -47,6 +47,11 @@ |
740 | first_photo = self.events_view.get_first_image_in_event_view() |
741 | self.click_item(first_photo) |
742 | |
743 | + def get_delete_dialog(self): |
744 | + delete_dialog = self.gallery_utils.get_delete_dialog() |
745 | + self.assertThat(delete_dialog.opacity, Eventually(Equals(1))) |
746 | + return delete_dialog |
747 | + |
748 | def click_delete_action(self): |
749 | trash_button = self.events_view.get_toolbar_delete_button() |
750 | self.click_item(trash_button) |
751 | @@ -69,8 +74,7 @@ |
752 | self.click_first_photo() |
753 | self.click_delete_action() |
754 | |
755 | - delete_dialog = self.events_view.get_delete_dialog() |
756 | - self.assertThat(delete_dialog.visible, Eventually(Equals(True))) |
757 | + delete_dialog = self.get_delete_dialog() |
758 | |
759 | cancel_item = self.events_view.get_delete_dialog_cancel_button() |
760 | self.click_item(cancel_item) |
761 | @@ -83,8 +87,7 @@ |
762 | |
763 | self.click_delete_action() |
764 | |
765 | - delete_dialog = self.events_view.get_delete_dialog() |
766 | - self.assertThat(delete_dialog.visible, Eventually(Equals(True))) |
767 | + delete_dialog = self.get_delete_dialog() |
768 | |
769 | delete_item = self.events_view.get_delete_dialog_delete_button() |
770 | self.click_item(delete_item) |
771 | |
772 | === modified file 'tests/autopilot/gallery_app/tests/test_photo_viewer.py' |
773 | --- tests/autopilot/gallery_app/tests/test_photo_viewer.py 2013-06-26 09:02:33 +0000 |
774 | +++ tests/autopilot/gallery_app/tests/test_photo_viewer.py 2013-07-02 13:33:30 +0000 |
775 | @@ -52,6 +52,11 @@ |
776 | def setUp(self): |
777 | super(TestPhotoViewer, self).setUp() |
778 | |
779 | + def get_delete_dialog(self): |
780 | + delete_dialog = self.photo_viewer.get_delete_dialog() |
781 | + self.assertThat(delete_dialog.opacity, Eventually(Equals(1))) |
782 | + return delete_dialog |
783 | + |
784 | def test_nav_bar_back_button(self): |
785 | """Clicking the back button must close the photo.""" |
786 | photo_viewer = self.photo_viewer.get_main_photo_viewer() |
787 | @@ -68,8 +73,7 @@ |
788 | self.pointing_device.move_to_object(trash_button) |
789 | self.pointing_device.click() |
790 | |
791 | - delete_dialog = self.photo_viewer.get_delete_dialog() |
792 | - self.assertThat(delete_dialog.visible, Eventually(Equals(True))) |
793 | + delete_dialog = self.get_delete_dialog() |
794 | |
795 | cancel_item = self.photo_viewer.get_delete_popover_cancel_item() |
796 | self.click_item(cancel_item) |
797 | @@ -82,8 +86,7 @@ |
798 | self.pointing_device.move_to_object(trash_button) |
799 | self.pointing_device.click() |
800 | |
801 | - delete_dialog = self.photo_viewer.get_delete_dialog() |
802 | - self.assertThat(delete_dialog.visible, Eventually(Equals(True))) |
803 | + delete_dialog = self.get_delete_dialog() |
804 | |
805 | delete_item = self.photo_viewer.get_delete_popover_delete_item() |
806 | self.click_item(delete_item) |
807 | @@ -94,8 +97,7 @@ |
808 | self.reveal_toolbar() |
809 | self.pointing_device.click_object(trash_button) |
810 | |
811 | - delete_dialog = self.photo_viewer.get_delete_dialog() |
812 | - self.assertThat(delete_dialog.visible, Eventually(Equals(True))) |
813 | + delete_dialog = self.get_delete_dialog() |
814 | |
815 | delete_item = self.photo_viewer.get_delete_popover_delete_item() |
816 | self.click_item(delete_item) |
817 | |
818 | === modified file 'tests/autopilot/gallery_app/tests/test_photos_view.py' |
819 | --- tests/autopilot/gallery_app/tests/test_photos_view.py 2013-06-26 08:59:08 +0000 |
820 | +++ tests/autopilot/gallery_app/tests/test_photos_view.py 2013-07-02 13:33:30 +0000 |
821 | @@ -60,6 +60,11 @@ |
822 | trash_button = self.photos_view.get_toolbar_delete_button() |
823 | self.click_item(trash_button) |
824 | |
825 | + def get_delete_dialog(self): |
826 | + delete_dialog = self.gallery_utils.get_delete_dialog() |
827 | + self.assertThat(delete_dialog.opacity, Eventually(Equals(1))) |
828 | + return delete_dialog |
829 | + |
830 | def test_open_photo(self): |
831 | self.click_first_photo() |
832 | photo_viewer = self.photos_view.get_main_photo_viewer() |
833 | @@ -83,8 +88,7 @@ |
834 | self.click_first_photo() |
835 | self.click_delete_action() |
836 | |
837 | - delete_dialog = self.photos_view.get_delete_dialog() |
838 | - self.assertThat(delete_dialog.opacity, Eventually(Equals(1))) |
839 | + delete_dialog = self.get_delete_dialog() |
840 | |
841 | cancel_item = self.photos_view.get_delete_dialog_cancel_button() |
842 | self.click_item(cancel_item) |
843 | @@ -97,8 +101,7 @@ |
844 | |
845 | self.click_delete_action() |
846 | |
847 | - delete_dialog = self.photos_view.get_delete_dialog() |
848 | - self.assertThat(delete_dialog.opacity, Eventually(Equals(1))) |
849 | + delete_dialog = self.get_delete_dialog() |
850 | |
851 | delete_item = self.photos_view.get_delete_dialog_delete_button() |
852 | self.click_item(delete_item) |
853 | |
854 | === modified file 'tests/unittests/mediaobjectfactory/tst_mediaobjectfactory.cpp' |
855 | --- tests/unittests/mediaobjectfactory/tst_mediaobjectfactory.cpp 2013-06-28 12:33:19 +0000 |
856 | +++ tests/unittests/mediaobjectfactory/tst_mediaobjectfactory.cpp 2013-07-02 13:33:30 +0000 |
857 | @@ -41,6 +41,7 @@ |
858 | void readPhotoMetadata(); |
859 | void readVideoMetadata(); |
860 | void enableContentLoadFilter(); |
861 | + void addMedia(); |
862 | |
863 | private: |
864 | MediaTable *m_mediaTable; |
865 | @@ -143,6 +144,34 @@ |
866 | QVERIFY(media == 0); |
867 | } |
868 | |
869 | +void tst_MediaObjectFactory::addMedia() |
870 | +{ |
871 | + qint64 id = 123; |
872 | + QString filename("/some/photo.jpg"); |
873 | + QSize size(320, 200); |
874 | + QDateTime timestamp(QDate(2013, 02, 03), QTime(12, 12, 12)); |
875 | + QDateTime exposureTime(QDate(2013, 03, 04), QTime(1, 2, 3)); |
876 | + Orientation originalOrientation(BOTTOM_RIGHT_ORIGIN); |
877 | + qint64 filesize = 2048; |
878 | + |
879 | + m_factory->addMedia(id, filename, size, timestamp, |
880 | + exposureTime, originalOrientation, |
881 | + filesize); |
882 | + |
883 | + QCOMPARE(m_factory->m_mediasFromDB.size(), 1); |
884 | + QSet<DataObject*>::iterator it; |
885 | + it = m_factory->m_mediasFromDB.begin(); |
886 | + DataObject *obj = *it; |
887 | + Photo *photo = qobject_cast<Photo*>(obj); |
888 | + QVERIFY(photo != 0); |
889 | + QCOMPARE(photo->id(), id); |
890 | + QCOMPARE(photo->path().toLocalFile(), filename); |
891 | + QCOMPARE(photo->size(), size); |
892 | + QCOMPARE(photo->exposureDateTime(), exposureTime); |
893 | + QCOMPARE(photo->fileTimestamp(), timestamp); |
894 | + QCOMPARE(photo->orientation(), originalOrientation); |
895 | +} |
896 | + |
897 | QTEST_MAIN(tst_MediaObjectFactory); |
898 | |
899 | #include "tst_mediaobjectfactory.moc" |
900 | |
901 | === modified file 'tests/unittests/stubs/gallery-manager_stub.cpp' |
902 | --- tests/unittests/stubs/gallery-manager_stub.cpp 2013-06-28 20:38:44 +0000 |
903 | +++ tests/unittests/stubs/gallery-manager_stub.cpp 2013-07-02 13:33:30 +0000 |
904 | @@ -119,3 +119,8 @@ |
905 | { |
906 | Q_UNUSED(file); |
907 | } |
908 | + |
909 | +void GalleryManager::onMediaItemRemoved(qint64 mediaId) |
910 | +{ |
911 | + Q_UNUSED(mediaId); |
912 | +} |
913 | |
914 | === modified file 'tests/unittests/stubs/media-table_stub.cpp' |
915 | --- tests/unittests/stubs/media-table_stub.cpp 2013-06-28 12:33:19 +0000 |
916 | +++ tests/unittests/stubs/media-table_stub.cpp 2013-07-02 13:33:30 +0000 |
917 | @@ -51,10 +51,6 @@ |
918 | mediaFakeTable.clear(); |
919 | } |
920 | |
921 | -void MediaTable::verifyFiles() |
922 | -{ |
923 | -} |
924 | - |
925 | qint64 MediaTable::getIdForMedia(const QString& filename) |
926 | { |
927 | foreach (const MediaDataRow &row, mediaFakeTable) { |
928 | @@ -109,6 +105,10 @@ |
929 | { |
930 | } |
931 | |
932 | +void MediaTable::emitAllRows() |
933 | +{ |
934 | +} |
935 | + |
936 | void MediaTable::getRow(qint64 mediaId, QSize& size, Orientation& |
937 | originalOrientation, QDateTime& fileTimestamp, QDateTime& exposureDateTime) |
938 | { |
FAILED: Continuous integration, rev:763 jenkins. qa.ubuntu. com/job/ gallery- app-ci/ 331/ jenkins. qa.ubuntu. com/job/ gallery- app-saucy- amd64-ci/ 141 jenkins. qa.ubuntu. com/job/ gallery- app-saucy- armhf-ci/ 141 jenkins. qa.ubuntu. com/job/ gallery- app-saucy- armhf-ci/ 141/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ gallery- app-saucy- i386-ci/ 141 jenkins. qa.ubuntu. com/job/ generic- mediumtests- saucy/519 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- saucy/521 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- saucy/521/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ generic- mediumtests- runner- saucy/470
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ gallery- app-ci/ 331/rebuild
http://