Merge lp:~jamesh/gallery-app/thumbnail-mtime into lp:gallery-app

Proposed by James Henstridge on 2015-08-17
Status: Merged
Approved by: Arthur Mello on 2015-12-10
Approved revision: 1236
Merged at revision: 1257
Proposed branch: lp:~jamesh/gallery-app/thumbnail-mtime
Merge into: lp:gallery-app
Diff against target: 176 lines (+30/-39)
6 files modified
rc/qml/AlbumViewer/AlbumInternals/FramePortrait.qml (+2/-18)
rc/qml/Components/MediaGrid.qml (+1/-10)
rc/qml/MediaViewer/MediaViewer.qml (+4/-1)
rc/qml/OrganicView/OrganicMediaList.qml (+1/-10)
src/media/media-source.cpp (+18/-0)
src/media/media-source.h (+4/-0)
To merge this branch: bzr merge lp:~jamesh/gallery-app/thumbnail-mtime
Reviewer Review Type Date Requested Status
Arthur Mello (community) 2015-08-17 Approve on 2015-12-10
PS Jenkins bot continuous-integration Needs Fixing on 2015-12-01
Review via email: mp+268185@code.launchpad.net

Commit Message

Use the file's last modified date for the "at" parameter when invoking the thumbnailer to avoid needlessly reloading thumbnails.

Description of the Change

Currently gallery-app appends an "?at=" parameter to the image://thumbnailer/ URI in order to work around the fact that QML's image cache isn't invalidated when the underlying file is changed.

At the moment it sets this parameter to Date.now(), which achieves this goal but also results in the thumbnail being reloaded when the file _hasn't_ been changed. What we really want is something that will change when the files contents change. The file's mtime is an obvious candidate here, so that's what I've done here:

1. Add a "lastModified" property to the MediaSource object.

2. set the "at" parameter to mediaSource.lastModified instead of Date.now()

3. Remove the onDataChanged signal connections in the QML, since I've set the dataChanged signal as the notifier for the lastModified property.

To post a comment you must log in.
Arthur Mello (artmello) wrote :

Thanks a lot for looking on that. I found one functional issue when testing it on krillin (r99):

1. Open one photo from "Events" tab;
2. Start editing it and crop the image;
3. Return to the opened photo and confirm it was cropped;
4. Return to the "Events" tab.

Expected result:
The thumbnail is updated with the new cropped image.

Current result:
The thumbnail is not updated.

See that I am not moving the event list and so the thumbnail keeps in the visible area all the time. If I move the event list in a way that the thumbnail leaves the visible area it is correctly refreshed when I return to it.

review: Needs Fixing
1235. By James Henstridge on 2015-08-18

Refresh the FileInfo before emitting dataChanged() so the new
modification time is seen.

James Henstridge (jamesh) wrote :

I think I've got this working now. I've added a refresh() call that clears the cache of the QFileInfo embedded in the MediaSource object, and called it prior to emitting the signal.

Arthur Mello (artmello) wrote :

Sorry for the long time without a review. Once again, thx for your work on this. It is definitely a improve from the previous review. But I am seeing on issue while testing:

Steps:
1. Take a couple of photos with camera-app;
2. Open gallery-app and confirm that taken photos are visible;
3. Click one photo to start navigating between them;
4. Edit one of the photos;
5. Return (top left arrow) to exit editing view;

Expected result:
Return to photo viewer displaying the result of the edited photo

Actual result:
Return to photo viewer displaying another photo of the collection. If I navigate back I can see that the edited photo was correctly modified

review: Needs Fixing
1236. By James Henstridge on 2015-11-27

Merge from trunk

James Henstridge (jamesh) wrote :

Sorry for not getting back to you on this. I did manage to reproduce your finding, but it wasn't consistent: sometimes it would advance to the next photo and other times it wasn't. So that sounds like a race condition.

I tried instrumenting the QML side code and don't see the view index being changed from that side.

At this point, my guess is that you're seeing a pre-existing race condition. I see that the code is using QFileSystemWatcher to track file system changes, and this feeds into the QAbstractListModel. If an item is being removed then added, this could presumably cause the view index to move forward (i.e. row 2 deleted, causing view to point at new row 2. New row inserted between rows 1 and 2 causes index to move forward to 3).

My patch doesn't even touch this code, so I don't know how it could be responsible for this.

Arthur Mello (artmello) wrote :

Issue mentioned above does happen on gallery trunk and is probably not related with this MR, bug #1524973 was created to track this issue.
Thanks a lot for looking at this.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'rc/qml/AlbumViewer/AlbumInternals/FramePortrait.qml'
2--- rc/qml/AlbumViewer/AlbumInternals/FramePortrait.qml 2015-11-05 18:10:29 +0000
3+++ rc/qml/AlbumViewer/AlbumInternals/FramePortrait.qml 2015-12-01 02:38:05 +0000
4@@ -44,25 +44,9 @@
5 anchors.fill: parent
6 asynchronous: true
7 visible: fullImage.opacity < 1
8- source: load && mediaSource ? "image://thumbnailer/" + mediaSource.path + "?at=" + Date.now() : ""
9+ source: load && mediaSource ? "image://thumbnailer/" + mediaSource.path + "?at=" + mediaSource.lastModified : ""
10 fillMode: fullImage.fillMode
11 sourceSize.width: 256
12-
13- Connections {
14- target: mediaSource ? mediaSource : null
15- onDataChanged: {
16- // data changed but filename didn't, so we need to bypass the qml image
17- // cache by tacking a timestamp to the filename so sees it as different.
18- preview.source = "image://thumbnailer/" + mediaSource.path + "?at=" + Date.now()
19-
20- // reload full image
21- var src = fullImage.source;
22- fullImage.asynchronous = false;
23- fullImage.source = "";
24- fullImage.asynchronous = true;
25- fullImage.source = src;
26- }
27- }
28 }
29 Image {
30 id: fullImage
31@@ -71,7 +55,7 @@
32 cache: false
33 fillMode: Image.PreserveAspectCrop
34 source: (preview.status === Image.Ready && !isPreview) ?
35- "image://thumbnailer/" + mediaSource.path : ""
36+ "image://thumbnailer/" + mediaSource.path + "?at=" + mediaSource.lastModified : ""
37
38 property int maxSize: Math.max(width, height)
39 sourceSize.width: maxSize
40
41=== modified file 'rc/qml/Components/MediaGrid.qml'
42--- rc/qml/Components/MediaGrid.qml 2015-11-05 18:10:29 +0000
43+++ rc/qml/Components/MediaGrid.qml 2015-12-01 02:38:05 +0000
44@@ -85,22 +85,13 @@
45 sourceFillMode: UbuntuShape.PreserveAspectCrop
46 source: Image {
47 id: thumbImage
48- source: "image://thumbnailer/" + mediaSource.path + "?at=" + Date.now()
49+ source: "image://thumbnailer/" + mediaSource.path + "?at=" + mediaSource.lastModified
50 asynchronous: true
51 fillMode: Image.PreserveAspectCrop
52 sourceSize {
53 width: photosGrid.thumbnailSize
54 height: photosGrid.thumbnailSize
55 }
56-
57- Connections {
58- target: mediaSource
59- onDataChanged: {
60- // data changed but filename didn't, so we need to bypass the qml image
61- // cache by tacking a timestamp to the filename so sees it as different.
62- thumbImage.source = "image://thumbnailer/" + mediaSource.path + "?at=" + Date.now()
63- }
64- }
65 }
66
67 Image {
68
69=== modified file 'rc/qml/MediaViewer/MediaViewer.qml'
70--- rc/qml/MediaViewer/MediaViewer.qml 2015-11-05 18:10:29 +0000
71+++ rc/qml/MediaViewer/MediaViewer.qml 2015-12-01 02:38:05 +0000
72@@ -332,7 +332,10 @@
73 editor = overview.pushPage(Qt.resolvedUrl("GalleryPhotoEditorPage.qml"), { photo: path });
74 }
75 editor.done.connect(function(photoWasModified) {
76- if (photoWasModified) galleryPhotoViewer.media.dataChanged();
77+ if (photoWasModified) {
78+ galleryPhotoViewer.media.refresh();
79+ galleryPhotoViewer.media.dataChanged();
80+ }
81 overview.popPage();
82 });
83 }
84
85=== modified file 'rc/qml/OrganicView/OrganicMediaList.qml'
86--- rc/qml/OrganicView/OrganicMediaList.qml 2015-11-06 14:05:24 +0000
87+++ rc/qml/OrganicView/OrganicMediaList.qml 2015-12-01 02:38:05 +0000
88@@ -174,7 +174,7 @@
89 sourceFillMode: UbuntuShape.PreserveAspectCrop
90 source: Image {
91 id: thumbImage
92- source: "image://thumbnailer/" + model.mediaSource.path + "?at=" + Date.now()
93+ source: "image://thumbnailer/" + model.mediaSource.path + "?at=" + model.mediaSource.lastModified
94 asynchronous: true
95
96 /* The SDK thumbnailer respects the freedesktop.org standard and uses 128 for the small
97@@ -188,15 +188,6 @@
98 fillMode: Image.PreserveAspectCrop
99 }
100
101- Connections {
102- target: model && model.mediaSource ? model.mediaSource : null
103- onDataChanged: {
104- // data changed but filename didn't, so we need to bypass the qml image
105- // cache by tacking a timestamp to the filename so sees it as different.
106- thumbImage.source = "image://thumbnailer/" + model.mediaSource.path + "?at=" + Date.now()
107- }
108- }
109-
110 Image {
111 // Display a play icon if the thumbnail is from a video
112 source: "../../img/icon_play.png"
113
114=== modified file 'src/media/media-source.cpp'
115--- src/media/media-source.cpp 2014-11-30 18:44:05 +0000
116+++ src/media/media-source.cpp 2015-12-01 02:38:05 +0000
117@@ -77,6 +77,15 @@
118 }
119
120 /*!
121+ * \brief MediaSource::lastModified
122+ * \return
123+ */
124+qint64 MediaSource::lastModified() const
125+{
126+ return m_file.lastModified().toMSecsSinceEpoch();
127+}
128+
129+/*!
130 * \brief MediaSource::Image
131 * \param respectOrientation
132 * \return
133@@ -242,6 +251,15 @@
134 }
135
136 /*!
137+ * \brief MediaSource::refresh
138+ * \return
139+ */
140+void MediaSource::refresh()
141+{
142+ m_file.refresh();
143+}
144+
145+/*!
146 * \brief MediaSource::set_busy
147 * \param busy
148 */
149
150=== modified file 'src/media/media-source.h'
151--- src/media/media-source.h 2014-11-30 18:44:05 +0000
152+++ src/media/media-source.h 2015-12-01 02:38:05 +0000
153@@ -47,6 +47,7 @@
154 Q_OBJECT
155 Q_PROPERTY(MediaType type READ type NOTIFY typeChanged)
156 Q_PROPERTY(QUrl path READ path NOTIFY pathChanged)
157+ Q_PROPERTY(qint64 lastModified READ lastModified NOTIFY dataChanged)
158 Q_PROPERTY(int orientation READ orientation NOTIFY orientationChanged)
159 Q_PROPERTY(QDate exposureDate READ exposureDate NOTIFY exposureDateTimeChanged)
160 Q_PROPERTY(QTime exposureTimeOfDay READ exposureTimeOfDay NOTIFY exposureDateTimeChanged)
161@@ -79,6 +80,7 @@
162
163 QFileInfo file() const;
164 QUrl path() const;
165+ qint64 lastModified() const;
166
167 virtual QImage image(bool respectOrientation = true, const QSize &scaleSize=QSize());
168 virtual Orientation orientation() const;
169@@ -101,6 +103,8 @@
170
171 void setMediaTable(MediaTable *mediaTable);
172
173+ Q_INVOKABLE void refresh();
174+
175 public Q_SLOTS:
176 void setSize(const QSize& size);
177

Subscribers

People subscribed via source and target branches