From 62c75d289f21223902bcdb61b8fb51fce9830bf6 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 3 May 2022 12:50:21 -0400
Subject: [PATCH] wip! item-manager: Handle collection COLUMNS_URNs with
 stripped prefix

For reasons I don't understand, tracker sometimes returns
blank node urns without their qualifiers (so "82" instead of "urn:bnode:82")

That causes duplicate entries to get added to the collection model, and
makes the album view get confused.

This commit tries to accomodate the oddity by manually adding the
prefix back.

https://gitlab.gnome.org/GNOME/gnome-photos/-/issues/197
---
 src/photos-base-item.c    | 16 ++++++++++++----
 src/photos-item-manager.c | 15 +++++++++++++++
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/src/photos-base-item.c b/src/photos-base-item.c
index 3b20bc2e..eef0bf66 100644
--- a/src/photos-base-item.c
+++ b/src/photos-base-item.c
@@ -2780,98 +2780,106 @@ photos_base_item_update_info_from_type (PhotosBaseItem *self)
 
   if (strstr (priv->rdf_type, "nfo#DataContainer") != NULL)
     priv->collection = TRUE;
 
   PHOTOS_BASE_ITEM_GET_CLASS (self)->update_type_description (self);
 }
 
 
 static void
 photos_base_item_populate_from_cursor (PhotosBaseItem *self, TrackerSparqlCursor *cursor)
 {
   PhotosBaseItemPrivate *priv;
   gboolean favorite;
   const gchar *author;
   const gchar *ctime;
   const gchar *equipment;
   const gchar *flash;
   const gchar *id;
   const gchar *identifier;
   const gchar *location;
   const gchar *mime_type;
   const gchar *orientation;
   const gchar *rdf_type;
   const gchar *resource_urn;
   const gchar *title;
   const gchar *uri;
   gchar *filename;
   gchar *name_fallback;
   gint64 height;
   gint64 width;
+  g_autofree char *bnode_id = NULL;
 
   priv = photos_base_item_get_instance_private (self);
 
   uri = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URI, NULL);
   if (uri == NULL)
     uri = "";
   photos_utils_set_string (&priv->uri, uri);
   g_object_notify (G_OBJECT (self), "uri");
 
+  rdf_type = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_RDF_TYPE, NULL);
+  photos_utils_set_string (&priv->rdf_type, rdf_type);
+
+  photos_base_item_update_info_from_type (self);
+
   id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
+
+  if (priv->collection && !g_str_has_prefix (id, "urn:bnode:"))
+    {
+      bnode_id = g_strdup_printf ("urn:bnode:%s", id);
+      id = bnode_id;
+    }
   photos_utils_set_string (&priv->id, id);
   g_object_notify (G_OBJECT (self), "id");
 
   identifier = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_IDENTIFIER, NULL);
   photos_utils_set_string (&priv->identifier, identifier);
 
   author = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_AUTHOR, NULL);
   photos_utils_set_string (&priv->author, author);
   g_object_notify (G_OBJECT (self), "secondary-text");
 
   location = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_LOCATION, NULL);
   photos_utils_set_string (&priv->location, location);
 
   resource_urn = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_RESOURCE_URN, NULL);
   photos_utils_set_string (&priv->resource_urn, resource_urn);
 
   favorite = tracker_sparql_cursor_get_boolean (cursor, PHOTOS_QUERY_COLUMNS_RESOURCE_FAVORITE);
 
   priv->mtime = photos_utils_get_mtime_from_sparql_cursor (cursor);
   g_object_notify (G_OBJECT (self), "mtime");
 
   mime_type = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_MIME_TYPE, NULL);
   photos_utils_set_string (&priv->mime_type, mime_type);
 
-  rdf_type = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_RDF_TYPE, NULL);
-  photos_utils_set_string (&priv->rdf_type, rdf_type);
-
-  photos_base_item_update_info_from_type (self);
   priv->favorite = favorite && !priv->collection;
 
   priv->ctime = -1;
   ctime = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_DATE_CREATED, NULL);
   if (ctime != NULL)
     {
       g_autoptr (GDateTime) date_created = NULL;
 
       date_created = g_date_time_new_from_iso8601 (ctime, NULL);
       if (date_created != NULL)
         priv->ctime = g_date_time_to_unix (date_created);
     }
 
   if (g_strcmp0 (priv->id, PHOTOS_COLLECTION_SCREENSHOT) == 0)
     title = _("Screenshots");
   else
     title = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_TITLE, NULL);
 
   if (title == NULL)
     title = "";
   photos_utils_set_string (&priv->name, title);
   g_object_notify (G_OBJECT (self), "primary-text");
 
   filename = g_strdup (tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_FILENAME, NULL));
   if ((filename == NULL || filename[0] == '\0') && !priv->collection)
     filename = PHOTOS_BASE_ITEM_GET_CLASS (self)->create_filename_fallback (self);
   photos_utils_take_string (&priv->filename, filename);
 
   equipment = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_EQUIPMENT, NULL);
   priv->equipment = g_quark_from_string (equipment);
diff --git a/src/photos-item-manager.c b/src/photos-item-manager.c
index 73ccaaa6..30a31769 100644
--- a/src/photos-item-manager.c
+++ b/src/photos-item-manager.c
@@ -278,81 +278,88 @@ photos_item_manager_info_updated (PhotosBaseItem *item, gpointer user_data)
     {
       photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_COLLECTION_VIEW],
                                          G_OBJECT (item));
       photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_FAVORITES], G_OBJECT (item));
       photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_OVERVIEW], G_OBJECT (item));
     }
   else
     {
       photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_COLLECTIONS], G_OBJECT (item));
 
       if (!is_favorite)
         photos_base_manager_remove_object (self->item_mngr_chldrn[PHOTOS_WINDOW_MODE_FAVORITES], G_OBJECT (item));
     }
 
  out:
   return;
 }
 
 
 static void
 photos_item_manager_add_cursor_for_mode (PhotosItemManager *self,
                                          GType base_item_type,
                                          TrackerSparqlCursor *cursor,
                                          PhotosWindowMode mode,
                                          gboolean force)
 {
   g_autoptr (PhotosBaseItem) item = NULL;
   PhotosBaseManager *item_mngr_chld;
   gboolean is_collection;
   const gchar *id;
+  g_autofree char *bnode_id = NULL;
 
   g_return_if_fail (PHOTOS_IS_ITEM_MANAGER (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_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);
 
   is_collection = photos_item_manager_cursor_is_collection (cursor);
   g_return_if_fail ((is_collection
                      && (mode == PHOTOS_WINDOW_MODE_COLLECTIONS || mode == PHOTOS_WINDOW_MODE_SEARCH))
                     || (!is_collection && mode != PHOTOS_WINDOW_MODE_COLLECTIONS));
 
   if (!force && !photos_item_manager_can_add_cursor_for_mode (self, cursor, mode))
     goto out;
 
   item_mngr_chld = self->item_mngr_chldrn[mode];
   id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
 
+  if (mode == PHOTOS_WINDOW_MODE_COLLECTIONS && !g_str_has_prefix (id, "urn:bnode:"))
+    {
+      bnode_id = g_strdup_printf ("urn:bnode:%s", id);
+      id = bnode_id;
+    }
+
   item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (item_mngr_chld, id));
   if (item != NULL)
     {
       g_object_ref (item);
     }
   else
     {
       gboolean already_present = FALSE;
 
       item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), id));
       if (item != NULL)
         {
           g_object_ref (item);
           already_present = TRUE;
         }
       else
         {
           item = photos_item_manager_create_item (self, base_item_type, cursor, TRUE);
           if (photos_base_item_is_collection (item))
             g_hash_table_insert (self->collections, g_strdup (id), g_object_ref (item));
 
           g_signal_connect_object (item, "info-updated", G_CALLBACK (photos_item_manager_info_updated), self, 0);
         }
 
       photos_base_manager_add_object (item_mngr_chld, G_OBJECT (item));
       photos_base_manager_add_object (self->item_mngr_chldrn[0], G_OBJECT (item));
 
       if (!already_present)
         g_signal_emit_by_name (self, "object-added", G_OBJECT (item));
     }
@@ -1351,68 +1358,76 @@ photos_item_manager_clear (PhotosItemManager *self, PhotosWindowMode mode)
             continue;
 
           item1 = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (self->item_mngr_chldrn[j], id));
           if (item1 != NULL)
             break;
         }
 
       if (item1 == NULL)
         {
           item1 = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (self->item_mngr_chldrn[0], id));
           g_assert_true (item == item1);
 
           g_signal_handlers_disconnect_by_func (item, photos_item_manager_info_updated, self);
           photos_base_manager_remove_object_by_id (self->item_mngr_chldrn[0], id);
         }
     }
 
   photos_base_manager_clear (item_mngr_chld);
 }
 
 
 PhotosBaseItem *
 photos_item_manager_create_item (PhotosItemManager *self,
                                  GType base_item_type,
                                  TrackerSparqlCursor *cursor,
                                  gboolean create_thumbnails)
 {
   PhotosBaseItem *item;
   PhotosBaseItem *ret_val = NULL;
   const gchar *id;
+  g_autofree char *bnode_id = NULL;
 
   g_return_val_if_fail (PHOTOS_IS_ITEM_MANAGER (self), NULL);
   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_IS_SPARQL_CURSOR (cursor), NULL);
 
   id = tracker_sparql_cursor_get_string (cursor, PHOTOS_QUERY_COLUMNS_URN, NULL);
+
+  if (photos_item_manager_cursor_is_collection (cursor) && !g_str_has_prefix (id, "urn:bnode:"))
+    {
+      bnode_id = g_strdup_printf ("urn:bnode:%s", id);
+      id = bnode_id;
+    }
+
   item = PHOTOS_BASE_ITEM (photos_base_manager_get_object_by_id (PHOTOS_BASE_MANAGER (self), id));
   if (item != NULL)
     {
       ret_val = g_object_ref (item);
     }
   else
     {
       GType type;
 
       if (base_item_type == G_TYPE_NONE)
         {
           GIOExtension *extension;
           g_auto (GStrv) split_identifier = NULL;
           const gchar *extension_name = "local";
           g_autofree gchar *identifier = NULL;
 
           identifier = g_strdup (tracker_sparql_cursor_get_string (cursor,
                                                                    PHOTOS_QUERY_COLUMNS_IDENTIFIER,
                                                                    NULL));
           if (identifier != NULL)
             {
               split_identifier = g_strsplit (identifier, ":", 4);
 
               if (photos_item_manager_cursor_is_collection (cursor))
                 {
                   /* Its a collection. */
                   extension_name = split_identifier[2];
                 }
               else
                 {
-- 
2.35.1

