gthumb support / metadata storage ideas



Hi all,

I've been using Gthumb[1] for my photo management for a few years now,
so have amassed much metadata in it's own format.

Gthumb stores metadata in compressed xml files in sub-directories named
".comments/" (a sub-directory of the images own directory).  It's rather
convenient as I can move directories around using other file management
tools and not lose the metadata.

I've been thinking about how F-spot could achieve this and I came to the
conclusion that it would be nice if it supported the gthumb format.

On import, f-spot could read any gthumb metadata it finds and whenever
metadata is changed in f-spot it could export it back out in the gthumb
format.  This would obviously be in addition to the sqlite database,
which allows for speedy searches (which Gthumb currently lacks).

The problem arises when metadata is changed in Gthumb *after* import
into F-spot.  Perhaps F-spot could keep a record of the gthumb metadata
modification times, and reimport?  Should this happen every time f-spot
is first run? (in addition to inotify events of course :) or manually
done?  How should tag lists be merged?

Should F-spot do this automatically?  or should it be an option?  Is it
outside the scope of f-spot?  The Gthumb format, whilst not a standard
of any kind, it is nice and simple.  Support for this metadata could be
added to Nautilus too.

One problem is that if the photo is moved out of the directory manually,
the metadata is still lost.  Could we start to save metadata in exif
headers?  Is this bad behaviour for an app?  Gthumb seems to have code
in cvs to start doing this (well, as I understand the code anyway).  I
can't decide whether I'd be happy for my photo management tool to be
modifying my photos; even just the headers.

Anyway, in the mean time I've hacked up a nasty patch for F-spot to read
Gthumb metadata on import.  Any new tags are created with the generic
"other" icon.  

I had a bit of trouble creating tags from within the FileImportBackend
Step routine, so I made some previously private variables public
elsewhere.  This is most likely bad form but it is currently just a
hack.  If anything, it might hilite new places that new tag creation
functions need to be available (or it might just hilite that I couldn't
figure out how to do it properly).

Also, I just manually added my Gthumb.cs file to Makefile, which I
believe is autogenerated.  Ick.

Perhaps this could lead to a metadata import class, which could be used
to support various other metadata formats on import?

There are lots of problems with this patch as you'll notice.  Lot's for
me to think about, and hopefully some people might be able to help :)

Thanks,


John.


[1] http://gthumb.sourceforge.net/
diff --git a/src/FileImportBackend.cs b/src/FileImportBackend.cs
--- a/src/FileImportBackend.cs
+++ b/src/FileImportBackend.cs
@@ -139,7 +139,19 @@ public class FileImportBackend : ImportB
 		}
 		
 		photo = store.Create (path, out thumbnail);
-
+	
+		GthumbMeta gthxml = new GthumbMeta( path );
+		foreach (string keyword in gthxml.get_keywords()) {
+			Tag tag = store.tag_store.GetByName(keyword);
+			if (tag == null) {
+				tag = store.tag_store.CreateTag(store.tag_store.RootCategory, keyword);
+				tag.StockIconName = "f-spot-other.png";
+				store.tag_store.Commit(tag);
+			}
+			photo.AddTag(tag);
+		}
+		store.Commit(photo);
+		
 		if (tags != null) {
 			foreach (Tag t in tags) {
 				photo.AddTag (t);
diff --git a/src/Gthumb.cs b/src/Gthumb.cs
new file mode 100644
--- /dev/null
+++ b/src/Gthumb.cs
@@ -0,0 +1,70 @@
+using ICSharpCode.SharpZipLib.GZip;
+using System;
+using System.IO;
+using System.Xml;
+
+public class GthumbMeta
+{
+	private string[] keywords = null;
+	private string note = null;
+	private string place = null;
+	private string time = null;
+
+	public void read_xml(string xmlfilename)
+	{
+		string lastnodename = "none";
+		FileStream xmlfile = File.OpenRead(xmlfilename);
+		Stream gth_gzxml = new GZipInputStream( xmlfile );
+		XmlTextReader r = new XmlTextReader(gth_gzxml);
+		while (r.Read())  {
+			if (r.NodeType  ==  XmlNodeType.Element)  {
+				lastnodename = r.Name.ToLower();
+			} else if (r.NodeType ==  XmlNodeType.Text) {
+				if (lastnodename == "keywords")
+					keywords = r.Value.Split(',');
+				if (lastnodename == "time")
+					time = r.Value;
+				if (lastnodename == "place")
+					place = r.Value;
+				if (lastnodename == "note")
+					place = r.Value;
+			}
+		}
+	}
+
+	private string get_xml_filename(string imgfilename)
+	{
+		string filename = System.IO.Path.GetFileName(imgfilename);
+		string filepath = System.IO.Path.GetDirectoryName(imgfilename);
+		return filepath + "/.comments/" + filename + ".xml";
+	}
+
+	public string[] get_keywords()
+	{
+		return keywords;
+	}
+
+	public string get_note()
+	{
+		return note;
+	}
+
+	public string get_place()
+	{
+		return place;
+	}
+	
+	public string get_time()
+	{
+		return time;
+	}
+
+	public GthumbMeta (string imgfilename)
+	{
+		string xmlfilename = get_xml_filename(imgfilename);
+		FileInfo finfo = new FileInfo( xmlfilename );
+		if (!finfo.Exists)
+			return;
+		read_xml(xmlfilename);
+	}
+}
diff --git a/src/Makefile b/src/Makefile
--- a/src/Makefile
+++ b/src/Makefile
@@ -325,6 +325,7 @@ F_SPOT_CSDISTFILES = \
 	$(srcdir)/CameraSelectionDialog.cs	\
 	$(srcdir)/CameraFileSelectionDialog.cs	\
 	$(srcdir)/TagSelectionDialog.cs		\
+	$(srcdir)/Gthumb.cs			\
 	$(srcdir)/main.cs
 
 ASSEMBLIES = \
diff --git a/src/PhotoStore.cs b/src/PhotoStore.cs
--- a/src/PhotoStore.cs
+++ b/src/PhotoStore.cs
@@ -410,7 +410,7 @@ public class Photo : DbItem, IComparable
 
 public class PhotoStore : DbStore {
 
-	TagStore tag_store;
+	public TagStore tag_store;
 	public static ThumbnailFactory ThumbnailFactory = new ThumbnailFactory (ThumbnailSize.Large);
 
 
diff --git a/src/TagStore.cs b/src/TagStore.cs
--- a/src/TagStore.cs
+++ b/src/TagStore.cs
@@ -399,6 +399,22 @@ public class TagStore : DbStore {
 			return LookupInCache (id) as Tag;
 	}
 	
+	public Tag GetByName (string tagname)
+	{
+		SqliteCommand command = new SqliteCommand ();
+		command.Connection = Connection;
+		command.CommandText = String.Format ("SELECT id FROM tags WHERE name like '{0}'", tagname);
+		SqliteDataReader reader = command.ExecuteReader ();
+		if (reader.Read ()) {
+			uint id = Convert.ToUInt32 (reader [0]);
+			command.Dispose ();
+			return Get(id) as Tag;
+		} else {
+			command.Dispose ();
+			return null;
+		}
+	}
+	
 	public override void Remove (DbItem item)
 	{
 		RemoveFromCache (item);

Attachment: signature.asc
Description: This is a digitally signed message part



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]