diff --git a/srcpkgs/gnome-photos/patches/tracker3.patch b/srcpkgs/gnome-photos/patches/tracker3.patch new file mode 100644 index 0000000000..406dca30fd --- /dev/null +++ b/srcpkgs/gnome-photos/patches/tracker3.patch @@ -0,0 +1,4254 @@ +From b4a8de1ef79c94c1c11b730787108f305c962e38 Mon Sep 17 00:00:00 2001 +From: Sam Thursfield +Date: Wed, 26 Aug 2020 01:57:46 +0200 +Subject: [PATCH 1/4] photos-tracker-controller: Label unit of timing + measurements + +Otherwise, it's not clear if these are timings or some kind of ID +number. +--- + src/photos-tracker-controller.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/photos-tracker-controller.c b/src/photos-tracker-controller.c +index a02db077..3a62cf2d 100644 +--- a/src/photos-tracker-controller.c ++++ b/src/photos-tracker-controller.c +@@ -237,7 +237,7 @@ photos_tracker_controller_cursor_next (GObject *source_object, GAsyncResult *res + } + + now = g_get_monotonic_time (); +- photos_debug (PHOTOS_DEBUG_TRACKER, "Query Cursor: %" G_GINT64_FORMAT, (now - priv->last_query_time) / 1000000); ++ photos_debug (PHOTOS_DEBUG_TRACKER, "Query Cursor: %" G_GINT64_FORMAT " seconds", (now - priv->last_query_time) / 1000000); + + photos_item_manager_add_item_for_mode (PHOTOS_ITEM_MANAGER (priv->item_mngr), + PHOTOS_TRACKER_CONTROLLER_GET_CLASS (self)->base_item_type, +@@ -346,7 +346,7 @@ photos_tracker_controller_set_query_status (PhotosTrackerController *self, gbool + else + { + photos_debug (PHOTOS_DEBUG_TRACKER, +- "Query Elapsed: %" G_GINT64_FORMAT, ++ "Query Elapsed: %" G_GINT64_FORMAT " seconds", + (now - priv->last_query_time) / 1000000); + priv->last_query_time = 0; + } +-- +GitLab + + +From 7c98a884ba6a26795ea25a8780002ac101036887 Mon Sep 17 00:00:00 2001 +From: Sam Thursfield +Date: Wed, 27 May 2020 13:07:58 +0200 +Subject: [PATCH 2/4] Fix build failure due to undefined M_PI constant + +The header needs to be included. + +Previously I suppose libtracker-sparql.h pulled this in. +--- + src/photos-utils.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/photos-utils.c b/src/photos-utils.c +index 401f52ba..c638297b 100644 +--- a/src/photos-utils.c ++++ b/src/photos-utils.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + + #include "photos-application.h" + #include "photos-device-item.h" +-- +GitLab + + +From 0e4bd7bbc8056743a8ea07ab3713ad543c00d478 Mon Sep 17 00:00:00 2001 +From: Sam Thursfield +Date: Sun, 7 Jun 2020 21:53:20 +0200 +Subject: [PATCH 3/4] Generate queries using SPARQL templates + +The code to generate SPARQL queries was split across many different +source files, making it difficult to make big changes. Now the +queries are written out as templates and we use template substitution +to build the SPARQL that we send to Tracker. +--- + src/meson.build | 5 + + src/photos-base-manager.c | 15 ++ + src/photos-base-manager.h | 4 + + src/photos-filterable.c | 8 - + src/photos-filterable.h | 3 - + src/photos-query-builder.c | 207 +++++++++----------- + src/photos-search-type-manager.c | 59 +----- + src/photos-search-type.c | 67 +++---- + src/photos-search-type.h | 6 +- + src/photos-sparql-template.c | 187 ++++++++++++++++++ + src/photos-sparql-template.h | 38 ++++ + src/photos.gresource.xml | 4 + + src/queries/all.sparql.template | 31 +++ + src/queries/collections.sparql.template | 14 ++ + src/queries/favorite-photos.sparql.template | 12 ++ + src/queries/photos.sparql.template | 11 ++ + 16 files changed, 446 insertions(+), 225 deletions(-) + create mode 100644 src/photos-sparql-template.c + create mode 100644 src/photos-sparql-template.h + create mode 100644 src/queries/all.sparql.template + create mode 100644 src/queries/collections.sparql.template + create mode 100644 src/queries/favorite-photos.sparql.template + create mode 100644 src/queries/photos.sparql.template + +diff --git a/src/meson.build b/src/meson.build +index 9919f0cf..b5b2759c 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -195,6 +195,7 @@ sources = common_sources + files( + 'photos-source.c', + 'photos-source-manager.c', + 'photos-source-notification.c', ++ 'photos-sparql-template.c', + 'photos-spinner-box.c', + 'photos-thumbnail-factory.c', + 'photos-tool.c', +@@ -290,6 +291,10 @@ resource_data = files( + 'photos-selection-toolbar.ui', + 'photos-share-dialog.ui', + 'photos-zoom-controls.ui', ++ 'queries/all.sparql.template', ++ 'queries/collections.sparql.template', ++ 'queries/favorite-photos.sparql.template', ++ 'queries/photos.sparql.template', + ) + + sources += gnome.compile_resources( +diff --git a/src/photos-base-manager.c b/src/photos-base-manager.c +index d49d19a5..052638d5 100644 +--- a/src/photos-base-manager.c ++++ b/src/photos-base-manager.c +@@ -250,6 +250,12 @@ photos_base_manager_default_get_where (PhotosBaseManager *self, gint flags) + } + + ++static PhotosSparqlTemplate * ++photos_base_manager_default_get_sparql_template (PhotosBaseManager *self, gint flags) ++{ ++ return NULL; ++} ++ + static void + photos_base_manager_default_remove_object_by_id (PhotosBaseManager *self, const gchar *id) + { +@@ -438,6 +444,7 @@ photos_base_manager_class_init (PhotosBaseManagerClass *class) + class->get_object_by_id = photos_base_manager_default_get_object_by_id; + class->get_previous_object = photos_base_manager_default_get_previous_object; + class->get_where = photos_base_manager_default_get_where; ++ class->get_sparql_template = photos_base_manager_default_get_sparql_template; + class->remove_object_by_id = photos_base_manager_default_remove_object_by_id; + class->set_active_object = photos_base_manager_default_set_active_object; + +@@ -714,6 +721,14 @@ photos_base_manager_get_where (PhotosBaseManager *self, gint flags) + } + + ++PhotosSparqlTemplate * ++photos_base_manager_get_sparql_template (PhotosBaseManager *self, gint flags) ++{ ++ g_return_val_if_fail (PHOTOS_IS_BASE_MANAGER (self), NULL); ++ return PHOTOS_BASE_MANAGER_GET_CLASS (self)->get_sparql_template (self, flags); ++} ++ ++ + void + photos_base_manager_process_new_objects (PhotosBaseManager *self, GHashTable *new_objects) + { +diff --git a/src/photos-base-manager.h b/src/photos-base-manager.h +index 99d203e9..04cfa7db 100644 +--- a/src/photos-base-manager.h ++++ b/src/photos-base-manager.h +@@ -24,6 +24,7 @@ + #define PHOTOS_BASE_MANAGER_H + + #include ++#include "photos-sparql-template.h" + + G_BEGIN_DECLS + +@@ -46,6 +47,7 @@ struct _PhotosBaseManagerClass + gchar *(*get_where) (PhotosBaseManager *self, gint flags); + void (*remove_object_by_id) (PhotosBaseManager *self, const gchar *id); + gboolean (*set_active_object) (PhotosBaseManager *self, GObject *object); ++ PhotosSparqlTemplate *(*get_sparql_template) (PhotosBaseManager *self, gint flags); + + /* signals */ + void (*active_changed) (PhotosBaseManager *self, GObject *object); +@@ -80,6 +82,8 @@ const gchar *photos_base_manager_get_title (PhotosBaseMana + + gchar *photos_base_manager_get_where (PhotosBaseManager *self, gint flags); + ++PhotosSparqlTemplate *photos_base_manager_get_sparql_template (PhotosBaseManager *self, gint flags); ++ + void photos_base_manager_process_new_objects (PhotosBaseManager *self, GHashTable *new_objects); + + void photos_base_manager_remove_object (PhotosBaseManager *self, GObject *object); +diff --git a/src/photos-filterable.c b/src/photos-filterable.c +index 361b1c29..aaa4109c 100644 +--- a/src/photos-filterable.c ++++ b/src/photos-filterable.c +@@ -69,14 +69,6 @@ photos_filterable_get_id (PhotosFilterable *self) + } + + +-gchar * +-photos_filterable_get_where (PhotosFilterable *self) +-{ +- g_return_val_if_fail (PHOTOS_IS_FILTERABLE (self), NULL); +- return PHOTOS_FILTERABLE_GET_IFACE (self)->get_where (self); +-} +- +- + gboolean + photos_filterable_is_search_criterion (PhotosFilterable *self) + { +diff --git a/src/photos-filterable.h b/src/photos-filterable.h +index e768bca0..8a3415fc 100644 +--- a/src/photos-filterable.h ++++ b/src/photos-filterable.h +@@ -37,7 +37,6 @@ struct _PhotosFilterableInterface + gboolean (*get_builtin) (PhotosFilterable *self); + gchar *(*get_filter) (PhotosFilterable *self); + const gchar *(*get_id) (PhotosFilterable *self); +- gchar *(*get_where) (PhotosFilterable *self); + gboolean (*is_search_criterion) (PhotosFilterable *self); + }; + +@@ -47,8 +46,6 @@ gchar *photos_filterable_get_filter (PhotosFilterable *self + + const gchar *photos_filterable_get_id (PhotosFilterable *self); + +-gchar *photos_filterable_get_where (PhotosFilterable *self); +- + gboolean photos_filterable_is_search_criterion (PhotosFilterable *self); + + G_END_DECLS +diff --git a/src/photos-query-builder.c b/src/photos-query-builder.c +index 6b996811..e0735bde 100644 +--- a/src/photos-query-builder.c ++++ b/src/photos-query-builder.c +@@ -26,112 +26,76 @@ + #include + + #include "photos-base-manager.h" ++#include "photos-query.h" + #include "photos-query-builder.h" + #include "photos-search-type.h" + #include "photos-source-manager.h" + #include "photos-search-match-manager.h" + #include "photos-search-type-manager.h" + ++#define PHOTOS_QUERY_COLLECTIONS_IDENTIFIER "photos:collection:" ++#define PHOTOS_QUERY_LOCAL_COLLECTIONS_IDENTIFIER "photos:collection:local:" + +-static gchar * +-photos_query_builder_filter (PhotosSearchContextState *state, gint flags) +-{ +- gchar *sparql; +- g_autofree gchar *src_mngr_filter = NULL; +- g_autofree gchar *srch_mtch_mngr_filter = NULL; +- g_autofree gchar *srch_typ_mngr_filter = NULL; ++const gchar *collections_default_filter = \ ++ "(fn:starts-with (nao:identifier (?urn), '" PHOTOS_QUERY_COLLECTIONS_IDENTIFIER "')" ++ " || (?urn = nfo:image-category-screenshot))"; + +- src_mngr_filter = photos_base_manager_get_filter (state->src_mngr, flags); +- srch_mtch_mngr_filter = photos_base_manager_get_filter (state->srch_mtch_mngr, flags); +- srch_typ_mngr_filter = photos_base_manager_get_filter (state->srch_typ_mngr, flags); + +- sparql = g_strdup_printf ("FILTER (%s && %s && %s)", +- src_mngr_filter, +- srch_mtch_mngr_filter, +- srch_typ_mngr_filter); +- +- return sparql; +-} ++/* This includes mimetype blocklist */ ++const gchar *photos_default_filter = \ ++ "(nie:mimeType(?urn) != 'image/gif' && nie:mimeType(?urn) != 'image/x-eps')"; + + + static gchar * +-photos_query_builder_optional (void) +-{ +- return g_strdup ("OPTIONAL { ?urn nco:creator ?creator . } " +- "OPTIONAL { ?urn nco:publisher ?publisher . }"); +-} +- +- +-static gchar * +-photos_query_builder_inner_where (PhotosSearchContextState *state, gboolean global, gint flags) ++photos_query_builder_query (PhotosSearchContextState *state, ++ gboolean global, ++ gint flags, ++ PhotosOffsetController *offset_cntrlr) + { +- g_autofree gchar *item_mngr_where = NULL; ++ PhotosSparqlTemplate *template; ++ const gchar *projection = NULL; ++ g_autofree gchar *item_pattern = NULL; ++ g_autofree gchar *search_filter = NULL; ++ g_autofree gchar *source_filter = NULL; ++ const gchar *order = NULL; ++ g_autofree gchar *offset_limit = NULL; + gchar *sparql; +- g_autofree gchar *srch_typ_mngr_where = NULL; + +- srch_typ_mngr_where = photos_base_manager_get_where (state->srch_typ_mngr, flags); ++ template = photos_base_manager_get_sparql_template (state->srch_typ_mngr, flags); ++ ++ projection = "?urn " ++ "nie:url (?urn) " ++ "nfo:fileName (?urn) " ++ "nie:mimeType (?urn) " ++ "nie:title (?urn) " ++ "tracker:coalesce (nco:fullname (?creator), nco:fullname (?publisher), '') " ++ "tracker:coalesce (nfo:fileLastModified (?urn), nie:contentLastModified (?urn)) AS ?mtime " ++ "nao:identifier (?urn) " ++ "rdf:type (?urn) " ++ "nie:dataSource(?urn) " ++ "( EXISTS { ?urn nao:hasTag nao:predefined-tag-favorite } ) " ++ "( EXISTS { ?urn nco:contributor ?contributor FILTER ( ?contributor != ?creator ) } ) " ++ "tracker:coalesce(nfo:fileCreated (?urn), nie:contentCreated (?urn)) " ++ "nfo:width (?urn) " ++ "nfo:height (?urn) " ++ "nfo:equipment (?urn) " ++ "nfo:orientation (?urn) " ++ "nmm:exposureTime (?urn) " ++ "nmm:fnumber (?urn) " ++ "nmm:focalLength (?urn) " ++ "nmm:isoSpeed (?urn) " ++ "nmm:flash (?urn) " ++ "slo:location (?urn) "; ++ ++ item_pattern = photos_base_manager_get_where (state->item_mngr, flags); + + if (!(flags & PHOTOS_QUERY_FLAGS_UNFILTERED)) + { +- if (global) +- { +- /* TODO: SearchCategoryManager */ +- +- item_mngr_where = photos_base_manager_get_where (state->item_mngr, flags); +- } ++ source_filter = photos_base_manager_get_filter (state->src_mngr, flags); ++ search_filter = photos_base_manager_get_filter (state->srch_mtch_mngr, flags); + } + +- sparql = g_strdup_printf ("WHERE { %s %s }", +- srch_typ_mngr_where, +- (item_mngr_where != NULL) ? item_mngr_where : ""); +- +- return sparql; +-} +- +- +-static gchar * +-photos_query_builder_where (PhotosSearchContextState *state, gboolean global, gint flags) +-{ +- const gchar *count_items = "COUNT (?item) AS ?count"; +- gboolean item_defined; +- g_autofree gchar *filter = NULL; +- g_autofree gchar *optional = NULL; +- gchar *sparql; +- g_autofree gchar *where_sparql = NULL; +- +- where_sparql = photos_query_builder_inner_where (state, global, flags); +- item_defined = strstr (where_sparql, "?item") != NULL; +- +- optional = photos_query_builder_optional (); +- +- if (!(flags & PHOTOS_QUERY_FLAGS_UNFILTERED)) +- filter = photos_query_builder_filter (state, flags); +- +- sparql = g_strdup_printf ("WHERE {{" +- " SELECT ?urn rdf:type (?urn) AS ?type %s %s GROUP BY (?urn)" +- " }" +- " %s %s" +- "}", +- item_defined ? count_items : "", +- where_sparql, +- optional, +- (filter != NULL) ? filter : ""); +- +- return sparql; +-} +- +- +-static gchar * +-photos_query_builder_query (PhotosSearchContextState *state, +- gboolean global, +- gint flags, +- PhotosOffsetController *offset_cntrlr) +-{ +- gchar *sparql; +- g_autofree gchar *tail_sparql = NULL; +- g_autofree gchar *where_sparql = NULL; +- +- where_sparql = photos_query_builder_where (state, global, flags); ++ order = "ORDER BY DESC (?mtime)"; + + if (global && (flags & PHOTOS_QUERY_FLAGS_UNLIMITED) == 0) + { +@@ -144,35 +108,19 @@ photos_query_builder_query (PhotosSearchContextState *state, + step = photos_offset_controller_get_step (offset_cntrlr); + } + +- tail_sparql = g_strdup_printf ("ORDER BY DESC (?mtime) LIMIT %d OFFSET %d", step, offset); ++ offset_limit = g_strdup_printf ("LIMIT %d OFFSET %d", step, offset); + } + +- sparql = g_strconcat ("SELECT ?urn " +- "nie:url (?urn) " +- "nfo:fileName (?urn) " +- "nie:mimeType (?urn) " +- "nie:title (?urn) " +- "tracker:coalesce (nco:fullname (?creator), nco:fullname (?publisher), '') " +- "tracker:coalesce (nfo:fileLastModified (?urn), nie:contentLastModified (?urn)) AS ?mtime " +- "nao:identifier (?urn) " +- "rdf:type (?urn) " +- "nie:dataSource(?urn) " +- "( EXISTS { ?urn nao:hasTag nao:predefined-tag-favorite } ) " +- "( EXISTS { ?urn nco:contributor ?contributor FILTER ( ?contributor != ?creator ) } ) " +- "tracker:coalesce(nfo:fileCreated (?urn), nie:contentCreated (?urn)) " +- "nfo:width (?urn) " +- "nfo:height (?urn) " +- "nfo:equipment (?urn) " +- "nfo:orientation (?urn) " +- "nmm:exposureTime (?urn) " +- "nmm:fnumber (?urn) " +- "nmm:focalLength (?urn) " +- "nmm:isoSpeed (?urn) " +- "nmm:flash (?urn) " +- "slo:location (?urn) ", +- where_sparql, +- tail_sparql, +- NULL); ++ sparql = photos_sparql_template_get_sparql (template, ++ "projection", projection, ++ "collections_default_filter", collections_default_filter, ++ "item_pattern", item_pattern, ++ "photos_default_filter", photos_default_filter, ++ "source_filter", source_filter ? source_filter : "", ++ "search_filter", search_filter ? search_filter : "", ++ "order", order, ++ "offset_limit", offset_limit ? offset_limit : "", ++ NULL); + + return sparql; + } +@@ -231,12 +179,37 @@ photos_query_builder_collection_icon_query (PhotosSearchContextState *state, con + PhotosQuery * + photos_query_builder_count_query (PhotosSearchContextState *state, gint flags) + { +- PhotosQuery *query; ++ PhotosSparqlTemplate *template; ++ const gchar *projection = NULL; ++ g_autofree gchar *item_pattern = NULL; ++ g_autofree gchar *search_filter = NULL; ++ g_autofree gchar *source_filter = NULL; + g_autofree gchar *sparql = NULL; +- g_autofree gchar *where_sparql = NULL; ++ PhotosQuery *query; ++ ++ template = photos_base_manager_get_sparql_template (state->srch_typ_mngr, flags); ++ ++ projection = "COUNT(?urn) "; ++ ++ item_pattern = photos_base_manager_get_where (state->item_mngr, flags); ++ ++ if (! (flags & PHOTOS_QUERY_FLAGS_UNFILTERED)) ++ { ++ source_filter = photos_base_manager_get_filter (state->src_mngr, flags); ++ search_filter = photos_base_manager_get_filter (state->srch_mtch_mngr, flags); ++ } ++ ++ sparql = photos_sparql_template_get_sparql (template, ++ "projection", projection, ++ "collections_default_filter", collections_default_filter, ++ "item_pattern", item_pattern, ++ "photos_default_filter", photos_default_filter, ++ "source_filter", source_filter ? source_filter : "", ++ "search_filter", search_filter ? search_filter : "", ++ "order", "", ++ "offset_limit", "", ++ NULL); + +- where_sparql = photos_query_builder_where (state, TRUE, flags); +- sparql = g_strconcat ("SELECT DISTINCT COUNT(?urn) ", where_sparql, NULL); + query = photos_query_new (state, sparql); + + return query; +diff --git a/src/photos-search-type-manager.c b/src/photos-search-type-manager.c +index 87f441ed..5c87d4a4 100644 +--- a/src/photos-search-type-manager.c ++++ b/src/photos-search-type-manager.c +@@ -41,13 +41,6 @@ struct _PhotosSearchTypeManager + G_DEFINE_TYPE (PhotosSearchTypeManager, photos_search_type_manager, PHOTOS_TYPE_BASE_MANAGER); + + +-static const gchar *BLACKLISTED_MIME_TYPES[] = +-{ +- "image/gif", +- "image/x-eps" +-}; +- +- + static gchar * + photos_search_type_manager_get_filter (PhotosBaseManager *mngr, gint flags) + { +@@ -69,9 +62,8 @@ photos_search_type_manager_get_filter (PhotosBaseManager *mngr, gint flags) + return filter; + } + +- +-static gchar * +-photos_search_type_manager_get_where (PhotosBaseManager *mngr, gint flags) ++static PhotosSparqlTemplate * ++photos_search_type_manager_get_sparql_template (PhotosBaseManager *mngr, gint flags) + { + GObject *search_type; + +@@ -86,74 +78,39 @@ photos_search_type_manager_get_where (PhotosBaseManager *mngr, gint flags) + else + search_type = photos_base_manager_get_object_by_id (mngr, PHOTOS_SEARCH_TYPE_STOCK_ALL); + +- return photos_filterable_get_where (PHOTOS_FILTERABLE (search_type)); ++ return photos_search_type_get_sparql_template (PHOTOS_SEARCH_TYPE (search_type)); + } + +- + static void + photos_search_type_manager_init (PhotosSearchTypeManager *self) + { + PhotosSearchType *search_type; +- gchar *item_filter; +- gchar *all_filter; +- gchar *blacklisted_mime_types_filter; +- gchar *col_filter; +- gchar **strv; +- guint i; +- guint n_elements; +- +- n_elements = G_N_ELEMENTS (BLACKLISTED_MIME_TYPES); +- strv = (gchar **) g_malloc0_n (n_elements + 1, sizeof (gchar *)); +- for (i = 0; i < n_elements; i++) +- strv[i] = g_strdup_printf ("nie:mimeType(?urn) != '%s'", BLACKLISTED_MIME_TYPES[i]); +- +- blacklisted_mime_types_filter = g_strjoinv (" && ", strv); +- +- item_filter = g_strdup_printf ("(fn:contains (?type, 'nmm#Photo') && %s)", blacklisted_mime_types_filter); +- col_filter = g_strdup_printf ("(fn:contains (?type, 'nfo#DataContainer')" +- " && ?count > 0" +- " && (fn:starts-with (nao:identifier (?urn), '%s')" +- " || (?urn = nfo:image-category-screenshot)))", +- PHOTOS_QUERY_COLLECTIONS_IDENTIFIER); +- all_filter = g_strdup_printf ("(%s || %s)", col_filter, item_filter); + + search_type = photos_search_type_new_full (PHOTOS_SEARCH_TYPE_STOCK_ALL, + _("All"), +- "?urn a rdfs:Resource. " +- "OPTIONAL {?item a nmm:Photo; nie:isPartOf ?urn}", +- all_filter); ++ "resource:///org/gnome/Photos/all.sparql.template"); + photos_base_manager_add_object (PHOTOS_BASE_MANAGER (self), G_OBJECT (search_type)); + g_object_unref (search_type); + + search_type = photos_search_type_new_full (PHOTOS_SEARCH_TYPE_STOCK_COLLECTIONS, + _("Albums"), +- "?urn a nfo:DataContainer. " +- "?item a nmm:Photo; nie:isPartOf ?urn.", +- col_filter); ++ "resource:///org/gnome/Photos/collections.sparql.template"); + photos_base_manager_add_object (PHOTOS_BASE_MANAGER (self), G_OBJECT (search_type)); + g_object_unref (search_type); + + search_type = photos_search_type_new_full (PHOTOS_SEARCH_TYPE_STOCK_FAVORITES, + _("Favorites"), +- "?urn a nmm:Photo; nao:hasTag nao:predefined-tag-favorite. ", +- blacklisted_mime_types_filter); ++ "resource:///org/gnome/Photos/favorite-photos.sparql.template"); + photos_base_manager_add_object (PHOTOS_BASE_MANAGER (self), G_OBJECT (search_type)); + g_object_unref (search_type); + + search_type = photos_search_type_new_full (PHOTOS_SEARCH_TYPE_STOCK_PHOTOS, + _("Photos"), +- "?urn a nmm:Photo", +- blacklisted_mime_types_filter); ++ "resource:///org/gnome/Photos/photos.sparql.template"); + photos_base_manager_add_object (PHOTOS_BASE_MANAGER (self), G_OBJECT (search_type)); + g_object_unref (search_type); + + photos_base_manager_set_active_object_by_id (PHOTOS_BASE_MANAGER (self), PHOTOS_SEARCH_TYPE_STOCK_ALL); +- +- g_free (item_filter); +- g_free (all_filter); +- g_free (blacklisted_mime_types_filter); +- g_free (col_filter); +- g_strfreev (strv); + } + + +@@ -163,7 +120,7 @@ photos_search_type_manager_class_init (PhotosSearchTypeManagerClass *class) + PhotosBaseManagerClass *base_manager_class = PHOTOS_BASE_MANAGER_CLASS (class); + + base_manager_class->get_filter = photos_search_type_manager_get_filter; +- base_manager_class->get_where = photos_search_type_manager_get_where; ++ base_manager_class->get_sparql_template = photos_search_type_manager_get_sparql_template; + } + + +diff --git a/src/photos-search-type.c b/src/photos-search-type.c +index 44dc60eb..f3bbae15 100644 +--- a/src/photos-search-type.c ++++ b/src/photos-search-type.c +@@ -25,24 +25,23 @@ + + #include "photos-filterable.h" + #include "photos-search-type.h" ++#include "photos-sparql-template.h" + + + struct _PhotosSearchType + { + GObject parent_instance; +- gchar *filter; + gchar *id; + gchar *name; +- gchar *where; ++ PhotosSparqlTemplate *sparql_template; + }; + + enum + { + PROP_0, +- PROP_FILTER, + PROP_ID, + PROP_NAME, +- PROP_WHERE, ++ PROP_SPARQL_TEMPLATE, + }; + + static void photos_search_type_filterable_iface_init (PhotosFilterableInterface *iface); +@@ -53,14 +52,6 @@ G_DEFINE_TYPE_WITH_CODE (PhotosSearchType, photos_search_type, G_TYPE_OBJECT, + photos_search_type_filterable_iface_init)); + + +-static gchar * +-photos_search_type_get_filter (PhotosFilterable *iface) +-{ +- PhotosSearchType *self = PHOTOS_SEARCH_TYPE (iface); +- return g_strdup (self->filter); +-} +- +- + static const gchar * + photos_search_type_get_id (PhotosFilterable *filterable) + { +@@ -69,14 +60,6 @@ photos_search_type_get_id (PhotosFilterable *filterable) + } + + +-static gchar * +-photos_search_type_get_where (PhotosFilterable *iface) +-{ +- PhotosSearchType *self = PHOTOS_SEARCH_TYPE (iface); +- return g_strdup (self->where); +-} +- +- + static gboolean + photos_search_type_is_search_criterion (PhotosFilterable *iface) + { +@@ -84,15 +67,20 @@ photos_search_type_is_search_criterion (PhotosFilterable *iface) + } + + ++PhotosSparqlTemplate * ++photos_search_type_get_sparql_template (PhotosSearchType *self) ++{ ++ return self->sparql_template; ++} ++ + static void + photos_search_type_finalize (GObject *object) + { + PhotosSearchType *self = PHOTOS_SEARCH_TYPE (object); + +- g_free (self->filter); + g_free (self->id); + g_free (self->name); +- g_free (self->where); ++ g_clear_object (&self->sparql_template); + + G_OBJECT_CLASS (photos_search_type_parent_class)->finalize (object); + } +@@ -113,6 +101,10 @@ photos_search_type_get_property (GObject *object, guint prop_id, GValue *value, + g_value_set_string (value, self->name); + break; + ++ case PROP_SPARQL_TEMPLATE: ++ g_value_set_object (value, self->sparql_template); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; +@@ -127,10 +119,6 @@ photos_search_type_set_property (GObject *object, guint prop_id, const GValue *v + + switch (prop_id) + { +- case PROP_FILTER: +- self->filter = g_value_dup_string (value); +- break; +- + case PROP_ID: + self->id = g_value_dup_string (value); + break; +@@ -139,8 +127,8 @@ photos_search_type_set_property (GObject *object, guint prop_id, const GValue *v + self->name = g_value_dup_string (value); + break; + +- case PROP_WHERE: +- self->where = g_value_dup_string (value); ++ case PROP_SPARQL_TEMPLATE: ++ self->sparql_template = g_object_ref (g_value_get_object (value)); + break; + + default: +@@ -165,14 +153,6 @@ photos_search_type_class_init (PhotosSearchTypeClass *class) + object_class->get_property = photos_search_type_get_property; + object_class->set_property = photos_search_type_set_property; + +- g_object_class_install_property (object_class, +- PROP_FILTER, +- g_param_spec_string ("filter", +- "", +- "", +- "(true)", +- G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); +- + g_object_class_install_property (object_class, + PROP_ID, + g_param_spec_string ("id", +@@ -190,11 +170,11 @@ photos_search_type_class_init (PhotosSearchTypeClass *class) + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, +- PROP_WHERE, +- g_param_spec_string ("where", +- "", ++ PROP_SPARQL_TEMPLATE, ++ g_param_spec_object ("sparql-template", + "", + "", ++ PHOTOS_TYPE_SPARQL_TEMPLATE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); + } + +@@ -202,9 +182,7 @@ photos_search_type_class_init (PhotosSearchTypeClass *class) + static void + photos_search_type_filterable_iface_init (PhotosFilterableInterface *iface) + { +- iface->get_filter = photos_search_type_get_filter; + iface->get_id = photos_search_type_get_id; +- iface->get_where = photos_search_type_get_where; + iface->is_search_criterion = photos_search_type_is_search_criterion; + } + +@@ -217,12 +195,13 @@ photos_search_type_new (const gchar *id, const gchar *name) + + + PhotosSearchType * +-photos_search_type_new_full (const gchar *id, const gchar *name, const gchar *where, const gchar *filter) ++photos_search_type_new_full (const gchar *id, const gchar *name, const gchar *template_path) + { ++ g_autoptr (PhotosSparqlTemplate) template = photos_sparql_template_new (template_path); ++ + return g_object_new (PHOTOS_TYPE_SEARCH_TYPE, + "id", id, + "name", name, +- "filter", filter, +- "where", where, ++ "sparql-template", template, + NULL); + } +diff --git a/src/photos-search-type.h b/src/photos-search-type.h +index 2f7135bd..7d699dd4 100644 +--- a/src/photos-search-type.h ++++ b/src/photos-search-type.h +@@ -24,6 +24,7 @@ + #define PHOTOS_SEARCH_TYPE_H + + #include ++#include "photos-sparql-template.h" + + G_BEGIN_DECLS + +@@ -39,8 +40,9 @@ PhotosSearchType *photos_search_type_new (const gchar *id, con + + PhotosSearchType *photos_search_type_new_full (const gchar *id, + const gchar *name, +- const gchar *where, +- const gchar *filter); ++ const gchar *template_path); ++ ++PhotosSparqlTemplate *photos_search_type_get_sparql_template (PhotosSearchType *self); + + G_END_DECLS + +diff --git a/src/photos-sparql-template.c b/src/photos-sparql-template.c +new file mode 100644 +index 00000000..b32437c0 +--- /dev/null ++++ b/src/photos-sparql-template.c +@@ -0,0 +1,187 @@ ++/* ++ * Photos - access, organize and share your photos on GNOME ++ * Copyright © 2020 Sam Thursfield ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++ ++#include "photos-sparql-template.h" ++ ++#define MAX_SPARQL_TEMPLATE_SIZE (1024 * 10) ++ ++struct _PhotosSparqlTemplate { ++ GObject parent_instance; ++ gchar *template_path; ++ gchar *template_text; ++}; ++ ++G_DEFINE_TYPE (PhotosSparqlTemplate, photos_sparql_template, G_TYPE_OBJECT) ++ ++enum { ++ PROP_0, ++ PROP_TEMPLATE_PATH, ++ N_PROPS ++}; ++ ++PhotosSparqlTemplate * ++photos_sparql_template_new (const gchar *template_path) ++{ ++ return g_object_new (PHOTOS_TYPE_SPARQL_TEMPLATE, "template-path", template_path, NULL); ++} ++ ++static void ++photos_sparql_template_constructed (GObject *object) ++{ ++ PhotosSparqlTemplate *self = PHOTOS_SPARQL_TEMPLATE (object); ++ g_autoptr (GFile) file = NULL; ++ g_autoptr (GFileInputStream) stream = NULL; ++ gchar buffer[MAX_SPARQL_TEMPLATE_SIZE + 1]; ++ gsize bytes_read; ++ g_autoptr (GError) error = NULL; ++ ++ G_OBJECT_CLASS (photos_sparql_template_parent_class)->constructed (object); ++ ++ file = g_file_new_for_uri (self->template_path); ++ ++ stream = g_file_read (file, NULL, &error); ++ ++ if (!stream) ++ { ++ g_critical ("Failed to open template %s: %s", self->template_path, error->message); ++ } ++ ++ g_input_stream_read_all (G_INPUT_STREAM (stream), buffer, MAX_SPARQL_TEMPLATE_SIZE, &bytes_read, NULL, &error); ++ ++ if (error) ++ { ++ g_critical ("Failed to read template %s: %s", self->template_path, error->message); ++ } ++ ++ buffer[bytes_read] = '\0'; ++ ++ self->template_text = g_strdup (buffer); ++} ++ ++static void ++photos_sparql_template_finalize (GObject *object) ++{ ++ G_OBJECT_CLASS (photos_sparql_template_parent_class)->finalize (object); ++} ++ ++static void ++photos_sparql_template_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ PhotosSparqlTemplate *self = PHOTOS_SPARQL_TEMPLATE (object); ++ ++ switch (prop_id) ++ { ++ case PROP_TEMPLATE_PATH: ++ g_value_set_string (value, self->template_path); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++photos_sparql_template_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ PhotosSparqlTemplate *self = PHOTOS_SPARQL_TEMPLATE (object); ++ ++ switch (prop_id) ++ { ++ case PROP_TEMPLATE_PATH: ++ self->template_path = g_value_dup_string (value); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++photos_sparql_template_class_init (PhotosSparqlTemplateClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->constructed = photos_sparql_template_constructed; ++ object_class->finalize = photos_sparql_template_finalize; ++ object_class->get_property = photos_sparql_template_get_property; ++ object_class->set_property = photos_sparql_template_set_property; ++ ++ g_object_class_install_property (object_class, ++ PROP_TEMPLATE_PATH, ++ g_param_spec_string ("template-path", ++ "Template path", ++ "Path to the template file.", ++ NULL, ++ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); ++} ++ ++static void ++photos_sparql_template_init (PhotosSparqlTemplate *self) ++{ ++} ++ ++ ++gchar * ++photos_sparql_template_get_sparql (PhotosSparqlTemplate *self, ++ const gchar *first_binding_name, ...) ++{ ++ va_list va; ++ gchar *sparql; ++ ++ sparql = self->template_text; ++ ++ /* FIXME: this is an inefficent way to do template substitutions ++ * because we allocate and free a copy of the string for each binding. ++ * We should check https://gitlab.gnome.org/GNOME/template-glib/ ++ */ ++ if (first_binding_name) ++ { ++ GRegex *regex; ++ gchar *name_regex; ++ const gchar *name; ++ const gchar *value; ++ ++ va_start (va, first_binding_name); ++ name = first_binding_name; ++ do { ++ value = va_arg (va, const gchar *); ++ ++ if (value == NULL) ++ { ++ g_critical ("Missing value for argument \"%s\"", name); ++ break; ++ } ++ ++ name_regex = g_strdup_printf ("{{\\s?%s\\s?}}", name); ++ regex = g_regex_new (name_regex, 0, 0, NULL); ++ sparql = g_regex_replace_literal (regex, sparql, -1, 0, value, 0, NULL) ; ++ } while ((name = va_arg (va, const gchar *))); ++ ++ va_end (va); ++ } ++ ++ return sparql; ++} +diff --git a/src/photos-sparql-template.h b/src/photos-sparql-template.h +new file mode 100644 +index 00000000..66351b38 +--- /dev/null ++++ b/src/photos-sparql-template.h +@@ -0,0 +1,38 @@ ++/* ++ * Photos - access, organize and share your photos on GNOME ++ * Copyright © 2020 Sam Thursfield ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#pragma once ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define PHOTOS_TYPE_SPARQL_TEMPLATE (photos_sparql_template_get_type()) ++ ++G_DECLARE_FINAL_TYPE (PhotosSparqlTemplate, photos_sparql_template, PHOTOS, SPARQL_TEMPLATE, GObject) ++ ++struct _PhotosSparqlTemplateClass ++{ ++ GObjectClass parent_class; ++}; ++ ++PhotosSparqlTemplate *photos_sparql_template_new (const gchar *template_path); ++ ++gchar *photos_sparql_template_get_sparql (PhotosSparqlTemplate *self, const gchar *first_binding_name, ...) G_GNUC_NULL_TERMINATED; ++ ++G_END_DECLS +diff --git a/src/photos.gresource.xml b/src/photos.gresource.xml +index 1015b11d..39e6d842 100644 +--- a/src/photos.gresource.xml ++++ b/src/photos.gresource.xml +@@ -35,6 +35,10 @@ + photos-selection-toolbar.ui + photos-share-dialog.ui + photos-zoom-controls.ui ++ queries/all.sparql.template ++ queries/collections.sparql.template ++ queries/favorite-photos.sparql.template ++ queries/photos.sparql.template + + + +diff --git a/src/queries/all.sparql.template b/src/queries/all.sparql.template +new file mode 100644 +index 00000000..1cef98e8 +--- /dev/null ++++ b/src/queries/all.sparql.template +@@ -0,0 +1,31 @@ ++SELECT {{projection}} ++{ ++ { ++ SELECT {{projection}} ++ { ++ { ++ SELECT ?urn COUNT(?item) AS ?count ++ { ++ ?urn a nfo:DataContainer. ++ ?item a nmm:Photo; nie:isPartOf ?urn. ++ } GROUP BY ?urn ++ } ++ FILTER (?count > 0 && {{collections_default_filter}} && {{search_filter}}) ++ } ++ GROUP BY ?urn ++ } ++ UNION ++ { ++ SELECT {{projection}} ++ { ++ ?urn a nmm:Photo . ++ OPTIONAL { ?urn nco:creator ?creator . } ++ OPTIONAL { ?urn nco:publisher ?publisher . } ++ {{item_pattern}} ++ FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++ } ++ GROUP BY ?urn ++ } ++} ++{{order}} ++{{offset_limit}} +diff --git a/src/queries/collections.sparql.template b/src/queries/collections.sparql.template +new file mode 100644 +index 00000000..20b35cd6 +--- /dev/null ++++ b/src/queries/collections.sparql.template +@@ -0,0 +1,14 @@ ++SELECT {{projection}} ++{ ++ { ++ SELECT ?urn COUNT(?item) AS ?count ++ { ++ ?urn a nfo:DataContainer. ++ ?item a nmm:Photo; nie:isPartOf ?urn. ++ } GROUP BY ?urn ++ } ++ FILTER (?count > 0 && {{collections_default_filter}} && {{search_filter}}) ++} ++GROUP BY ?urn ++{{order}} ++{{offset_limit}} +diff --git a/src/queries/favorite-photos.sparql.template b/src/queries/favorite-photos.sparql.template +new file mode 100644 +index 00000000..0885a08a +--- /dev/null ++++ b/src/queries/favorite-photos.sparql.template +@@ -0,0 +1,12 @@ ++SELECT {{projection}} ++{ ++ ?urn a nmm:Photo . ++ ?urn a nmm:Photo; nao:hasTag nao:predefined-tag-favorite . ++ OPTIONAL { ?urn nco:creator ?creator . } ++ OPTIONAL { ?urn nco:publisher ?publisher . } ++ {{item_pattern}} ++ FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++} ++GROUP BY ?urn ++{{order}} ++{{offset_limit}} +diff --git a/src/queries/photos.sparql.template b/src/queries/photos.sparql.template +new file mode 100644 +index 00000000..4eb10b74 +--- /dev/null ++++ b/src/queries/photos.sparql.template +@@ -0,0 +1,11 @@ ++SELECT {{projection}} ++{ ++ ?urn a nmm:Photo . ++ OPTIONAL { ?urn nco:creator ?creator . } ++ OPTIONAL { ?urn nco:publisher ?publisher . } ++ {{item_pattern}} ++ FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++} ++GROUP BY ?urn ++{{order}} ++{{offset_limit}} +-- +GitLab + + +From d61d440efe340bda4e195d640066fb29220d0cb5 Mon Sep 17 00:00:00 2001 +From: Sam Thursfield +Date: Sat, 23 May 2020 13:36:25 +0200 +Subject: [PATCH 4/4] Port to Tracker 3 + +Notable changes: + + * User data (favourites, albums) is now stored in a private database in + ~/.local/share/gnome-photos. This is combined with the Tracker Miner + FS index of photos at query time. + * Inside Flatpak, the app connects to Tracker via the new + xdg-tracker-portal instead of talking directly over D-Bus. Access is + limited by the portal so the app can only see the Pictures graph. + * The Flatpak build can use a bundled version of Tracker Miners, if a + suitable version is not available on the host. + * Change detection is done using TrackerNotifier instead of watching + the GraphUpdated D-Bus signal directly. + +Closes https://gitlab.gnome.org/GNOME/gnome-photos/-/issues/59 +Closes https://gitlab.gnome.org/GNOME/gnome-photos/-/issues/152 +--- + data/meson.build | 2 + + data/tracker/meson.build | 33 ++ + ...e.Photos.Tracker3.Miner.Extract.service.in | 7 + + ...ome.Photos.Tracker3.Miner.Files.service.in | 7 + + data/tracker/org.gnome.Photos.domain.rule.in | 20 + + flatpak/org.gnome.Photos.json | 55 +- + meson.build | 3 +- + src/meson.build | 19 +- + ...freedesktop.Tracker3.Miner.Files.Index.xml | 11 + + src/org.freedesktop.Tracker3.Miner.xml | 56 +++ + src/photos-application.c | 184 +++---- + src/photos-base-item.c | 18 +- + src/photos-indexing-notification.c | 53 +- + src/photos-item-manager.c | 69 +-- + src/photos-quarks.c | 20 +- + src/photos-query-builder.c | 148 ++++-- + src/photos-search-context.c | 3 + + src/photos-search-context.h | 1 + + src/photos-search-match-manager.c | 2 +- + src/photos-source.c | 14 +- + src/photos-tracker-change-event.c | 136 ----- + src/photos-tracker-change-event.h | 64 --- + src/photos-tracker-change-monitor.c | 468 ------------------ + src/photos-tracker-change-monitor.h | 42 -- + src/photos-tracker-controller.c | 12 +- + src/photos-tracker-extract-priority.xml | 2 +- + src/photos-tracker-import-controller.c | 85 ++-- + src/photos-tracker-queue.c | 206 +++++++- + src/photos-tracker-queue.h | 7 + + src/photos-tracker-resources.xml | 31 -- + src/photos-utils.c | 24 +- + src/photos-utils.h | 2 +- + src/queries/all.sparql.template | 34 +- + src/queries/collections.sparql.template | 20 +- + src/queries/favorite-photos.sparql.template | 20 +- + src/queries/photos.sparql.template | 22 +- + 36 files changed, 754 insertions(+), 1146 deletions(-) + create mode 100644 data/tracker/meson.build + create mode 100644 data/tracker/org.gnome.Photos.Tracker3.Miner.Extract.service.in + create mode 100644 data/tracker/org.gnome.Photos.Tracker3.Miner.Files.service.in + create mode 100644 data/tracker/org.gnome.Photos.domain.rule.in + create mode 100644 src/org.freedesktop.Tracker3.Miner.Files.Index.xml + create mode 100644 src/org.freedesktop.Tracker3.Miner.xml + delete mode 100644 src/photos-tracker-change-event.c + delete mode 100644 src/photos-tracker-change-event.h + delete mode 100644 src/photos-tracker-change-monitor.c + delete mode 100644 src/photos-tracker-change-monitor.h + delete mode 100644 src/photos-tracker-resources.xml + +diff --git a/data/meson.build b/data/meson.build +index 7898b0cc..f0d85d4b 100644 +--- a/data/meson.build ++++ b/data/meson.build +@@ -54,3 +54,5 @@ install_data( + photos_namespace.to_lower() + '.gschema.xml', + install_dir: join_paths(photos_datadir, 'glib-2.0', 'schemas'), + ) ++ ++subdir('tracker') +diff --git a/data/tracker/meson.build b/data/tracker/meson.build +new file mode 100644 +index 00000000..accc99c2 +--- /dev/null ++++ b/data/tracker/meson.build +@@ -0,0 +1,33 @@ ++# Files needed for running Tracker inside the Flatpak sandbox, for systems ++# which don't have a suitable version of Tracker in the host OS. ++# ++# We must export the .service files from the sandbox so they work on the ++# session bus. This means the Tracker domain name must correspond with the ++# application ID. ++ ++ ++domain_ontologies_dir = get_option('datadir') / 'tracker3' / 'domain-ontologies' ++dbus_services_dir = get_option('datadir') / 'dbus-1' / 'services' ++ ++tracker_domain_config = configuration_data() ++tracker_domain_config.set('application_id', photos_namespace) ++tracker_domain_config.set('domain_rule', get_option('prefix') / domain_ontologies_dir / photos_namespace + '.domain.rule') ++ ++configure_file( ++ input: 'org.gnome.Photos.domain.rule.in', ++ output: photos_namespace + '.domain.rule', ++ configuration: tracker_domain_config, ++ install_dir: domain_ontologies_dir) ++ ++configure_file( ++ input: 'org.gnome.Photos.Tracker3.Miner.Extract.service.in', ++ output: photos_namespace + '.Tracker3.Miner.Extract.service', ++ configuration: tracker_domain_config, ++ install_dir: dbus_services_dir) ++ ++configure_file( ++ input: 'org.gnome.Photos.Tracker3.Miner.Files.service.in', ++ output: photos_namespace + '.Tracker3.Miner.Files.service', ++ configuration: tracker_domain_config, ++ install_dir: dbus_services_dir) ++ +diff --git a/data/tracker/org.gnome.Photos.Tracker3.Miner.Extract.service.in b/data/tracker/org.gnome.Photos.Tracker3.Miner.Extract.service.in +new file mode 100644 +index 00000000..eb7a87aa +--- /dev/null ++++ b/data/tracker/org.gnome.Photos.Tracker3.Miner.Extract.service.in +@@ -0,0 +1,7 @@ ++[D-BUS Service] ++Name=@application_id@.Tracker3.Miner.Extract ++Exec=/app/libexec/tracker-extract-3 --domain-ontology @domain_rule@ ++ ++# Miner details needed for tracker-control ++Path=/org/freedesktop/Tracker3/Miner/Extract ++NameSuffix=Miner.Files +diff --git a/data/tracker/org.gnome.Photos.Tracker3.Miner.Files.service.in b/data/tracker/org.gnome.Photos.Tracker3.Miner.Files.service.in +new file mode 100644 +index 00000000..4fa7371d +--- /dev/null ++++ b/data/tracker/org.gnome.Photos.Tracker3.Miner.Files.service.in +@@ -0,0 +1,7 @@ ++[D-BUS Service] ++Name=@application_id@.Tracker3.Miner.Files ++Exec=/app/libexec/tracker-miner-fs-3 --domain-ontology @domain_rule@ --initial-sleep 0 ++ ++# Miner details needed for tracker-control ++Path=/org/freedesktop/Tracker3/Miner/Files ++NameSuffix=Miner.Files +diff --git a/data/tracker/org.gnome.Photos.domain.rule.in b/data/tracker/org.gnome.Photos.domain.rule.in +new file mode 100644 +index 00000000..8f5fc4a1 +--- /dev/null ++++ b/data/tracker/org.gnome.Photos.domain.rule.in +@@ -0,0 +1,20 @@ ++# This defines a private Tracker domain for GNOME Photos. ++# ++# It's used to run the Tracker indexer inside a Flatpak sandbox, when Photos is ++# running on a host that doesn't have a suitable version of Tracker installed. ++ ++[DomainOntology] ++# Location for the Tracker database ++CacheLocation=$XDG_CACHE_HOME/gnome-photos/miner/files ++ ++# Name of the ontology to use, must be one located in ++# $(sharedir)/tracker/ontologies ++OntologyName=nepomuk ++ ++# DBus name for the owner (not optional). Tracker will use ++# the domain as the prefix of the DBus name for all the ++# services related to this domain ontology. ++Domain=@application_id@ ++ ++# List of miners we expect to run in this domain. ++Miners=Miner.Files;Miner.Extract +diff --git a/flatpak/org.gnome.Photos.json b/flatpak/org.gnome.Photos.json +index 5d16689c..adfdaf34 100644 +--- a/flatpak/org.gnome.Photos.json ++++ b/flatpak/org.gnome.Photos.json +@@ -7,7 +7,7 @@ + "tags": [ "nightly" ], + "desktop-file-name-prefix": "(Nightly) ", + "finish-args": [ +- "--env=TRACKER_SPARQL_BACKEND=bus", ++ "--add-policy=Tracker3.dbus:org.freedesktop.Tracker3.Miner.Files=tracker:Pictures", + "--filesystem=xdg-download", + "--filesystem=xdg-pictures", + "--metadata=X-DConf=migrate-path=/org/gnome/photos/", +@@ -17,8 +17,8 @@ + "--socket=wayland", + "--socket=x11", + "--talk-name=org.freedesktop.FileManager1", +- "--talk-name=org.freedesktop.Tracker1", +- "--talk-name=org.freedesktop.Tracker1.Miner.Extract", ++ "--talk-name=org.freedesktop.Tracker3.Miner.Files", ++ "--talk-name=org.freedesktop.Tracker3.Miner.Files.Index", + "--talk-name=com.intel.dleyna-renderer", + "--talk-name=org.gnome.ControlCenter", + "--talk-name=org.gnome.SettingsDaemon", +@@ -248,56 +248,22 @@ + } + ] + }, +- { +- "name": "tracker", +- "buildsystem": "meson", +- "cleanup": [ "/bin", "/etc", "/lib/girepository-1.0", "/libexec", "/share/dbus-1", "/share/gir-1.0" ], +- "config-opts": [ "-Dbash_completion=no", "-Ddocs=false", "-Dsystemd_user_services=no" ], +- "sources": [ +- { +- "type": "git", +- "url": "https://gitlab.gnome.org/GNOME/tracker.git", +- "branch": "tracker-2.3" +- } +- ] +- }, +- { +- "name": "intltool", +- "cleanup": [ "*" ], +- "sources": [ +- { +- "type": "archive", +- "url": "https://launchpad.net/intltool/trunk/0.51.0/+download/intltool-0.51.0.tar.gz", +- "sha256": "67c74d94196b153b774ab9f89b2fa6c6ba79352407037c8c14d5aeb334e959cd" +- } +- ] +- }, + { + "name": "tracker-miners", + "buildsystem": "meson", +- "cleanup": [ "/etc", +- "/lib", +- "/libexec", +- "/share/dbus-1/services/org.freedesktop.Tracker1.Miner.Extract.service", +- "/share/dbus-1/services/org.freedesktop.Tracker1.Writeback.service", +- "/share/tracker/miners/org.freedesktop.Tracker1.Miner.Applications.service", +- "/share/tracker/miners/org.freedesktop.Tracker1.Miner.Extract.service", +- "/share/tracker/miners/org.freedesktop.Tracker1.Miner.RSS.service", +- "/share/tracker-miners", +- "/share/glib-2.0/schemas/org.freedesktop.Tracker.Extract.gschema.xml", +- "/share/glib-2.0/schemas/org.freedesktop.Tracker.Writeback.gschema.xml" ], +- "config-opts": [ "-Dextract=false", +- "-Dgeneric_media_extractor=none", +- "-Dminer_apps=false", ++ "cleanup": [ "/share/dbus-1/services/org.freedesktop.Tracker3.Miner.Extract.service", ++ "/share/dbus-1/services/org.freedesktop.Tracker3.Miner.Files.service", ++ "/share/dbus-1/services/org.freedesktop.Tracker3.Writeback.service" ], ++ "config-opts": [ "-Dman=false", + "-Dminer_fs=true", + "-Dminer_rss=false", +- "-Dsystemd_user_services=no", ++ "-Dsystemd_user_services=false", + "-Dwriteback=false" ], + "sources": [ + { + "type": "git", + "url": "https://gitlab.gnome.org/GNOME/tracker-miners.git", +- "branch": "tracker-miners-2.3" ++ "branch": "master" + } + ] + }, +@@ -309,7 +275,8 @@ + { + "type": "git", + "url": "https://gitlab.gnome.org/GNOME/gnome-photos.git", +- "disable-shallow-clone": "true" ++ "disable-shallow-clone": "true", ++ "branch": "sam/tracker3" + } + ] + } +diff --git a/meson.build b/meson.build +index 70816d24..1c6f2906 100644 +--- a/meson.build ++++ b/meson.build +@@ -172,8 +172,7 @@ libgdata_dep = dependency('libgdata', version: '>= 0.17.10') + libgfgraph_dep = dependency('libgfbgraph-0.2', version: '>= 0.2.1') + libjpeg_dep = dependency('libjpeg') + libpng_dep = dependency('libpng16') +-tracker_control_dep = dependency('tracker-control-2.0') +-tracker_sparql_dep = dependency('tracker-sparql-2.0') ++tracker_sparql_dep = dependency('tracker-sparql-3.0') + + dbus_dep = dependency('dbus-1') + dbus_service_dir = dbus_dep.get_pkgconfig_variable( +diff --git a/src/meson.build b/src/meson.build +index b5b2759c..6b3b7f01 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -205,8 +205,6 @@ sources = common_sources + files( + 'photos-tool-enhance.c', + 'photos-tool-filter-button.c', + 'photos-tool-filters.c', +- 'photos-tracker-change-event.c', +- 'photos-tracker-change-monitor.c', + 'photos-tracker-collection-view-controller.c', + 'photos-tracker-collections-controller.c', + 'photos-tracker-controller.c', +@@ -367,22 +365,18 @@ sources += gnome.gdbus_codegen( + autocleanup: 'all', + ) + +-tracker_extract_priority = 'photos-tracker-extract-priority' +- + sources += gnome.gdbus_codegen( +- tracker_extract_priority, +- tracker_extract_priority + '.xml', +- interface_prefix: 'org.freedesktop.Tracker1.', ++ 'photos-tracker-miner', ++ 'org.freedesktop.Tracker3.Miner.xml', ++ interface_prefix: 'org.freedesktop.Tracker3.', + namespace: 'Tracker', + autocleanup: 'all', + ) + +-tracker_resources = 'photos-tracker-resources' +- + sources += gnome.gdbus_codegen( +- tracker_resources, +- tracker_resources + '.xml', +- interface_prefix: 'org.freedesktop.Tracker1.', ++ 'photos-tracker-miner-index', ++ 'org.freedesktop.Tracker3.Miner.Files.Index.xml', ++ interface_prefix: 'org.freedesktop.Tracker3.', + namespace: 'Tracker', + autocleanup: 'all', + ) +@@ -401,7 +395,6 @@ deps = common_deps + [ + libgdata_dep, + libgfgraph_dep, + m_dep, +- tracker_control_dep, + tracker_sparql_dep, + ] + +diff --git a/src/org.freedesktop.Tracker3.Miner.Files.Index.xml b/src/org.freedesktop.Tracker3.Miner.Files.Index.xml +new file mode 100644 +index 00000000..e368f1e2 +--- /dev/null ++++ b/src/org.freedesktop.Tracker3.Miner.Files.Index.xml +@@ -0,0 +1,11 @@ ++ ++ ++ ++ ++ ++ " ++ Extension flags, no allowed values at the moment ++ ++ ++ ++ +diff --git a/src/org.freedesktop.Tracker3.Miner.xml b/src/org.freedesktop.Tracker3.Miner.xml +new file mode 100644 +index 00000000..6fe09c84 +--- /dev/null ++++ b/src/org.freedesktop.Tracker3.Miner.xml +@@ -0,0 +1,56 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/src/photos-application.c b/src/photos-application.c +index cb114fda..5fbbbef2 100644 +--- a/src/photos-application.c ++++ b/src/photos-application.c +@@ -36,7 +36,6 @@ + #include + #include + #include +-#include + + #include "photos-application.h" + #include "photos-base-item.h" +@@ -68,7 +67,8 @@ + #include "photos-share-notification.h" + #include "photos-share-point-manager.h" + #include "photos-thumbnail-factory.h" +-#include "photos-tracker-extract-priority.h" ++#include "photos-tracker-miner-index.h" ++#include "photos-tracker-queue.h" + #include "photos-utils.h" + + +@@ -127,7 +127,7 @@ struct _PhotosApplication + PhotosSearchProvider *search_provider; + PhotosSelectionController *sel_cntrlr; + PhotosThumbnailFactory *factory; +- TrackerExtractPriority *extract_priority; ++ TrackerMinerFilesIndex *miner_control_proxy; + gboolean empty_results; + gboolean main_window_deleted; + guint create_miners_count; +@@ -148,7 +148,6 @@ static guint signals[LAST_SIGNAL] = { 0 }; + + static void photos_application_search_context_iface_init (PhotosSearchContextInterface *iface); + +- + G_DEFINE_TYPE_WITH_CODE (PhotosApplication, photos_application, GTK_TYPE_APPLICATION, + G_IMPLEMENT_INTERFACE (PHOTOS_TYPE_SEARCH_CONTEXT, + photos_application_search_context_iface_init)); +@@ -191,11 +190,11 @@ struct _PhotosApplicationCreateData + struct _PhotosApplicationImportData + { + PhotosApplication *application; ++ TrackerMinerFilesIndex *miner_control_proxy; + GFile *destination; + GFile *import_sub_dir; + GList *files; + PhotosBaseItem *collection; +- TrackerMinerManager *manager; + gchar *collection_urn; + gint64 ctime_latest; + }; +@@ -248,7 +247,7 @@ photos_application_create_data_free (PhotosApplicationCreateData *data) + + static PhotosApplicationImportData * + photos_application_import_data_new (PhotosApplication *application, +- TrackerMinerManager *manager, ++ TrackerMinerFilesIndex *miner_control_proxy, + GList *files, + gint64 ctime_latest) + { +@@ -257,7 +256,7 @@ photos_application_import_data_new (PhotosApplication *application, + data = g_slice_new0 (PhotosApplicationImportData); + g_application_hold (G_APPLICATION (application)); + data->application = application; +- data->manager = g_object_ref (manager); ++ data->miner_control_proxy = miner_control_proxy; + data->files = g_list_copy_deep (files, (GCopyFunc) g_object_ref, NULL); + data->ctime_latest = ctime_latest; + return data; +@@ -277,8 +276,8 @@ photos_application_import_data_free (PhotosApplicationImportData *data) + + g_clear_object (&data->destination); + g_clear_object (&data->import_sub_dir); ++ g_clear_object (&data->miner_control_proxy); + g_list_free_full (data->files, g_object_unref); +- g_clear_object (&data->manager); + g_free (data->collection_urn); + g_slice_free (PhotosApplicationImportData, data); + } +@@ -584,27 +583,6 @@ photos_application_actions_update (PhotosApplication *self) + } + + +-static void +-photos_application_tracker_clear_rdf_types (GObject *source_object, GAsyncResult *res, gpointer user_data) +-{ +- PhotosApplication *self = PHOTOS_APPLICATION (user_data); +- TrackerExtractPriority *extract_priority = TRACKER_EXTRACT_PRIORITY (source_object); +- +- { +- g_autoptr (GError) error = NULL; +- +- if (!tracker_extract_priority_call_clear_rdf_types_finish (extract_priority, res, &error)) +- { +- g_warning ("Unable to call ClearRdfTypes: %s", error->message); +- goto out; +- } +- } +- +- out: +- g_application_release (G_APPLICATION (self)); +-} +- +- + static gboolean + photos_application_delete_event (PhotosApplication *self) + { +@@ -641,16 +619,6 @@ photos_application_destroy (PhotosApplication *self) + self->create_window_cancellable = g_cancellable_new (); + + photos_application_stop_miners (self); +- +- if (self->extract_priority != NULL) +- { +- g_application_hold (G_APPLICATION (self)); +- tracker_extract_priority_call_clear_rdf_types (self->extract_priority, +- NULL, +- photos_application_tracker_clear_rdf_types, +- self); +- g_clear_object (&self->extract_priority); +- } + } + + +@@ -740,60 +708,6 @@ photos_application_gegl_init_fishes_idle (gpointer user_data) + } + + +-static void +-photos_application_tracker_set_rdf_types (GObject *source_object, GAsyncResult *res, gpointer user_data) +-{ +- PhotosApplication *self = PHOTOS_APPLICATION (user_data); +- TrackerExtractPriority *extract_priority = TRACKER_EXTRACT_PRIORITY (source_object); +- +- { +- g_autoptr (GError) error = NULL; +- +- if (!tracker_extract_priority_call_set_rdf_types_finish (extract_priority, res, &error)) +- { +- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- g_warning ("Unable to call SetRdfTypes: %s", error->message); +- +- goto out; +- } +- } +- +- out: +- g_application_release (G_APPLICATION (self)); +-} +- +- +-static void +-photos_application_tracker_extract_priority (GObject *source_object, GAsyncResult *res, gpointer user_data) +-{ +- PhotosApplication *self = PHOTOS_APPLICATION (user_data); +- const gchar *const rdf_types[] = {"nfo:Image", NULL}; +- +- { +- g_autoptr (GError) error = NULL; +- +- self->extract_priority = tracker_extract_priority_proxy_new_for_bus_finish (res, &error); +- if (error != NULL) +- { +- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) +- g_warning ("Unable to create TrackerExtractPriority proxy: %s", error->message); +- +- goto out; +- } +- } +- +- g_application_hold (G_APPLICATION (self)); +- tracker_extract_priority_call_set_rdf_types (self->extract_priority, +- rdf_types, +- self->create_window_cancellable, +- photos_application_tracker_set_rdf_types, +- self); +- +- out: +- g_application_release (G_APPLICATION (self)); +-} +- +- + static gboolean + photos_application_create_window (PhotosApplication *self) + { +@@ -828,13 +742,6 @@ photos_application_create_window (PhotosApplication *self) + self->init_fishes_id = g_idle_add (photos_application_gegl_init_fishes_idle, self); + + g_application_hold (G_APPLICATION (self)); +- tracker_extract_priority_proxy_new_for_bus (G_BUS_TYPE_SESSION, +- G_DBUS_PROXY_FLAGS_NONE, +- "org.freedesktop.Tracker1.Miner.Extract", +- "/org/freedesktop/Tracker1/Extract/Priority", +- self->create_window_cancellable, +- photos_application_tracker_extract_priority, +- self); + + photos_application_start_miners (self); + return TRUE; +@@ -1113,18 +1020,18 @@ photos_application_get_state (PhotosSearchContext *context) + + + static void +-photos_application_import_index_file (GObject *source_object, GAsyncResult *res, gpointer user_data) ++photos_application_import_index_location (GObject *source_object, GAsyncResult *res, gpointer user_data) + { + PhotosApplication *self; + g_autoptr (GFile) file = G_FILE (user_data); +- TrackerMinerManager *manager = TRACKER_MINER_MANAGER (source_object); ++ TrackerMinerFilesIndex *miner_control_proxy = TRACKER_MINER_FILES_INDEX (source_object); + + self = PHOTOS_APPLICATION (g_application_get_default ()); + + { + g_autoptr (GError) error = NULL; + +- if (!tracker_miner_manager_index_file_for_process_finish (manager, res, &error)) ++ if (!tracker_miner_files_index_call_index_location_finish (miner_control_proxy, res, &error)) + { + g_autofree gchar *uri = NULL; + +@@ -1323,7 +1230,6 @@ photos_application_import_file_copy (GObject *source_object, GAsyncResult *res, + PhotosApplication *self = data->application; + g_autoptr (GFile) destination = NULL; + GFile *source = G_FILE (source_object); +- TrackerMinerManager *manager = data->manager; + + { + g_autoptr (GError) error = NULL; +@@ -1348,6 +1254,8 @@ photos_application_import_file_copy (GObject *source_object, GAsyncResult *res, + else + { + g_autofree gchar *destination_uri = NULL; ++ const gchar *tracker_priority_graphs[] = { TRACKER_PICTURES_GRAPH }; ++ const gchar *tracker_index_location_flags[] = { "for-process" }; + + g_assert_true (G_IS_FILE (destination)); + g_set_object (&data->destination, destination); +@@ -1364,11 +1272,13 @@ photos_application_import_file_copy (GObject *source_object, GAsyncResult *res, + + g_application_hold (G_APPLICATION (self)); + g_application_mark_busy (G_APPLICATION (self)); +- tracker_miner_manager_index_file_for_process_async (manager, +- destination, +- NULL, +- photos_application_import_index_file, +- g_object_ref (destination)); ++ tracker_miner_files_index_call_index_location (self->miner_control_proxy, ++ destination_uri, ++ tracker_priority_graphs, ++ tracker_index_location_flags, ++ NULL, ++ photos_application_import_index_location, ++ g_object_ref (destination)); + } + + out: +@@ -1509,6 +1419,37 @@ photos_application_import_response (GtkDialog *dialog, gint response_id, gpointe + } + + ++static TrackerMinerFilesIndex * ++photos_application_get_miner_fs_control_proxy (GCancellable *cancellable) ++{ ++ g_autoptr (PhotosTrackerQueue) tracker_queue = NULL; ++ const gchar *miner_fs_control_busname; ++ g_autoptr (GError) error = NULL; ++ TrackerMinerFilesIndex *miner_control_proxy; ++ ++ tracker_queue = photos_tracker_queue_dup_singleton (cancellable, &error); ++ ++ if (!tracker_queue) { ++ g_warning ("Error getting Tracker queue: %s. Import will not work.", error->message); ++ return NULL; ++ } ++ ++ miner_fs_control_busname = photos_tracker_queue_get_miner_fs_control_busname (tracker_queue); ++ miner_control_proxy = tracker_miner_files_index_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_NONE, ++ miner_fs_control_busname, ++ "/org/freedesktop/Tracker3/Miner/Files/Control", ++ NULL, ++ &error); ++ if (!miner_control_proxy) { ++ g_warning ("Error getting Tracker Miner FS control proxy: %s. Import will not work", error->message); ++ return NULL; ++ } ++ ++ return miner_control_proxy; ++} ++ ++ + static void + photos_application_import (PhotosApplication *self) + { +@@ -1519,8 +1460,8 @@ photos_application_import (PhotosApplication *self) + GtkWidget *dialog; + g_autoptr (PhotosApplicationImportData) data = NULL; + PhotosSource *source; +- TrackerMinerManager *manager = NULL; /* TODO: use g_autoptr */ + gint64 ctime_latest = -1; ++ TrackerMinerFilesIndex *miner_control_proxy; + + source = PHOTOS_SOURCE (photos_base_manager_get_active_object (self->state->src_mngr)); + g_return_if_fail (PHOTOS_IS_SOURCE (source)); +@@ -1533,17 +1474,9 @@ photos_application_import (PhotosApplication *self) + selection = photos_selection_controller_get_selection (self->sel_cntrlr); + g_return_if_fail (selection != NULL); + +- { +- g_autoptr (GError) error = NULL; +- +- manager = tracker_miner_manager_new_full (FALSE, &error); +- if (error != NULL) +- { +- g_warning ("Unable to create a TrackerMinerManager, importing from attached devices won't work: %s", +- error->message); +- goto out; +- } +- } ++ miner_control_proxy = photos_application_get_miner_fs_control_proxy (NULL); ++ if (!miner_control_proxy) ++ goto out; + + for (l = selection; l != NULL; l = l->next) + { +@@ -1572,14 +1505,14 @@ photos_application_import (PhotosApplication *self) + dialog = photos_import_dialog_new (GTK_WINDOW (self->main_window), ctime_latest); + gtk_widget_show_all (dialog); + +- data = photos_application_import_data_new (self, manager, files, ctime_latest); ++ data = photos_application_import_data_new (self, miner_control_proxy, files, ctime_latest); ++ + g_signal_connect (dialog, + "response", + G_CALLBACK (photos_application_import_response), + g_steal_pointer (&data)); + + out: +- g_clear_object (&manager); + g_list_free_full (files, g_object_unref); + } + +@@ -2974,6 +2907,7 @@ photos_application_dispose (GObject *object) + g_clear_object (&self->insta_action); + g_clear_object (&self->load_next_action); + g_clear_object (&self->load_previous_action); ++ g_clear_object (&self->miner_control_proxy); + g_clear_object (&self->open_action); + g_clear_object (&self->preview_menu_action); + g_clear_object (&self->primary_menu_action); +@@ -3002,7 +2936,6 @@ photos_application_dispose (GObject *object) + g_clear_object (&self->camera_cache); + g_clear_object (&self->sel_cntrlr); + g_clear_object (&self->factory); +- g_clear_object (&self->extract_priority); + + if (self->state != NULL) + { +@@ -3130,8 +3063,7 @@ photos_application_get_miners_running (PhotosApplication *self) + return self->miners_running; + } + +- +-gint ++int + photos_application_get_scale_factor (PhotosApplication *self) + { + GList *windows; +diff --git a/src/photos-base-item.c b/src/photos-base-item.c +index 143431f9..b34706f2 100644 +--- a/src/photos-base-item.c ++++ b/src/photos-base-item.c +@@ -2756,6 +2756,16 @@ photos_base_item_update_info_from_type (PhotosBaseItem *self) + } + + ++static gdouble ++get_double_with_default (TrackerSparqlCursor *cursor, PhotosQueryColumns column, gdouble default_value) ++{ ++ if (tracker_sparql_cursor_is_bound (cursor, column)) { ++ return tracker_sparql_cursor_get_double (cursor, column); ++ } else { ++ return default_value; ++ } ++} ++ + static void + photos_base_item_populate_from_cursor (PhotosBaseItem *self, TrackerSparqlCursor *cursor) + { +@@ -2891,10 +2901,10 @@ photos_base_item_populate_from_cursor (PhotosBaseItem *self, TrackerSparqlCursor + priv->width = height; + } + +- priv->exposure_time = tracker_sparql_cursor_get_double (cursor, PHOTOS_QUERY_COLUMNS_EXPOSURE_TIME); +- priv->fnumber = tracker_sparql_cursor_get_double (cursor, PHOTOS_QUERY_COLUMNS_FNUMBER); +- priv->focal_length = tracker_sparql_cursor_get_double (cursor, PHOTOS_QUERY_COLUMNS_FOCAL_LENGTH); +- priv->iso_speed = tracker_sparql_cursor_get_double (cursor, PHOTOS_QUERY_COLUMNS_ISO_SPEED); ++ priv->exposure_time = get_double_with_default (cursor, PHOTOS_QUERY_COLUMNS_EXPOSURE_TIME, 0.0); ++ priv->fnumber = get_double_with_default (cursor, PHOTOS_QUERY_COLUMNS_FNUMBER, 0.0); ++ priv->focal_length = get_double_with_default (cursor, PHOTOS_QUERY_COLUMNS_FOCAL_LENGTH, 0.0); ++ priv->iso_speed = get_double_with_default (cursor, PHOTOS_QUERY_COLUMNS_ISO_SPEED, 0.0); + + flash = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_FLASH, NULL); + priv->flash = g_quark_from_string (flash); +diff --git a/src/photos-indexing-notification.c b/src/photos-indexing-notification.c +index 7b2ea7dd..122d0d91 100644 +--- a/src/photos-indexing-notification.c ++++ b/src/photos-indexing-notification.c +@@ -26,12 +26,13 @@ + #include + #include + #include +-#include + + #include "photos-application.h" + #include "photos-gom-miner.h" + #include "photos-indexing-notification.h" + #include "photos-notification-manager.h" ++#include "photos-tracker-queue.h" ++#include "photos-tracker-miner.h" + + + struct _PhotosIndexingNotification +@@ -41,7 +42,7 @@ struct _PhotosIndexingNotification + GtkWidget *primary_label; + GtkWidget *secondary_label; + GtkWidget *spinner; +- TrackerMinerManager *manager; ++ TrackerMiner *miner_fs_proxy; + gboolean closed; + gboolean on_display; + guint timeout_id; +@@ -56,8 +57,6 @@ enum + REMOTE_MINER_TIMEOUT = 10 /* s */ + }; + +-static const gchar *MINER_FILES = "org.freedesktop.Tracker1.Miner.Files"; +- + + static void + photos_indexing_notification_remove_timeout (PhotosIndexingNotification *self) +@@ -180,16 +179,18 @@ photos_indexing_notification_check_notification (PhotosIndexingNotification *sel + GSList *running = NULL; + gboolean is_indexing_local = FALSE; + gboolean is_indexing_remote = FALSE; ++ GError *error = NULL; ++ gdouble progress; + +- running = tracker_miner_manager_get_running (self->manager); +- if (g_slist_find_custom (running, (gconstpointer) MINER_FILES, (GCompareFunc) g_strcmp0) != NULL) +- { +- gdouble progress; ++ tracker_miner_call_get_progress_sync (self->miner_fs_proxy, &progress, NULL, &error); ++ if (error) { ++ g_warning ("Couldn't get indexing progress from Tracker Miner FS: %s", error->message); + +- tracker_miner_manager_get_status (self->manager, MINER_FILES, NULL, &progress, NULL); +- if (progress < 1) +- is_indexing_local = TRUE; +- } ++ g_clear_error (&error); ++ } else { ++ if (progress < 1) ++ is_indexing_local = TRUE; ++ } + + app = g_application_get_default (); + miners_running = photos_application_get_miners_running (PHOTOS_APPLICATION (app)); +@@ -222,7 +223,6 @@ photos_indexing_notification_dispose (GObject *object) + photos_indexing_notification_remove_timeout (self); + + g_clear_object (&self->ntfctn_mngr); +- g_clear_object (&self->manager); + + G_OBJECT_CLASS (photos_indexing_notification_parent_class)->dispose (object); + } +@@ -240,13 +240,25 @@ photos_indexing_notification_init (PhotosIndexingNotification *self) + app = g_application_get_default (); + + { ++ g_autoptr (PhotosTrackerQueue) tracker_queue; + g_autoptr (GError) error = NULL; ++ const gchar *miner_fs_busname; ++ ++ tracker_queue = photos_tracker_queue_dup_singleton (NULL, &error); ++ ++ if (tracker_queue) { ++ miner_fs_busname = photos_tracker_queue_get_miner_fs_busname (tracker_queue); ++ self->miner_fs_proxy = tracker_miner_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_NONE, ++ miner_fs_busname, ++ "/org/freedesktop/Tracker3/Miner/Files", ++ NULL, ++ &error); ++ } + +- self->manager = tracker_miner_manager_new_full (FALSE, &error); + if (error != NULL) + { +- g_warning ("Unable to create a TrackerMinerManager, indexing progress notification won't work: %s", +- error->message); ++ g_warning ("Unable to create proxy for Tracker Miner FS, indexing progress notification won't work"); + return; + } + } +@@ -293,10 +305,11 @@ photos_indexing_notification_init (PhotosIndexingNotification *self) + self, + G_CONNECT_SWAPPED); + +- g_signal_connect_swapped (self->manager, +- "miner-progress", +- G_CALLBACK (photos_indexing_notification_check_notification), +- self); ++ g_signal_connect_object (self->miner_fs_proxy, ++ "progress", ++ G_CALLBACK (photos_indexing_notification_check_notification), ++ self, ++ G_CONNECT_SWAPPED); + } + + +diff --git a/src/photos-item-manager.c b/src/photos-item-manager.c +index c44fbbfc..e4511bb1 100644 +--- a/src/photos-item-manager.c ++++ b/src/photos-item-manager.c +@@ -41,12 +41,9 @@ + #include "photos-query.h" + #include "photos-search-context.h" + #include "photos-single-item-job.h" +-#include "photos-tracker-change-event.h" +-#include "photos-tracker-change-monitor.h" + #include "photos-tracker-queue.h" + #include "photos-utils.h" + +- + struct _PhotosItemManager + { + PhotosBaseManager parent_instance; +@@ -60,7 +57,7 @@ struct _PhotosItemManager + PhotosBaseItem *active_collection; + PhotosBaseManager **item_mngr_chldrn; + PhotosLoadState load_state; +- PhotosTrackerChangeMonitor *monitor; ++ TrackerNotifier *notifier; + PhotosTrackerQueue *queue; + PhotosWindowMode mode; + gboolean fullscreen; +@@ -311,7 +308,7 @@ photos_item_manager_add_cursor_for_mode (PhotosItemManager *self, + g_return_if_fail (base_item_type == G_TYPE_NONE + || (base_item_type != PHOTOS_TYPE_BASE_ITEM + && g_type_is_a (base_item_type, PHOTOS_TYPE_BASE_ITEM))); +- g_return_if_fail (TRACKER_SPARQL_IS_CURSOR (cursor)); ++ g_return_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor)); + g_return_if_fail (mode != PHOTOS_WINDOW_MODE_NONE); + g_return_if_fail (mode != PHOTOS_WINDOW_MODE_EDIT); + g_return_if_fail (mode != PHOTOS_WINDOW_MODE_PREVIEW); +@@ -529,21 +526,23 @@ photos_item_manager_item_created (PhotosItemManager *self, const gchar *urn) + + + static void +-photos_item_manager_changes_pending_foreach (gpointer key, gpointer value, gpointer user_data) ++photos_item_manager_changes_pending_foreach (gpointer data, ++ gpointer user_data) + { + PhotosItemManager *self = PHOTOS_ITEM_MANAGER (user_data); +- PhotosTrackerChangeEvent *change_event = (PhotosTrackerChangeEvent *) value; +- PhotosTrackerChangeEventType change_type; ++ TrackerNotifierEvent *event = (TrackerNotifierEvent *) data; ++ TrackerNotifierEventType change_type; + const gchar *change_urn; + +- change_type = photos_tracker_change_event_get_type (change_event); +- change_urn = photos_tracker_change_event_get_urn (change_event); ++ change_type = tracker_notifier_event_get_event_type (event); ++ change_urn = tracker_notifier_event_get_urn (event); + +- if (change_type == PHOTOS_TRACKER_CHANGE_EVENT_CHANGED) ++ if (change_type == TRACKER_NOTIFIER_EVENT_UPDATE) + { + GObject *object; + + object = photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), change_urn); ++ g_message ("UPDATE event for %s, object %p", change_urn, object); + if (object != NULL) + { + photos_base_item_refresh (PHOTOS_BASE_ITEM (object)); +@@ -557,15 +556,17 @@ photos_item_manager_changes_pending_foreach (gpointer key, gpointer value, gpoin + } + } + } +- else if (change_type == PHOTOS_TRACKER_CHANGE_EVENT_CREATED) ++ else if (change_type == TRACKER_NOTIFIER_EVENT_CREATE) + { ++ g_message ("CREATE event for %s", change_urn); + photos_item_manager_item_created (self, change_urn); + } +- else if (change_type == PHOTOS_TRACKER_CHANGE_EVENT_DELETED) ++ else if (change_type == TRACKER_NOTIFIER_EVENT_DELETE) + { + GObject *object; + + object = photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), change_urn); ++ g_message ("DELETE event for %s, object %p", change_urn, object); + if (object != NULL) + { + photos_base_item_destroy (PHOTOS_BASE_ITEM (object)); +@@ -577,9 +578,15 @@ photos_item_manager_changes_pending_foreach (gpointer key, gpointer value, gpoin + + + static void +-photos_item_manager_changes_pending (PhotosItemManager *self, GHashTable *changes) +-{ +- g_hash_table_foreach (changes, photos_item_manager_changes_pending_foreach, self); ++photos_item_manager_changes_pending (PhotosItemManager *self, ++ const gchar *service, ++ const gchar *graph, ++ GPtrArray *events, ++ gpointer user_data) ++{ ++ if (g_str_equal (graph, TRACKER_PICTURES_GRAPH)) { ++ g_ptr_array_foreach (events, photos_item_manager_changes_pending_foreach, self); ++ } + } + + +@@ -717,7 +724,7 @@ photos_item_manager_wait_for_changes_timeout (gpointer user_data) + g_autoptr (PhotosQuery) query = NULL; + g_autofree gchar *sparql = NULL; + +- sparql = g_strdup_printf ("SELECT ?urn nie:url (?urn) WHERE { ?urn nie:url '%s' }", uri); ++ sparql = g_strdup_printf ("SELECT ?urn nie:isStoredAs (?urn) WHERE { ?urn nie:isStoredAs '%s' }", uri); + query = photos_query_new (NULL, sparql); + photos_tracker_queue_select (self->queue, + query, +@@ -1038,7 +1045,7 @@ photos_item_manager_dispose (GObject *object) + g_clear_object (&self->active_object); + g_clear_object (&self->loader_cancellable); + g_clear_object (&self->active_collection); +- g_clear_object (&self->monitor); ++ g_clear_object (&self->notifier); + g_clear_object (&self->queue); + + G_OBJECT_CLASS (photos_item_manager_parent_class)->dispose (object); +@@ -1093,20 +1100,24 @@ photos_item_manager_init (PhotosItemManager *self) + + self->mode = PHOTOS_WINDOW_MODE_NONE; + +- self->monitor = photos_tracker_change_monitor_dup_singleton (NULL, NULL); +- if (G_LIKELY (self->monitor != NULL)) +- g_signal_connect_object (self->monitor, +- "changes-pending", +- G_CALLBACK (photos_item_manager_changes_pending), +- self, +- G_CONNECT_SWAPPED); +- + { + g_autoptr (GError) error = NULL; + + self->queue = photos_tracker_queue_dup_singleton (NULL, &error); + if (G_UNLIKELY (error != NULL)) +- g_warning ("Unable to create PhotosTrackerQueue: %s", error->message); ++ { ++ g_warning ("Unable to create PhotosTrackerQueue: %s", error->message); ++ self->notifier = NULL; ++ } ++ else ++ { ++ self->notifier = photos_tracker_queue_get_notifier (self->queue); ++ g_signal_connect_object (self->notifier, ++ "events", ++ G_CALLBACK (photos_item_manager_changes_pending), ++ self, ++ G_CONNECT_SWAPPED); ++ } + } + + self->fullscreen = FALSE; +@@ -1241,7 +1252,7 @@ photos_item_manager_add_item (PhotosItemManager *self, + g_return_if_fail (base_item_type == G_TYPE_NONE + || (base_item_type != PHOTOS_TYPE_BASE_ITEM + && g_type_is_a (base_item_type, PHOTOS_TYPE_BASE_ITEM))); +- g_return_if_fail (TRACKER_SPARQL_IS_CURSOR (cursor)); ++ g_return_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor)); + + id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL); + g_return_if_fail (id != NULL && id[0] != '\0'); +@@ -1370,7 +1381,7 @@ photos_item_manager_create_item (PhotosItemManager *self, + g_return_val_if_fail (base_item_type == G_TYPE_NONE + || (base_item_type != PHOTOS_TYPE_BASE_ITEM + && g_type_is_a (base_item_type, PHOTOS_TYPE_BASE_ITEM)), NULL); +- g_return_val_if_fail (TRACKER_SPARQL_IS_CURSOR (cursor), NULL); ++ g_return_val_if_fail (TRACKER_IS_SPARQL_CURSOR (cursor), NULL); + + id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL); + item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), id)); +diff --git a/src/photos-quarks.c b/src/photos-quarks.c +index 0870ef62..5ee25745 100644 +--- a/src/photos-quarks.c ++++ b/src/photos-quarks.c +@@ -25,68 +25,68 @@ + GQuark + photos_quarks_flash_off_quark (void) + { +- return g_quark_from_static_string ("http://www.tracker-project.org/temp/nmm#flash-off"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nmm#flash-off"); + } + + + GQuark + photos_quarks_flash_on_quark (void) + { +- return g_quark_from_static_string ("http://www.tracker-project.org/temp/nmm#flash-on"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nmm#flash-on"); + } + + + GQuark + photos_quarks_orientation_bottom_quark (void) + { +- return g_quark_from_static_string ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#orientation-bottom"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nfo#orientation-bottom"); + } + + + GQuark + photos_quarks_orientation_bottom_mirror_quark (void) + { +- return g_quark_from_static_string ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#orientation-bottom-mirror"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nfo#orientation-bottom-mirror"); + } + + + GQuark + photos_quarks_orientation_left_quark (void) + { +- return g_quark_from_static_string ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#orientation-left"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nfo#orientation-left"); + } + + + GQuark + photos_quarks_orientation_left_mirror_quark (void) + { +- return g_quark_from_static_string ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#orientation-left-mirror"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nfo#orientation-left-mirror"); + } + + + GQuark + photos_quarks_orientation_right_quark (void) + { +- return g_quark_from_static_string ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#orientation-right"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nfo#orientation-right"); + } + + + GQuark + photos_quarks_orientation_right_mirror_quark (void) + { +- return g_quark_from_static_string ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#orientation-right-mirror"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nfo#orientation-right-mirror"); + } + + + GQuark + photos_quarks_orientation_top_quark (void) + { +- return g_quark_from_static_string ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#orientation-top"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nfo#orientation-top"); + } + + + GQuark + photos_quarks_orientation_top_mirror_quark (void) + { +- return g_quark_from_static_string ("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#orientation-top-mirror"); ++ return g_quark_from_static_string ("http://tracker.api.gnome.org/ontology/v3/nfo#orientation-top-mirror"); + } +diff --git a/src/photos-query-builder.c b/src/photos-query-builder.c +index e0735bde..46d65e8f 100644 +--- a/src/photos-query-builder.c ++++ b/src/photos-query-builder.c +@@ -25,6 +25,7 @@ + + #include + ++#include "photos-application.h" + #include "photos-base-manager.h" + #include "photos-query.h" + #include "photos-query-builder.h" +@@ -32,6 +33,7 @@ + #include "photos-source-manager.h" + #include "photos-search-match-manager.h" + #include "photos-search-type-manager.h" ++#include "photos-tracker-queue.h" + + #define PHOTOS_QUERY_COLLECTIONS_IDENTIFIER "photos:collection:" + #define PHOTOS_QUERY_LOCAL_COLLECTIONS_IDENTIFIER "photos:collection:local:" +@@ -48,12 +50,14 @@ const gchar *photos_default_filter = \ + + static gchar * + photos_query_builder_query (PhotosSearchContextState *state, +- gboolean global, ++ const gchar *values, + gint flags, + PhotosOffsetController *offset_cntrlr) + { + PhotosSparqlTemplate *template; +- const gchar *projection = NULL; ++ const gchar *miner_fs_busname = NULL; ++ const gchar *main_projection = NULL; ++ const gchar *second_projection = NULL; + g_autofree gchar *item_pattern = NULL; + g_autofree gchar *search_filter = NULL; + g_autofree gchar *source_filter = NULL; +@@ -63,29 +67,35 @@ photos_query_builder_query (PhotosSearchContextState *state, + + template = photos_base_manager_get_sparql_template (state->srch_typ_mngr, flags); + +- projection = "?urn " +- "nie:url (?urn) " +- "nfo:fileName (?urn) " +- "nie:mimeType (?urn) " +- "nie:title (?urn) " +- "tracker:coalesce (nco:fullname (?creator), nco:fullname (?publisher), '') " +- "tracker:coalesce (nfo:fileLastModified (?urn), nie:contentLastModified (?urn)) AS ?mtime " +- "nao:identifier (?urn) " +- "rdf:type (?urn) " +- "nie:dataSource(?urn) " +- "( EXISTS { ?urn nao:hasTag nao:predefined-tag-favorite } ) " +- "( EXISTS { ?urn nco:contributor ?contributor FILTER ( ?contributor != ?creator ) } ) " +- "tracker:coalesce(nfo:fileCreated (?urn), nie:contentCreated (?urn)) " +- "nfo:width (?urn) " +- "nfo:height (?urn) " +- "nfo:equipment (?urn) " +- "nfo:orientation (?urn) " +- "nmm:exposureTime (?urn) " +- "nmm:fnumber (?urn) " +- "nmm:focalLength (?urn) " +- "nmm:isoSpeed (?urn) " +- "nmm:flash (?urn) " +- "slo:location (?urn) "; ++ miner_fs_busname = photos_tracker_queue_get_miner_fs_busname (state->queue); ++ ++ main_projection = "?urn " ++ "?file " ++ "nfo:fileName (?file) AS ?filename " ++ "nie:mimeType (?urn) AS ?mimetype " ++ "nie:title (?urn) AS ?title " ++ "tracker:coalesce (nco:fullname (?creator), nco:fullname (?publisher), '') AS ?author_name " ++ "tracker:coalesce (nfo:fileLastModified (?file), nie:contentLastModified (?urn)) AS ?mtime " ++ "nao:identifier (?urn) AS ?identifier " ++ "rdf:type (?urn) AS ?type " ++ "nie:dataSource(?urn) AS ?datasource " ++ "( EXISTS { ?urn nco:contributor ?contributor FILTER ( ?contributor != ?creator ) } ) AS ?has_contributor " ++ "tracker:coalesce(nfo:fileCreated (?file), nie:contentCreated (?urn)) AS ?ctime " ++ "nfo:width (?urn) AS ?width " ++ "nfo:height (?urn) AS ?height " ++ "nfo:equipment (?urn) AS ?equipment " ++ "nfo:orientation (?urn) AS ?orientation " ++ "nmm:exposureTime (?urn) AS ?exposure_time " ++ "nmm:fnumber (?urn) AS ?fnumber " ++ "nmm:focalLength (?urn) AS ?focal_length " ++ "nmm:isoSpeed (?urn) AS ?isospeed " ++ "nmm:flash (?urn) AS ?flash " ++ "slo:location (?urn) AS ?location "; ++ ++ second_projection = "?urn ?file ?filename ?mimetype ?title ?author_name ?mtime ?identifier ?type ?datasource " ++ "( EXISTS { ?urn nao:hasTag nao:predefined-tag-favorite } ) AS ?is_favorite " ++ "?has_contributor ?ctime ?width ?height ?equipment ?orientation ?exposure_time ?fnumber " ++ "?focal_length ?isospeed ?flash ?location "; + + item_pattern = photos_base_manager_get_where (state->item_mngr, flags); + +@@ -97,7 +107,7 @@ photos_query_builder_query (PhotosSearchContextState *state, + + order = "ORDER BY DESC (?mtime)"; + +- if (global && (flags & PHOTOS_QUERY_FLAGS_UNLIMITED) == 0) ++ if (values == NULL && (flags & PHOTOS_QUERY_FLAGS_UNLIMITED) == 0) + { + gint offset = 0; + gint step = 60; +@@ -112,12 +122,16 @@ photos_query_builder_query (PhotosSearchContextState *state, + } + + sparql = photos_sparql_template_get_sparql (template, +- "projection", projection, ++ "miner_fs_busname", miner_fs_busname, ++ "main_projection", main_projection, ++ "second_projection", second_projection, ++ "final_projection", second_projection, ++ "values", values ? values : "", + "collections_default_filter", collections_default_filter, + "item_pattern", item_pattern, + "photos_default_filter", photos_default_filter, +- "source_filter", source_filter ? source_filter : "", +- "search_filter", search_filter ? search_filter : "", ++ "source_filter", source_filter ? source_filter : "(true)", ++ "search_filter", search_filter ? search_filter : "(true)", + "order", order, + "offset_limit", offset_limit ? offset_limit : "", + NULL); +@@ -133,6 +147,7 @@ photos_query_builder_create_collection_query (PhotosSearchContextState *state, + { + g_autoptr (GDateTime) now = NULL; + PhotosQuery *query; ++ g_autoptr (TrackerResource) collection = NULL; + g_autofree gchar *identifier = NULL; + g_autofree gchar *sparql = NULL; + g_autofree gchar *time = NULL; +@@ -144,13 +159,14 @@ photos_query_builder_create_collection_query (PhotosSearchContextState *state, + now = g_date_time_new_now_utc (); + time = g_date_time_format_iso8601 (now); + +- sparql = g_strdup_printf ("INSERT { _:res a nfo:DataContainer ; a nie:DataObject ; " +- "nie:contentLastModified '%s' ; " +- "nie:title '%s' ; " +- "nao:identifier '%s' }", +- time, +- name, +- identifier); ++ collection = tracker_resource_new ("_:res"); ++ tracker_resource_add_uri (collection, "rdf:type", "nfo:DataContainer"); ++ tracker_resource_add_uri (collection, "rdf:type", "nie:DataObject"); ++ tracker_resource_set_string (collection, "nie:contentLastModified", time); ++ tracker_resource_set_string (collection, "nie:title", name); ++ tracker_resource_set_string (collection, "nao:identifier", identifier); ++ ++ sparql = tracker_resource_print_sparql_update (collection, NULL, "tracker:Pictures"); + + query = photos_query_new (state, sparql); + +@@ -180,7 +196,9 @@ PhotosQuery * + photos_query_builder_count_query (PhotosSearchContextState *state, gint flags) + { + PhotosSparqlTemplate *template; +- const gchar *projection = NULL; ++ const gchar *miner_fs_busname = NULL; ++ const gchar *count_projection = NULL; ++ const gchar *value_projection = NULL; + g_autofree gchar *item_pattern = NULL; + g_autofree gchar *search_filter = NULL; + g_autofree gchar *source_filter = NULL; +@@ -189,7 +207,10 @@ photos_query_builder_count_query (PhotosSearchContextState *state, gint flags) + + template = photos_base_manager_get_sparql_template (state->srch_typ_mngr, flags); + +- projection = "COUNT(?urn) "; ++ miner_fs_busname = photos_tracker_queue_get_miner_fs_busname (state->queue); ++ ++ value_projection = "?urn "; ++ count_projection = "COUNT(?urn) "; + + item_pattern = photos_base_manager_get_where (state->item_mngr, flags); + +@@ -200,12 +221,16 @@ photos_query_builder_count_query (PhotosSearchContextState *state, gint flags) + } + + sparql = photos_sparql_template_get_sparql (template, +- "projection", projection, ++ "miner_fs_busname", miner_fs_busname, ++ "main_projection", value_projection, ++ "second_projection", value_projection, ++ "final_projection", count_projection, ++ "values", "", + "collections_default_filter", collections_default_filter, + "item_pattern", item_pattern, + "photos_default_filter", photos_default_filter, +- "source_filter", source_filter ? source_filter : "", +- "search_filter", search_filter ? search_filter : "", ++ "source_filter", source_filter ? source_filter : "(true)", ++ "search_filter", search_filter ? search_filter : "(true)", + "order", "", + "offset_limit", "", + NULL); +@@ -264,7 +289,7 @@ photos_query_builder_fetch_collections_local (PhotosSearchContextState *state) + g_autofree gchar *sparql = NULL; + + sparql = photos_query_builder_query (state, +- TRUE, ++ NULL, + PHOTOS_QUERY_FLAGS_COLLECTIONS + | PHOTOS_QUERY_FLAGS_LOCAL + | PHOTOS_QUERY_FLAGS_UNLIMITED, +@@ -284,7 +309,7 @@ photos_query_builder_global_query (PhotosSearchContextState *state, + PhotosQuery *query; + g_autofree gchar *sparql = NULL; + +- sparql = photos_query_builder_query (state, TRUE, flags, offset_cntrlr); ++ sparql = photos_query_builder_query (state, NULL, flags, offset_cntrlr); + query = photos_query_new (state, sparql); + + return query; +@@ -313,10 +338,19 @@ photos_query_builder_set_collection_query (PhotosSearchContextState *state, + PhotosQuery *query; + g_autofree gchar *sparql = NULL; + +- sparql = g_strdup_printf ("%s { <%s> nie:isPartOf <%s> }", +- setting ? "INSERT" : "DELETE", +- item_urn, +- collection_urn); ++ if (setting) ++ sparql = g_strdup_printf ("INSERT DATA { " ++ " GRAPH tracker:Pictures {" ++ " <%s> a nie:DataObject , nmm:Photo ; " ++ " nie:isPartOf <%s> " ++ " }" ++ "}", item_urn, collection_urn); ++ else ++ sparql = g_strdup_printf ("DELETE DATA { " ++ " GRAPH tracker:Pictures {" ++ " <%s> nie:isPartOf <%s> " ++ " }" ++ "}", item_urn, collection_urn); + query = photos_query_new (state, sparql); + + return query; +@@ -326,17 +360,14 @@ photos_query_builder_set_collection_query (PhotosSearchContextState *state, + PhotosQuery * + photos_query_builder_single_query (PhotosSearchContextState *state, gint flags, const gchar *resource) + { +- g_autoptr (GRegex) regex = NULL; + PhotosQuery *query; +- g_autofree gchar *replacement = NULL; + g_autofree gchar *sparql = NULL; +- g_autofree gchar *tmp = NULL; ++ g_autofree gchar *values = NULL; ++ ++ values = g_strdup_printf ("VALUES ?urn { <%s> }", resource); + +- tmp = photos_query_builder_query (state, FALSE, flags, NULL); ++ sparql = photos_query_builder_query (state, values, flags, NULL); + +- regex = g_regex_new ("\\?urn", 0, 0, NULL); +- replacement = g_strconcat ("<", resource, ">", NULL); +- sparql = g_regex_replace (regex, tmp, -1, 0, replacement, 0, NULL); + query = photos_query_new (state, sparql); + + return query; +@@ -354,7 +385,14 @@ photos_query_builder_update_mtime_query (PhotosSearchContextState *state, const + now = g_date_time_new_now_utc (); + time = g_date_time_format_iso8601 (now); + +- sparql = g_strdup_printf ("INSERT OR REPLACE { <%s> nie:contentLastModified '%s' }", resource, time); ++ sparql = g_strdup_printf ("WITH tracker:Pictures " ++ "DELETE { <%s> nie:contentLastModified ?time } " ++ "INSERT { " ++ " <%s> a nmm:Photo ; " ++ " nie:contentLastModified '%s' " ++ "}" ++ "WHERE { <%s> nie:contentLastModified ?time }", ++ resource, resource, time, resource); + query = photos_query_new (state, sparql); + + return query; +diff --git a/src/photos-search-context.c b/src/photos-search-context.c +index 4b503798..6f8694bc 100644 +--- a/src/photos-search-context.c ++++ b/src/photos-search-context.c +@@ -31,6 +31,7 @@ + #include "photos-search-match-manager.h" + #include "photos-search-type-manager.h" + #include "photos-source-manager.h" ++#include "photos-tracker-queue.h" + + + G_DEFINE_INTERFACE (PhotosSearchContext, photos_search_context, G_TYPE_OBJECT); +@@ -54,6 +55,7 @@ photos_search_context_state_new (PhotosSearchContext *self) + state->srch_cntrlr = photos_search_controller_new (); + state->srch_mtch_mngr = photos_search_match_manager_new (state->srch_cntrlr); + state->srch_typ_mngr = photos_search_type_manager_new (); ++ state->queue = photos_tracker_queue_dup_singleton (NULL, NULL); + + return state; + } +@@ -68,6 +70,7 @@ photos_search_context_state_free (PhotosSearchContextState *state) + g_object_unref (state->srch_mtch_mngr); + g_object_unref (state->srch_typ_mngr); + g_object_unref (state->srch_cntrlr); ++ g_clear_object (&state->queue); + g_slice_free (PhotosSearchContextState, state); + } + +diff --git a/src/photos-search-context.h b/src/photos-search-context.h +index 2af6cf96..5a18d386 100644 +--- a/src/photos-search-context.h ++++ b/src/photos-search-context.h +@@ -41,6 +41,7 @@ struct _PhotosSearchContextState + gpointer srch_typ_mngr; + gpointer offset_cntrlr; + gpointer srch_cntrlr; ++ gpointer queue; + }; + + PhotosSearchContextState *photos_search_context_state_new (PhotosSearchContext *self); +diff --git a/src/photos-search-match-manager.c b/src/photos-search-match-manager.c +index e6dc429a..2ba1fd0f 100644 +--- a/src/photos-search-match-manager.c ++++ b/src/photos-search-match-manager.c +@@ -148,7 +148,7 @@ photos_search_match_manager_init (PhotosSearchMatchManager *self) + " tracker:case-fold (tracker:coalesce (nco:fullname (?creator), nco:fullname(?publisher)))," + " \"%s\")"; + title_filter = "fn:contains (" +- " tracker:case-fold (tracker:coalesce (nie:title (?urn), nfo:fileName(?urn)))," ++ " tracker:case-fold (tracker:coalesce (nie:title (?urn), nfo:fileName(?file)))," + " \"%s\")"; + + search_match = photos_search_match_new (PHOTOS_SEARCH_MATCH_STOCK_ALL, +diff --git a/src/photos-source.c b/src/photos-source.c +index db6f2de9..5219652c 100644 +--- a/src/photos-source.c ++++ b/src/photos-source.c +@@ -62,7 +62,7 @@ G_DEFINE_TYPE_WITH_CODE (PhotosSource, photos_source, G_TYPE_OBJECT, + DZL_DEFINE_COUNTER (instances, "PhotosSource", "Instances", "Number of PhotosSource instances") + + +-static const gchar *TRACKER_SCHEMA = "org.freedesktop.Tracker.Miner.Files"; ++static const gchar *TRACKER_SCHEMA = "org.freedesktop.Tracker3.Miner.Files"; + static const gchar *TRACKER_KEY_RECURSIVE_DIRECTORIES = "index-recursive-directories"; + + +@@ -94,7 +94,7 @@ photos_source_build_filter_local (void) + continue; + + tracker_uri = photos_utils_convert_path_to_uri (tracker_dirs[i]); +- g_string_append_printf (tracker_filter, " || fn:contains (nie:url (?urn), '%s')", tracker_uri); ++ g_string_append_printf (tracker_filter, " || fn:contains (nie:isStoredAs (?urn), '%s')", tracker_uri); + } + + path = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); +@@ -109,11 +109,11 @@ photos_source_build_filter_local (void) + export_path = g_build_filename (path, PHOTOS_EXPORT_SUBPATH, NULL); + export_uri = photos_utils_convert_path_to_uri (export_path); + +- filter = g_strdup_printf ("(((fn:contains (nie:url (?urn), '%s')" +- " || fn:contains (nie:url (?urn), '%s')" +- " || fn:contains (nie:url (?urn), '%s')" ++ filter = g_strdup_printf ("(((fn:contains (nie:isStoredAs (?urn), '%s')" ++ " || fn:contains (nie:isStoredAs (?urn), '%s')" ++ " || fn:contains (nie:isStoredAs (?urn), '%s')" + " %s)" +- " && !fn:contains (nie:url (?urn), '%s'))" ++ " && !fn:contains (nie:isStoredAs (?urn), '%s'))" + " || fn:starts-with (nao:identifier (?urn), '%s')" + " || (?urn = nfo:image-category-screenshot))", + desktop_uri, +@@ -146,7 +146,7 @@ photos_source_build_filter_resource (PhotosSource *self) + + root = g_mount_get_root (self->mount); + uri = g_file_get_uri (root); +- filter = g_strdup_printf ("(fn:starts-with (nie:url (?urn), '%s'))", uri); ++ filter = g_strdup_printf ("(fn:starts-with (nie:isStoredAs (?urn), '%s'))", uri); + } + else + { +diff --git a/src/photos-tracker-change-event.c b/src/photos-tracker-change-event.c +deleted file mode 100644 +index d5c74ebc..00000000 +--- a/src/photos-tracker-change-event.c ++++ /dev/null +@@ -1,136 +0,0 @@ +-/* +- * Photos - access, organize and share your photos on GNOME +- * Copyright © 2012 – 2019 Red Hat, Inc. +- * +- * This program is free software: you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation, either version 3 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program. If not, see . +- */ +- +-/* Based on code from: +- * + Documents +- */ +- +- +-#include "config.h" +- +-#include "photos-tracker-change-event.h" +- +- +-struct _PhotosTrackerChangeEvent +-{ +- PhotosTrackerChangeEventType type; +- gchar *predicate; +- gchar *urn; +- gint32 predicate_id; +- gint32 urn_id; +-}; +- +- +-static const gchar *RDF_TYPE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"; +- +- +-void +-photos_tracker_change_event_free (PhotosTrackerChangeEvent *self) +-{ +- g_free (self->predicate); +- g_free (self->urn); +- g_slice_free (PhotosTrackerChangeEvent, self); +-} +- +- +-PhotosTrackerChangeEvent * +-photos_tracker_change_event_new (gint32 urn_id, gint32 predicate_id, gboolean is_delete) +-{ +- PhotosTrackerChangeEvent *self; +- +- self = g_slice_new0 (PhotosTrackerChangeEvent); +- self->urn_id = urn_id; +- self->predicate_id = predicate_id; +- +- if (is_delete) +- self->type = PHOTOS_TRACKER_CHANGE_EVENT_DELETED; +- else +- self->type = PHOTOS_TRACKER_CHANGE_EVENT_CREATED; +- +- return self; +-} +- +- +-PhotosTrackerChangeEvent * +-photos_tracker_change_event_copy (PhotosTrackerChangeEvent *event) +-{ +- PhotosTrackerChangeEvent *self; +- +- self = g_slice_new0 (PhotosTrackerChangeEvent); +- self->type = event->type; +- self->predicate = g_strdup (event->predicate); +- self->urn = g_strdup (event->urn); +- self->predicate_id = event->predicate_id; +- self->urn_id = event->urn_id; +- +- return self; +-} +- +- +-PhotosTrackerChangeEventType +-photos_tracker_change_event_get_type (PhotosTrackerChangeEvent *self) +-{ +- return self->type; +-} +- +- +-gint32 +-photos_tracker_change_event_get_predicate_id (PhotosTrackerChangeEvent *self) +-{ +- return self->predicate_id; +-} +- +- +-const gchar * +-photos_tracker_change_event_get_urn (PhotosTrackerChangeEvent *self) +-{ +- return self->urn; +-} +- +- +-gint32 +-photos_tracker_change_event_get_urn_id (PhotosTrackerChangeEvent *self) +-{ +- return self->urn_id; +-} +- +- +-void +-photos_tracker_change_event_merge (PhotosTrackerChangeEvent *self, PhotosTrackerChangeEvent *event) +-{ +- g_return_if_fail (g_strcmp0 (self->urn, event->urn) == 0); +- +- if (event->type == PHOTOS_TRACKER_CHANGE_EVENT_DELETED || event->type == PHOTOS_TRACKER_CHANGE_EVENT_CREATED) +- self->type = event->type; +-} +- +- +-void +-photos_tracker_change_event_set_resolved_values (PhotosTrackerChangeEvent *self, +- const gchar *urn, +- const gchar *predicate) +-{ +- g_return_if_fail (self->predicate == NULL); +- g_return_if_fail (self->urn == NULL); +- +- self->urn = g_strdup (urn); +- self->predicate = g_strdup (predicate); +- +- if (g_strcmp0 (predicate, RDF_TYPE) != 0) +- self->type = PHOTOS_TRACKER_CHANGE_EVENT_CHANGED; +-} +diff --git a/src/photos-tracker-change-event.h b/src/photos-tracker-change-event.h +deleted file mode 100644 +index eee4f40c..00000000 +--- a/src/photos-tracker-change-event.h ++++ /dev/null +@@ -1,64 +0,0 @@ +-/* +- * Photos - access, organize and share your photos on GNOME +- * Copyright © 2012 – 2019 Red Hat, Inc. +- * +- * This program is free software: you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation, either version 3 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program. If not, see . +- */ +- +-/* Based on code from: +- * + Documents +- */ +- +-#ifndef PHOTOS_TRACKER_CHANGE_EVENT_H +-#define PHOTOS_TRACKER_CHANGE_EVENT_H +- +-#include +- +-G_BEGIN_DECLS +- +-typedef enum +-{ +- PHOTOS_TRACKER_CHANGE_EVENT_CHANGED, +- PHOTOS_TRACKER_CHANGE_EVENT_CREATED, +- PHOTOS_TRACKER_CHANGE_EVENT_DELETED +-} PhotosTrackerChangeEventType; +- +-typedef struct _PhotosTrackerChangeEvent PhotosTrackerChangeEvent; +- +-PhotosTrackerChangeEvent *photos_tracker_change_event_new (gint32 urn_id, +- gint32 predicate_id, +- gboolean is_delete); +- +-PhotosTrackerChangeEvent *photos_tracker_change_event_copy (PhotosTrackerChangeEvent *event); +- +-void photos_tracker_change_event_free (PhotosTrackerChangeEvent *self); +- +-PhotosTrackerChangeEventType photos_tracker_change_event_get_type (PhotosTrackerChangeEvent *self); +- +-gint32 photos_tracker_change_event_get_predicate_id (PhotosTrackerChangeEvent *self); +- +-const gchar *photos_tracker_change_event_get_urn (PhotosTrackerChangeEvent *self); +- +-gint32 photos_tracker_change_event_get_urn_id (PhotosTrackerChangeEvent *self); +- +-void photos_tracker_change_event_merge (PhotosTrackerChangeEvent *self, +- PhotosTrackerChangeEvent *event); +- +-void photos_tracker_change_event_set_resolved_values (PhotosTrackerChangeEvent *self, +- const gchar *urn, +- const gchar *predicate); +- +-G_END_DECLS +- +-#endif /* PHOTOS_TRACKER_CHANGE_EVENT_H */ +diff --git a/src/photos-tracker-change-monitor.c b/src/photos-tracker-change-monitor.c +deleted file mode 100644 +index 2e9810aa..00000000 +--- a/src/photos-tracker-change-monitor.c ++++ /dev/null +@@ -1,468 +0,0 @@ +-/* +- * Photos - access, organize and share your photos on GNOME +- * Copyright © 2012 – 2019 Red Hat, Inc. +- * +- * This program is free software: you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation, either version 3 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program. If not, see . +- */ +- +-/* Based on code from: +- * + Documents +- */ +- +- +-#include "config.h" +- +-#include +-#include +- +-#include "photos-tracker-change-event.h" +-#include "photos-tracker-change-monitor.h" +-#include "photos-tracker-queue.h" +-#include "photos-tracker-resources.h" +-#include "photos-query.h" +- +- +-struct _PhotosTrackerChangeMonitor +-{ +- GObject parent_instance; +- GHashTable *pending_changes; +- GHashTable *unresolved_ids; +- GQueue *pending_events; +- PhotosTrackerQueue *queue; +- TrackerResources *resource_service; +- guint outstanding_ops; +- guint pending_events_id; +-}; +- +-enum +-{ +- CHANGES_PENDING, +- LAST_SIGNAL +-}; +- +-static guint signals[LAST_SIGNAL] = { 0 }; +- +-static void photos_tracker_change_monitor_initable_iface_init (GInitableIface *iface); +- +- +-G_DEFINE_TYPE_EXTENDED (PhotosTrackerChangeMonitor, photos_tracker_change_monitor, G_TYPE_OBJECT, 0, +- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, photos_tracker_change_monitor_initable_iface_init)); +- +- +-enum +-{ +- CHANGE_MONITOR_TIMEOUT = 500, /* ms */ +- CHANGE_MONITOR_MAX_ITEMS = 500 +-}; +- +- +-typedef struct _PhotosTrackerChangeMonitorQueryData PhotosTrackerChangeMonitorQueryData; +-typedef struct _TrackerResourcesEvent TrackerResourcesEvent; +- +-struct _PhotosTrackerChangeMonitorQueryData +-{ +- PhotosTrackerChangeMonitor *self; +- GHashTable *id_table; +- GQueue *events; +-}; +- +-struct _TrackerResourcesEvent +-{ +- gint32 graph; +- gint32 subject; +- gint32 predicate; +- gint32 object; +-}; +- +- +-static void +-photos_tracker_change_monitor_query_data_free (PhotosTrackerChangeMonitorQueryData *data) +-{ +- g_clear_object (&data->self); +- +- if (data->id_table != NULL) +- g_hash_table_unref (data->id_table); +- +- if (data->events != NULL) +- g_queue_free_full (data->events, (GDestroyNotify) photos_tracker_change_event_free); +- +- g_slice_free (PhotosTrackerChangeMonitorQueryData, data); +-} +- +- +-static PhotosTrackerChangeMonitorQueryData * +-photos_tracker_change_monitor_query_data_new (PhotosTrackerChangeMonitor *self, +- GHashTable *id_table, +- GQueue *events) +-{ +- PhotosTrackerChangeMonitorQueryData *data; +- +- data = g_slice_new0 (PhotosTrackerChangeMonitorQueryData); +- data->self = g_object_ref (self); +- data->id_table = id_table; +- data->events = events; +- +- return data; +-} +- +- +-static void +-photos_tracker_change_monitor_add_event (PhotosTrackerChangeMonitor *self, PhotosTrackerChangeEvent *change_event) +-{ +- PhotosTrackerChangeEvent *old_change_event; +- const gchar *urn; +- +- urn = photos_tracker_change_event_get_urn (change_event); +- old_change_event = (PhotosTrackerChangeEvent *) g_hash_table_lookup (self->pending_changes, urn); +- +- if (old_change_event != NULL) +- photos_tracker_change_event_merge (old_change_event, change_event); +- else +- g_hash_table_insert (self->pending_changes, g_strdup (urn), photos_tracker_change_event_copy (change_event)); +-} +- +- +-static void +-photos_tracker_change_monitor_remove_timeout (PhotosTrackerChangeMonitor *self) +-{ +- if (self->pending_events_id != 0) +- { +- g_source_remove (self->pending_events_id); +- self->pending_events_id = 0; +- } +-} +- +- +-static void +-photos_tracker_change_monitor_send_events (PhotosTrackerChangeMonitor *self, GHashTable *id_table, GQueue *events) +-{ +- GList *l; +- +- for (l = events->head; l != NULL; l = l->next) +- { +- PhotosTrackerChangeEvent *change_event = (PhotosTrackerChangeEvent *) l->data; +- const gchar *predicate; +- const gchar *urn; +- gint32 predicate_id; +- gint32 urn_id; +- +- predicate_id = photos_tracker_change_event_get_predicate_id (change_event); +- urn_id = photos_tracker_change_event_get_urn_id (change_event); +- +- predicate = (gchar *) g_hash_table_lookup (id_table, GINT_TO_POINTER (predicate_id)); +- if (G_UNLIKELY (predicate == NULL)) +- continue; +- +- urn = (gchar *) g_hash_table_lookup (id_table, GINT_TO_POINTER (urn_id)); +- if (G_UNLIKELY (urn == NULL)) +- continue; +- +- photos_tracker_change_event_set_resolved_values (change_event, urn, predicate); +- photos_tracker_change_monitor_add_event (self, change_event); +- } +- +- g_signal_emit (self, signals[CHANGES_PENDING], 0, self->pending_changes); +- g_hash_table_remove_all (self->pending_changes); +-} +- +- +-static void +-photos_tracker_change_monitor_cursor_next (GObject *source_object, GAsyncResult *res, gpointer user_data) +-{ +- PhotosTrackerChangeMonitorQueryData *data = (PhotosTrackerChangeMonitorQueryData *) user_data; +- PhotosTrackerChangeMonitor *self = data->self; +- TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR (source_object); +- GHashTableIter iter; +- gboolean valid; +- +- { +- g_autoptr (GError) error = NULL; +- +- valid = tracker_sparql_cursor_next_finish (cursor, res, &error); +- if (error != NULL) +- g_warning ("Unable to resolve item URNs for graph changes: %s", error->message); +- } +- +- if (valid) +- { +- guint idx; +- +- idx = 0; +- g_hash_table_iter_init (&iter, data->id_table); +- while (g_hash_table_iter_next (&iter, NULL, NULL)) +- { +- const gchar *str; +- +- str = tracker_sparql_cursor_get_string (cursor, idx, NULL); +- g_hash_table_iter_replace (&iter, g_strdup (str)); +- idx++; +- } +- +- photos_tracker_change_monitor_send_events (self, data->id_table, data->events); +- } +- +- tracker_sparql_cursor_close (cursor); +- photos_tracker_change_monitor_query_data_free (data); +-} +- +- +-static void +-photos_tracker_change_monitor_query_executed (GObject *source_object, GAsyncResult *res, gpointer user_data) +-{ +- PhotosTrackerChangeMonitorQueryData *data = (PhotosTrackerChangeMonitorQueryData *) user_data; +- TrackerSparqlConnection *connection = TRACKER_SPARQL_CONNECTION (source_object); +- TrackerSparqlCursor *cursor; /* TODO: Use g_autoptr */ +- +- { +- g_autoptr (GError) error = NULL; +- +- cursor = tracker_sparql_connection_query_finish (connection, res, &error); +- if (error != NULL) +- { +- g_warning ("Unable to resolve item URNs for graph changes: %s", error->message); +- photos_tracker_change_monitor_query_data_free (data); +- return; +- } +- } +- +- tracker_sparql_cursor_next_async (cursor, NULL, photos_tracker_change_monitor_cursor_next, data); +- g_object_unref (cursor); +-} +- +- +-static gboolean +-photos_tracker_change_monitor_process_events (PhotosTrackerChangeMonitor *self) +-{ +- GHashTable *id_table; +- GHashTableIter iter; +- GQueue *events; +- g_autoptr (GString) sparql = NULL; +- PhotosTrackerChangeMonitorQueryData *data; +- g_autoptr (PhotosQuery) query = NULL; +- gpointer id; +- +- events = self->pending_events; +- self->pending_events = g_queue_new (); +- +- id_table = self->unresolved_ids; +- self->unresolved_ids = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); +- +- self->pending_events_id = 0; +- +- sparql = g_string_new ("SELECT"); +- +- g_hash_table_iter_init (&iter, id_table); +- while (g_hash_table_iter_next (&iter, &id, NULL)) +- g_string_append_printf (sparql, " tracker:uri(%d)", GPOINTER_TO_INT (id)); +- +- g_string_append (sparql, " {}"); +- +- query = photos_query_new (NULL, sparql->str); +- +- data = photos_tracker_change_monitor_query_data_new (self, id_table, events); +- photos_tracker_queue_select (self->queue, +- query, +- NULL, +- photos_tracker_change_monitor_query_executed, +- data, +- NULL); +- +- return G_SOURCE_REMOVE; +-} +- +- +-static void +-photos_tracker_change_monitor_add_pending_event (PhotosTrackerChangeMonitor *self, +- const TrackerResourcesEvent *event, +- gboolean is_delete) +-{ +- PhotosTrackerChangeEvent *change_event; +- +- photos_tracker_change_monitor_remove_timeout (self); +- +- g_hash_table_insert (self->unresolved_ids, GINT_TO_POINTER (event->subject), NULL); +- g_hash_table_insert (self->unresolved_ids, GINT_TO_POINTER (event->predicate), NULL); +- +- change_event = photos_tracker_change_event_new (event->subject, event->predicate, is_delete); +- g_queue_push_tail (self->pending_events, change_event); +- +- if (self->pending_events->length >= CHANGE_MONITOR_MAX_ITEMS) +- photos_tracker_change_monitor_process_events (self); +- else +- self->pending_events_id = g_timeout_add (CHANGE_MONITOR_TIMEOUT, +- (GSourceFunc) photos_tracker_change_monitor_process_events, +- self); +-} +- +- +-static void +-photos_tracker_change_monitor_graph_updated (TrackerResources *resource_service, +- const gchar *class_name, +- GVariant *delete_events, +- GVariant *insert_events, +- gpointer user_data) +-{ +- PhotosTrackerChangeMonitor *self = PHOTOS_TRACKER_CHANGE_MONITOR (user_data); +- const TrackerResourcesEvent *events; +- gsize i; +- gsize n_elements; +- +- events = (const TrackerResourcesEvent *) g_variant_get_fixed_array (delete_events, +- &n_elements, +- sizeof (TrackerResourcesEvent)); +- for (i = 0; i < n_elements; i++) +- photos_tracker_change_monitor_add_pending_event (self, &events[i], TRUE); +- +- events = (const TrackerResourcesEvent *) g_variant_get_fixed_array (insert_events, +- &n_elements, +- sizeof (TrackerResourcesEvent)); +- for (i = 0; i < n_elements; i++) +- photos_tracker_change_monitor_add_pending_event (self, &events[i], FALSE); +-} +- +- +-static GObject * +-photos_tracker_change_monitor_constructor (GType type, +- guint n_construct_params, +- GObjectConstructParam *construct_params) +-{ +- static GObject *self = NULL; +- +- if (self == NULL) +- { +- self = G_OBJECT_CLASS (photos_tracker_change_monitor_parent_class)->constructor (type, +- n_construct_params, +- construct_params); +- g_object_add_weak_pointer (self, (gpointer) &self); +- return self; +- } +- +- return g_object_ref (self); +-} +- +- +-static void +-photos_tracker_change_monitor_dispose (GObject *object) +-{ +- PhotosTrackerChangeMonitor *self = PHOTOS_TRACKER_CHANGE_MONITOR (object); +- +- photos_tracker_change_monitor_remove_timeout (self); +- +- g_clear_object (&self->queue); +- g_clear_object (&self->resource_service); +- +- G_OBJECT_CLASS (photos_tracker_change_monitor_parent_class)->dispose (object); +-} +- +- +-static void +-photos_tracker_change_monitor_finalize (GObject *object) +-{ +- PhotosTrackerChangeMonitor *self = PHOTOS_TRACKER_CHANGE_MONITOR (object); +- +- g_hash_table_unref (self->pending_changes); +- g_hash_table_unref (self->unresolved_ids); +- +- g_queue_free_full (self->pending_events, (GDestroyNotify) photos_tracker_change_event_free); +- +- G_OBJECT_CLASS (photos_tracker_change_monitor_parent_class)->finalize (object); +-} +- +- +-static void +-photos_tracker_change_monitor_init (PhotosTrackerChangeMonitor *self) +-{ +- self->pending_changes = g_hash_table_new_full (g_str_hash, +- g_str_equal, +- g_free, +- (GDestroyNotify) photos_tracker_change_event_free); +- +- self->unresolved_ids = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); +- +- self->pending_events = g_queue_new (); +-} +- +- +-static void +-photos_tracker_change_monitor_class_init (PhotosTrackerChangeMonitorClass *class) +-{ +- GObjectClass *object_class = G_OBJECT_CLASS (class); +- +- object_class->constructor = photos_tracker_change_monitor_constructor; +- object_class->dispose = photos_tracker_change_monitor_dispose; +- object_class->finalize = photos_tracker_change_monitor_finalize; +- +- signals[CHANGES_PENDING] = g_signal_new ("changes-pending", +- G_TYPE_FROM_CLASS (class), +- G_SIGNAL_RUN_LAST, +- 0, +- NULL, /*accumulator */ +- NULL, /*accu_data */ +- g_cclosure_marshal_VOID__BOXED, +- G_TYPE_NONE, +- 1, +- G_TYPE_HASH_TABLE); +-} +- +- +-static gboolean +-photos_tracker_change_monitor_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) +-{ +- PhotosTrackerChangeMonitor *self = PHOTOS_TRACKER_CHANGE_MONITOR (initable); +- gboolean ret_val = TRUE; +- +- if (G_LIKELY (self->queue != NULL && self->resource_service != NULL)) +- goto out; +- +- self->queue = photos_tracker_queue_dup_singleton (cancellable, error); +- if (G_UNLIKELY (self->queue == NULL)) +- { +- ret_val = FALSE; +- goto out; +- } +- +- self->resource_service = tracker_resources_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, +- G_DBUS_PROXY_FLAGS_NONE, +- "org.freedesktop.Tracker1", +- "/org/freedesktop/Tracker1/Resources", +- cancellable, +- error); +- if (G_UNLIKELY (self->resource_service == NULL)) +- { +- ret_val = FALSE; +- goto out; +- } +- +- g_signal_connect (self->resource_service, +- "graph-updated", +- G_CALLBACK (photos_tracker_change_monitor_graph_updated), +- self); +- +- out: +- return ret_val; +-} +- +- +-static void +-photos_tracker_change_monitor_initable_iface_init (GInitableIface *iface) +-{ +- iface->init = photos_tracker_change_monitor_initable_init; +-} +- +- +-PhotosTrackerChangeMonitor * +-photos_tracker_change_monitor_dup_singleton (GCancellable *cancellable, GError **error) +-{ +- return g_initable_new (PHOTOS_TYPE_TRACKER_CHANGE_MONITOR, cancellable, error, NULL); +-} +diff --git a/src/photos-tracker-change-monitor.h b/src/photos-tracker-change-monitor.h +deleted file mode 100644 +index 9740b046..00000000 +--- a/src/photos-tracker-change-monitor.h ++++ /dev/null +@@ -1,42 +0,0 @@ +-/* +- * Photos - access, organize and share your photos on GNOME +- * Copyright © 2012 – 2019 Red Hat, Inc. +- * +- * This program is free software: you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation, either version 3 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program. If not, see . +- */ +- +-/* Based on code from: +- * + Documents +- */ +- +-#ifndef PHOTOS_TRACKER_CHANGE_MONITOR_H +-#define PHOTOS_TRACKER_CHANGE_MONITOR_H +- +-#include +- +-G_BEGIN_DECLS +- +-#define PHOTOS_TYPE_TRACKER_CHANGE_MONITOR (photos_tracker_change_monitor_get_type ()) +-G_DECLARE_FINAL_TYPE (PhotosTrackerChangeMonitor, +- photos_tracker_change_monitor, +- PHOTOS, +- TRACKER_CHANGE_MONITOR, +- GObject); +- +-PhotosTrackerChangeMonitor *photos_tracker_change_monitor_dup_singleton (GCancellable *cancellable, +- GError **error); +- +-G_END_DECLS +- +-#endif /* PHOTOS_TRACKER_CHANGE_MONITOR_H */ +diff --git a/src/photos-tracker-controller.c b/src/photos-tracker-controller.c +index 3a62cf2d..f0b81e02 100644 +--- a/src/photos-tracker-controller.c ++++ b/src/photos-tracker-controller.c +@@ -292,6 +292,12 @@ photos_tracker_controller_perform_current_query (PhotosTrackerController *self) + + priv = photos_tracker_controller_get_instance_private (self); + ++ if (G_UNLIKELY (priv->queue == NULL)) ++ { ++ photos_tracker_controller_query_error (self, priv->queue_error); ++ goto out; ++ } ++ + g_clear_object (&priv->current_query); + priv->current_query = PHOTOS_TRACKER_CONTROLLER_GET_CLASS (self)->get_query (self); + g_return_if_fail (priv->current_query != NULL); +@@ -303,12 +309,6 @@ photos_tracker_controller_perform_current_query (PhotosTrackerController *self) + g_object_unref (priv->cancellable); + priv->cancellable = g_cancellable_new (); + +- if (G_UNLIKELY (priv->queue == NULL)) +- { +- photos_tracker_controller_query_error (self, priv->queue_error); +- goto out; +- } +- + photos_tracker_queue_select (priv->queue, + priv->current_query, + priv->cancellable, +diff --git a/src/photos-tracker-extract-priority.xml b/src/photos-tracker-extract-priority.xml +index 7e3b9b0c..2b562299 100644 +--- a/src/photos-tracker-extract-priority.xml ++++ b/src/photos-tracker-extract-priority.xml +@@ -21,7 +21,7 @@ + --> + + +- ++ + + + +diff --git a/src/photos-tracker-import-controller.c b/src/photos-tracker-import-controller.c +index 085ace3a..d7ed9b20 100644 +--- a/src/photos-tracker-import-controller.c ++++ b/src/photos-tracker-import-controller.c +@@ -24,7 +24,6 @@ + #include "config.h" + + #include +-#include + + #include "photos-base-manager.h" + #include "photos-debug.h" +@@ -34,6 +33,8 @@ + #include "photos-query-builder.h" + #include "photos-search-context.h" + #include "photos-tracker-import-controller.h" ++#include "photos-tracker-miner-index.h" ++#include "photos-tracker-queue.h" + #include "photos-utils.h" + + +@@ -45,7 +46,7 @@ struct _PhotosTrackerImportController + PhotosBaseManager *item_mngr; + PhotosBaseManager *src_mngr; + PhotosOffsetController *offset_cntrlr; +- TrackerMinerManager *manager; ++ TrackerMinerFilesIndex *miner_control_proxy; + }; + + +@@ -76,12 +77,12 @@ static void + photos_tracker_import_controller_index (GObject *source_object, GAsyncResult *res, gpointer user_data) + { + g_autoptr (GFile) file = G_FILE (user_data); +- TrackerMinerManager *manager = TRACKER_MINER_MANAGER (source_object); ++ TrackerMinerFilesIndex *miner_control_proxy = TRACKER_MINER_FILES_INDEX (source_object); + + { + g_autoptr (GError) error = NULL; + +- if (!tracker_miner_manager_index_file_for_process_finish (manager, res, &error)) ++ if (!tracker_miner_files_index_call_index_location_finish (miner_control_proxy, res, &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { +@@ -102,6 +103,8 @@ photos_tracker_import_controller_next_files (GObject *source_object, GAsyncResul + GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object); + GList *infos = NULL; + GList *l; ++ const gchar *tracker_priority_graphs[] = { TRACKER_PICTURES_GRAPH }; ++ const gchar *tracker_index_location_flags[] = { "for-process" }; + + { + g_autoptr (GError) error = NULL; +@@ -187,11 +190,13 @@ photos_tracker_import_controller_next_files (GObject *source_object, GAsyncResul + if (g_content_type_equals (mime_type, IMPORTABLE_MIME_TYPES[i]) + || g_content_type_is_a (mime_type, IMPORTABLE_MIME_TYPES[i])) + { +- tracker_miner_manager_index_file_for_process_async (self->manager, +- file, +- self->cancellable, +- photos_tracker_import_controller_index, +- g_object_ref (file)); ++ tracker_miner_files_index_call_index_location (self->miner_control_proxy, ++ uri, ++ tracker_priority_graphs, ++ tracker_index_location_flags, ++ self->cancellable, ++ photos_tracker_import_controller_index, ++ g_object_ref (file)); + indexing = TRUE; + } + } +@@ -291,28 +296,6 @@ photos_tracker_import_controller_source_active_changed (PhotosTrackerImportContr + { + g_return_if_fail (g_queue_is_empty (self->pending_directories)); + +- if (G_LIKELY (self->manager != NULL)) +- { +- g_autoptr (GFile) root = NULL; +- g_autofree gchar *uri = NULL; +- +- root = g_mount_get_root (mount); +- g_queue_push_tail (self->pending_directories, g_object_ref (root)); +- +- uri = g_file_get_uri (root); +- photos_debug (PHOTOS_DEBUG_IMPORT, "Enumerating device directory %s", uri); +- +- g_file_enumerate_children_async (root, +- G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE"," +- G_FILE_ATTRIBUTE_STANDARD_NAME"," +- G_FILE_ATTRIBUTE_STANDARD_TYPE, +- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, +- G_PRIORITY_DEFAULT, +- self->cancellable, +- photos_tracker_import_controller_enumerate_children, +- self); +- } +- + photos_tracker_controller_refresh_for_object (PHOTOS_TRACKER_CONTROLLER (self)); + } + } +@@ -379,7 +362,6 @@ photos_tracker_import_controller_dispose (GObject *object) + + g_clear_object (&self->src_mngr); + g_clear_object (&self->offset_cntrlr); +- g_clear_object (&self->manager); + + G_OBJECT_CLASS (photos_tracker_import_controller_parent_class)->dispose (object); + } +@@ -397,6 +379,37 @@ photos_tracker_import_controller_finalize (GObject *object) + } + + ++static TrackerMinerFilesIndex * ++photos_tracker_import_controller_get_miner_fs_control_proxy (GCancellable *cancellable) ++{ ++ g_autoptr (PhotosTrackerQueue) tracker_queue = NULL; ++ const gchar *miner_fs_control_busname; ++ g_autoptr (GError) error = NULL; ++ TrackerMinerFilesIndex *miner_control_proxy; ++ ++ tracker_queue = photos_tracker_queue_dup_singleton (cancellable, &error); ++ ++ if (!tracker_queue) { ++ g_warning ("Error getting Tracker queue: %s. Import will not work.", error->message); ++ return NULL; ++ } ++ ++ miner_fs_control_busname = photos_tracker_queue_get_miner_fs_control_busname (tracker_queue); ++ miner_control_proxy = tracker_miner_files_index_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_NONE, ++ miner_fs_control_busname, ++ "/org/freedesktop/Tracker3/Miner/Files/Control", ++ NULL, ++ &error); ++ if (!miner_control_proxy) { ++ g_warning ("Error getting Tracker Miner FS control proxy: %s. Import will not work", error->message); ++ return NULL; ++ } ++ ++ return miner_control_proxy; ++} ++ ++ + static void + photos_tracker_import_controller_init (PhotosTrackerImportController *self) + { +@@ -421,13 +434,7 @@ photos_tracker_import_controller_init (PhotosTrackerImportController *self) + + self->offset_cntrlr = photos_offset_import_controller_dup_singleton (); + +- { +- g_autoptr (GError) error = NULL; +- +- self->manager = tracker_miner_manager_new_full (FALSE, &error); +- if (error != NULL) +- g_warning ("Unable to create a TrackerMinerManager, indexing attached devices won't work: %s", error->message); +- } ++ self->miner_control_proxy = photos_tracker_import_controller_get_miner_fs_control_proxy (NULL); + } + + +diff --git a/src/photos-tracker-queue.c b/src/photos-tracker-queue.c +index 6ac829cd..a5f4c91a 100644 +--- a/src/photos-tracker-queue.c ++++ b/src/photos-tracker-queue.c +@@ -37,9 +37,20 @@ struct _PhotosTrackerQueue + GObject parent_instance; + GError *initialization_error; + GQueue *queue; +- TrackerSparqlConnection *connection; ++ TrackerSparqlConnection *local_connection; ++ TrackerSparqlConnection *miner_fs_connection; + gboolean is_initialized; + gboolean running; ++ gboolean miner_fs_ready; ++ const gchar *miner_fs_busname; ++ const gchar *miner_fs_control_busname; ++}; ++ ++enum ++{ ++ PROP_0, ++ PROP_MINER_FS_READY, ++ PROP_MINER_FS_BUSNAME + }; + + static void photos_tracker_queue_initable_iface_init (GInitableIface *iface); +@@ -90,7 +101,6 @@ photos_tracker_queue_data_free (PhotosTrackerQueueData *data) + + G_DEFINE_AUTOPTR_CLEANUP_FUNC (PhotosTrackerQueueData, photos_tracker_queue_data_free); + +- + static PhotosTrackerQueueData * + photos_tracker_queue_data_new (PhotosQuery *query, + PhotosTrackerQueryType query_type, +@@ -179,7 +189,7 @@ photos_tracker_queue_check (PhotosTrackerQueue *self) + switch (data->query_type) + { + case PHOTOS_TRACKER_QUERY_SELECT: +- tracker_sparql_connection_query_async (self->connection, ++ tracker_sparql_connection_query_async (self->local_connection, + sparql, + data->cancellable, + photos_tracker_queue_collector, +@@ -187,18 +197,16 @@ photos_tracker_queue_check (PhotosTrackerQueue *self) + break; + + case PHOTOS_TRACKER_QUERY_UPDATE: +- tracker_sparql_connection_update_async (self->connection, ++ tracker_sparql_connection_update_async (self->local_connection, + sparql, +- G_PRIORITY_DEFAULT, + data->cancellable, + photos_tracker_queue_collector, + g_object_ref (self)); + break; + + case PHOTOS_TRACKER_QUERY_UPDATE_BLANK: +- tracker_sparql_connection_update_blank_async (self->connection, ++ tracker_sparql_connection_update_blank_async (self->local_connection, + sparql, +- G_PRIORITY_DEFAULT, + data->cancellable, + photos_tracker_queue_collector, + g_object_ref (self)); +@@ -211,6 +219,53 @@ photos_tracker_queue_check (PhotosTrackerQueue *self) + } + + ++static gboolean ++photos_tracker_queue_start_session_miner_fs (PhotosTrackerQueue *self, GError **error) ++{ ++ const gchar *busname = "org.freedesktop.Tracker3.Miner.Files"; ++ const gchar *control_busname = "org.freedesktop.Tracker3.Miner.Files.Control"; ++ ++ photos_debug (PHOTOS_DEBUG_TRACKER, "Connecting to %s", busname); ++ self->miner_fs_connection = tracker_sparql_connection_bus_new (busname, NULL, NULL, error); ++ if (*error) ++ { ++ g_warning ("Unable to create connection for session-wide Tracker indexer at %s: %s", busname, (*error)->message); ++ return FALSE; ++ } ++ ++ self->miner_fs_busname = busname; ++ self->miner_fs_control_busname = control_busname; ++ ++ return TRUE; ++} ++ ++ ++static gboolean ++photos_tracker_queue_start_local_miner_fs (PhotosTrackerQueue *self, GError **error) ++{ ++ const gchar *busname = "org.gnome.Photos.Tracker3.Miner.Files"; ++ const gchar *control_busname = "org.gnome.Photos.Tracker3.Miner.Files.Control"; ++ ++ photos_debug (PHOTOS_DEBUG_TRACKER, "Connecting to %s", busname); ++ self->miner_fs_connection = tracker_sparql_connection_bus_new (busname, NULL, NULL, error); ++ if (*error) ++ { ++ g_critical ("Could not start local Tracker indexer at %s: %s", busname, (*error)->message); ++ return FALSE; ++ } ++ ++ self->miner_fs_busname = busname; ++ self->miner_fs_control_busname = control_busname; ++ ++ return TRUE; ++} ++ ++static gboolean ++inside_flatpak (void) ++{ ++ return g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS); ++} ++ + static GObject * + photos_tracker_queue_constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params) + { +@@ -234,7 +289,8 @@ photos_tracker_queue_dispose (GObject *object) + { + PhotosTrackerQueue *self = PHOTOS_TRACKER_QUEUE (object); + +- g_clear_object (&self->connection); ++ g_clear_object (&self->local_connection); ++ g_clear_object (&self->miner_fs_connection); + + G_OBJECT_CLASS (photos_tracker_queue_parent_class)->dispose (object); + } +@@ -258,6 +314,38 @@ photos_tracker_queue_init (PhotosTrackerQueue *self) + self->queue = g_queue_new (); + } + ++static void ++photos_tracker_queue_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) ++{ ++ PhotosTrackerQueue *self = PHOTOS_TRACKER_QUEUE (object); ++ ++ switch (prop_id) ++ { ++ case PROP_MINER_FS_READY: ++ g_value_set_boolean (value, self->miner_fs_ready); ++ break; ++ ++ case PROP_MINER_FS_BUSNAME: ++ g_value_set_string (value, self->miner_fs_busname); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++photos_tracker_queue_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) ++{ ++ switch (prop_id) ++ { ++ /* All properties are read only */ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} + + static void + photos_tracker_queue_class_init (PhotosTrackerQueueClass *class) +@@ -267,6 +355,24 @@ photos_tracker_queue_class_init (PhotosTrackerQueueClass *class) + object_class->constructor = photos_tracker_queue_constructor; + object_class->dispose = photos_tracker_queue_dispose; + object_class->finalize = photos_tracker_queue_finalize; ++ object_class->get_property = photos_tracker_queue_get_property; ++ object_class->set_property = photos_tracker_queue_set_property; ++ ++ g_object_class_install_property (object_class, ++ PROP_MINER_FS_READY, ++ g_param_spec_boolean ("tracker-miner-fs-ready", ++ "Tracker Miner FS ready", ++ "TRUE if it is possible to query Tracker indexer", ++ FALSE, ++ G_PARAM_READABLE)); ++ ++ g_object_class_install_property (object_class, ++ PROP_MINER_FS_BUSNAME, ++ g_param_spec_string ("tracker-miner-fs-busname", ++ "Tracker Miner FS busname", ++ "D-Bus name of the Tracker indexer daemon", ++ "", ++ G_PARAM_READABLE)); + } + + +@@ -274,13 +380,16 @@ static gboolean + photos_tracker_queue_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) + { + PhotosTrackerQueue *self = PHOTOS_TRACKER_QUEUE (initable); ++ TrackerSparqlConnectionFlags tracker_flags; ++ g_autoptr (GFile) store = NULL; + gboolean ret_val = FALSE; ++ gboolean miner_ok = FALSE; + + G_LOCK (init_lock); + + if (self->is_initialized) + { +- if (self->connection != NULL) ++ if (self->local_connection != NULL && self->miner_fs_connection != NULL) + ret_val = TRUE; + else + g_assert_nonnull (self->initialization_error); +@@ -290,11 +399,49 @@ photos_tracker_queue_initable_init (GInitable *initable, GCancellable *cancellab + + g_assert_no_error (self->initialization_error); + +- self->connection = tracker_sparql_connection_get (cancellable, &self->initialization_error); ++ /* Connect to the local database which stores user data. ++ * ++ * Same flags that tracker-miner-fs uses by default. See: ++ * https://gitlab.gnome.org/GNOME/tracker-miners/-/blob/master/src/miners/fs/tracker-main.c#L735 and ++ * https://gitlab.gnome.org/GNOME/tracker-miners/-/blob/master/data/org.freedesktop.Tracker.FTS.gschema.xml */ ++ tracker_flags = TRACKER_SPARQL_CONNECTION_FLAGS_FTS_ENABLE_UNACCENT | ++ TRACKER_SPARQL_CONNECTION_FLAGS_FTS_ENABLE_STOP_WORDS | ++ TRACKER_SPARQL_CONNECTION_FLAGS_FTS_IGNORE_NUMBERS; ++ ++ store = g_file_new_build_filename (g_get_user_data_dir (), "gnome-photos", NULL); ++ ++ photos_debug (PHOTOS_DEBUG_TRACKER, "Opening local database at %s", g_file_peek_path (store)); ++ self->local_connection = tracker_sparql_connection_new (tracker_flags, ++ store, ++ tracker_sparql_get_ontology_nepomuk (), ++ cancellable, ++ &self->initialization_error); ++ + if (G_UNLIKELY (self->initialization_error != NULL)) + goto out; + +- ret_val = TRUE; ++ /* Connect to filesystem indexer. */ ++ miner_ok = photos_tracker_queue_start_session_miner_fs (self, &self->initialization_error); ++ ++ if (!miner_ok && inside_flatpak ()) ++ { ++ g_clear_error (&self->initialization_error); ++ miner_ok = photos_tracker_queue_start_local_miner_fs (self, &self->initialization_error); ++ } ++ ++ if (miner_ok) ++ { ++ photos_debug (PHOTOS_DEBUG_TRACKER, "Using %s as tracker-miner-fs", self->miner_fs_busname); ++ ret_val = TRUE; ++ } ++ else ++ { ++ photos_debug (PHOTOS_DEBUG_TRACKER, "Initialization failed due to %s", self->initialization_error->message); ++ g_clear_object (&self->miner_fs_connection); ++ g_clear_object (&self->local_connection); ++ ret_val = FALSE; ++ } ++ + + out: + self->is_initialized = TRUE; +@@ -319,10 +466,44 @@ photos_tracker_queue_initable_iface_init (GInitableIface *iface) + PhotosTrackerQueue * + photos_tracker_queue_dup_singleton (GCancellable *cancellable, GError **error) + { ++ GObject *singleton; ++ + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + +- return g_initable_new (PHOTOS_TYPE_TRACKER_QUEUE, cancellable, error, NULL); ++ singleton = g_object_new (PHOTOS_TYPE_TRACKER_QUEUE, NULL); ++ ++ if (g_initable_init (G_INITABLE (singleton), cancellable, error)) ++ return PHOTOS_TRACKER_QUEUE (singleton); ++ ++ /* On error we deliberately don't unref the object so that we won't try ++ * and re-initialize Tracker when called again. ++ */ ++ return NULL; ++} ++ ++ ++const gchar * ++photos_tracker_queue_get_miner_fs_busname (PhotosTrackerQueue *self) ++{ ++ return self->miner_fs_busname; ++} ++ ++ ++const gchar * ++photos_tracker_queue_get_miner_fs_control_busname (PhotosTrackerQueue *self) ++{ ++ return self->miner_fs_control_busname; ++} ++ ++ ++TrackerNotifier * ++photos_tracker_queue_get_notifier (PhotosTrackerQueue *self) ++{ ++ /* We want notifications on filesystem changes, we don't need them for the ++ * local database which we manage ourselves. ++ */ ++ return tracker_sparql_connection_create_notifier (self->miner_fs_connection); + } + + +@@ -351,6 +532,7 @@ photos_tracker_queue_select (PhotosTrackerQueue *self, + } + + ++ + void + photos_tracker_queue_update (PhotosTrackerQueue *self, + PhotosQuery *query, +diff --git a/src/photos-tracker-queue.h b/src/photos-tracker-queue.h +index 93d97306..6e31ecf6 100644 +--- a/src/photos-tracker-queue.h ++++ b/src/photos-tracker-queue.h +@@ -24,16 +24,23 @@ + #define PHOTOS_TRACKER_QUEUE_H + + #include ++#include + + #include "photos-query.h" + + G_BEGIN_DECLS + ++#define TRACKER_PICTURES_GRAPH "http://tracker.api.gnome.org/ontology/v3/tracker#Pictures" ++ + #define PHOTOS_TYPE_TRACKER_QUEUE (photos_tracker_queue_get_type ()) + G_DECLARE_FINAL_TYPE (PhotosTrackerQueue, photos_tracker_queue, PHOTOS, TRACKER_QUEUE, GObject); + + PhotosTrackerQueue *photos_tracker_queue_dup_singleton (GCancellable *cancellable, GError **error); + ++const gchar * photos_tracker_queue_get_miner_fs_busname (PhotosTrackerQueue *self); ++const gchar * photos_tracker_queue_get_miner_fs_control_busname (PhotosTrackerQueue *self); ++TrackerNotifier * photos_tracker_queue_get_notifier (PhotosTrackerQueue *self); ++ + void photos_tracker_queue_select (PhotosTrackerQueue *self, + PhotosQuery *query, + GCancellable *cancellable, +diff --git a/src/photos-tracker-resources.xml b/src/photos-tracker-resources.xml +deleted file mode 100644 +index 18177aa0..00000000 +--- a/src/photos-tracker-resources.xml ++++ /dev/null +@@ -1,31 +0,0 @@ +- +- +- +- +- +- +- +- +- +- +- +- +- +diff --git a/src/photos-utils.c b/src/photos-utils.c +index c638297b..cc8593d8 100644 +--- a/src/photos-utils.c ++++ b/src/photos-utils.c +@@ -1261,7 +1261,13 @@ photos_utils_set_edited_name (const gchar *urn, const gchar *title) + g_autoptr (PhotosTrackerQueue) queue = NULL; + g_autofree gchar *sparql = NULL; + +- sparql = g_strdup_printf ("INSERT OR REPLACE { <%s> nie:title \"%s\" }", urn, title); ++ sparql = g_strdup_printf ("WITH tracker:Pictures " ++ "DELETE { <%s> nie:title ?title } " ++ "INSERT { " ++ " <%s> a nmm:Photo ; " ++ " nie:title \"%s\" . " ++ "}" ++ "WHERE { <%s> nie:title ?title }", urn, urn, title, urn); + query = photos_query_new (NULL, sparql); + + { +@@ -1289,9 +1295,19 @@ photos_utils_set_favorite (const gchar *urn, gboolean is_favorite) + g_autoptr (PhotosTrackerQueue) queue = NULL; + g_autofree gchar *sparql = NULL; + +- sparql = g_strdup_printf ("%s { <%s> nao:hasTag nao:predefined-tag-favorite }", +- (is_favorite) ? "INSERT OR REPLACE" : "DELETE", +- urn); ++ if (is_favorite) ++ sparql = g_strdup_printf ("INSERT DATA { " ++ " GRAPH tracker:Pictures {" ++ " <%s> a nmm:Photo ; " ++ " nao:hasTag nao:predefined-tag-favorite " ++ " } " ++ "}", urn); ++ else ++ sparql = g_strdup_printf ("DELETE DATA {" ++ " GRAPH tracker:Pictures {" ++ " <%s> nao:hasTag nao:predefined-tag-favorite " ++ " } " ++ "}", urn); + query = photos_query_new (NULL, sparql); + + { +diff --git a/src/photos-utils.h b/src/photos-utils.h +index 78ec3668..f1450f7d 100644 +--- a/src/photos-utils.h ++++ b/src/photos-utils.h +@@ -46,7 +46,7 @@ G_BEGIN_DECLS + #define PHOTOS_TRACKER_CONTROLLER_EXTENSION_POINT_NAME "photos-tracker-controller" + + #define PHOTOS_COLLECTION_SCREENSHOT \ +- "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#image-category-screenshot" ++ "http://tracker.api.gnome.org/ontology/v3/nfo#image-category-screenshot" + #define PHOTOS_EXPORT_SUBPATH "Exports" + + typedef enum +diff --git a/src/queries/all.sparql.template b/src/queries/all.sparql.template +index 1cef98e8..14e08027 100644 +--- a/src/queries/all.sparql.template ++++ b/src/queries/all.sparql.template +@@ -1,30 +1,42 @@ +-SELECT {{projection}} ++SELECT {{final_projection}} ++FROM NAMED tracker:Pictures + { + { +- SELECT {{projection}} ++ SELECT {{main_projection}} + { + { + SELECT ?urn COUNT(?item) AS ?count + { ++ {{values}} ++ VALUES (?file ?filename ?creator ?publisher) { ("" "" "" "") } + ?urn a nfo:DataContainer. +- ?item a nmm:Photo; nie:isPartOf ?urn. +- } GROUP BY ?urn ++ ?item a nmm:Photo ; ++ nie:isPartOf ?urn . ++ {{item_pattern}} ++ } ++ GROUP BY ?urn + } + FILTER (?count > 0 && {{collections_default_filter}} && {{search_filter}}) + } +- GROUP BY ?urn + } + UNION + { +- SELECT {{projection}} ++ SELECT {{second_projection}} + { +- ?urn a nmm:Photo . +- OPTIONAL { ?urn nco:creator ?creator . } +- OPTIONAL { ?urn nco:publisher ?publisher . } + {{item_pattern}} +- FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++ SERVICE ++ { ++ SELECT {{main_projection}} ++ { ++ {{values}} ++ ?urn a nmm:Photo ; ++ nie:isStoredAs ?file . ++ OPTIONAL { ?urn nco:creator ?creator . } ++ OPTIONAL { ?urn nco:publisher ?publisher . } ++ FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++ } ++ } + } +- GROUP BY ?urn + } + } + {{order}} +diff --git a/src/queries/collections.sparql.template b/src/queries/collections.sparql.template +index 20b35cd6..4dfb345a 100644 +--- a/src/queries/collections.sparql.template ++++ b/src/queries/collections.sparql.template +@@ -1,14 +1,20 @@ +-SELECT {{projection}} ++SELECT {{final_projection}} + { ++ SELECT {{main_projection}} + { +- SELECT ?urn COUNT(?item) AS ?count + { +- ?urn a nfo:DataContainer. +- ?item a nmm:Photo; nie:isPartOf ?urn. +- } GROUP BY ?urn ++ SELECT ?urn COUNT(?item) AS ?count ++ { ++ {{values}} ++ VALUES (?file ?filename ?creator ?publisher) { ("" "" "" "") } ++ ?urn a nfo:DataContainer . ++ ?item a nmm:Photo ; ++ nie:isPartOf ?urn . ++ } ++ GROUP BY ?urn ++ } ++ FILTER (?count > 0 && {{collections_default_filter}} && {{search_filter}}) + } +- FILTER (?count > 0 && {{collections_default_filter}} && {{search_filter}}) + } +-GROUP BY ?urn + {{order}} + {{offset_limit}} +diff --git a/src/queries/favorite-photos.sparql.template b/src/queries/favorite-photos.sparql.template +index 0885a08a..9a0d7806 100644 +--- a/src/queries/favorite-photos.sparql.template ++++ b/src/queries/favorite-photos.sparql.template +@@ -1,12 +1,20 @@ +-SELECT {{projection}} ++SELECT {{final_projection}} ++FROM tracker:Pictures + { + ?urn a nmm:Photo . + ?urn a nmm:Photo; nao:hasTag nao:predefined-tag-favorite . +- OPTIONAL { ?urn nco:creator ?creator . } +- OPTIONAL { ?urn nco:publisher ?publisher . } +- {{item_pattern}} +- FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++ SERVICE ++ { ++ SELECT {{main_projection}} ++ { ++ ?urn a nmm:Photo ; ++ nie:isStoredAs ?file . ++ OPTIONAL { ?urn nco:creator ?creator . } ++ OPTIONAL { ?urn nco:publisher ?publisher . } ++ {{item_pattern}} ++ FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++ } ++ } + } +-GROUP BY ?urn + {{order}} + {{offset_limit}} +diff --git a/src/queries/photos.sparql.template b/src/queries/photos.sparql.template +index 4eb10b74..6ade35e2 100644 +--- a/src/queries/photos.sparql.template ++++ b/src/queries/photos.sparql.template +@@ -1,11 +1,19 @@ +-SELECT {{projection}} ++SELECT {{final_projection}} ++FROM tracker:Pictures + { +- ?urn a nmm:Photo . +- OPTIONAL { ?urn nco:creator ?creator . } +- OPTIONAL { ?urn nco:publisher ?publisher . } +- {{item_pattern}} +- FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++ SERVICE ++ { ++ SELECT {{main_projection}} ++ { ++ {{values}} ++ ?urn a nmm:Photo ; ++ nie:isStoredAs ?file . ++ OPTIONAL { ?urn nco:creator ?creator . } ++ OPTIONAL { ?urn nco:publisher ?publisher . } ++ {{item_pattern}} ++ FILTER ({{photos_default_filter}} && {{source_filter}} && {{search_filter}}) ++ } ++ } + } +-GROUP BY ?urn + {{order}} + {{offset_limit}} +-- +GitLab + diff --git a/srcpkgs/gnome-photos/template b/srcpkgs/gnome-photos/template index 1ed8b9861e..7c83a2e75d 100644 --- a/srcpkgs/gnome-photos/template +++ b/srcpkgs/gnome-photos/template @@ -1,22 +1,23 @@ # Template file for 'gnome-photos' pkgname=gnome-photos -version=3.34.2 +version=3.37.91.1 revision=1 build_helper="gir" build_style=meson configure_args="-Ddogtail=false" hostmakedepends="pkg-config gettext itstool glib-devel gdk-pixbuf librsvg" makedepends="gtk+3-devel babl-devel exempi-devel lcms2-devel - gfbgraph-devel tracker-devel libexif-devel librsvg-devel grilo-devel + gfbgraph-devel tracker3-devel libexif-devel librsvg-devel grilo-devel libgexiv2-devel gnome-online-accounts-devel gnome-desktop-devel libgdata-devel geocode-glib-devel libdazzle-devel gegl-devel" -depends="desktop-file-utils tracker tracker-miners" +depends="desktop-file-utils tracker3 tracker3-miners" short_desc="Access, organize, and share your photos on GNOME" maintainer="Enno Boland " license="GPL-2.0-or-later" homepage="https://wiki.gnome.org/Apps/Photos" -distfiles="${GNOME_SITE}/gnome-photos/${version%.*}/gnome-photos-${version}.tar.xz" -checksum=3c59c76ef28618ec055a1799d1040287b90a0b021feb0a02b1eac28e9c2eb41a +distfiles="${GNOME_SITE}/gnome-photos/${version%.*.*}/gnome-photos-${version}.tar.xz" +checksum=278da23234e27c543020dae943ad52086b9ecaa5ee3aad0b031b86ee04d394aa +patch_args="-Np1" build_options="gir" build_options_default="gir"