Implementing "download only new files" for import from camera



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Moin,

I'm a new f-spot user and one of the functionalities I'd absolutely
love to see would be the possibility to only download new photos when
importing directly from my camera (Kodak ZD710).

I realize that gphoto2 already has a --new option, but that needs
support from the camera, which mine apparently doesn't offer.
Implementing an equivalent function without explicit camera support
would need a local database of photos that have already been
downloaded. Which is something that gphoto2 can't offer. This is where
f-spot comes in. I figured that since f-spot already has a local
database it should be easy to implement the feature this way.

Note that this is *different* from duplicate detection through md5sums
- --which only works on already downloaded files-- in that I want to
prevent downloading files from the camera in the first place (to
prevent the download time that's associated with that).

My plan to do that without downloading each photo includes checking the
file names (which are at least unique per memory card, and since I only
have one card ...) and maybe the date the photo was taken from the exif
information (exif information can be downloaded separately from the
full file, similar to thumbnails). Maybe even combine that with the
camera's serial number.

For starters I've a patch that extends src/GPhotoCamera.cs to make it
possible to read the exif information. (It also refactors GetFile(...)
and GetPreview(...) because both are essentially only special cases of a
more generic GetFileByType(..., CameraFileType type).)

Now it's easy to get arbitrary exif data from CameraFileSelectionDialog
without downloading the file first, see print-date-taken.patch

Then comes the part that surprised me: apparently f-spot doesn't store
exif information in its photo database. Or at least I presume it
doesn't: I couldn't find a db schema documentation (I didn't look hard
for it, though) and the schema printed with ".schema" from sqlite3
photos.db doesn't look like it would include any interesting data.

So here are the questions: Did I miss something?
If not: would it be politically ok to just create a new table "import"
or similar to jot down the aforementioned pieces of information (camera
serial no, file name on camera, date taken) for all imported-from-camera
files (maybe even connect the entries in that table to the
corresponding entries in the photos table to be able to delete entries
from the import table when the corresponding photo is removed from the
catalog)?

I'd also like to hear about horizon broadening experiences for certain
corner cases: E.g. what happens with multiple memory cards (should be
safe through the combination of camera serial no. and date), what
happens when a file is moved between directories on a card, does your
camera do anything special to foil or support my plans?

I'm also interested in reports on whether the two patches work with
your camera and produce meaningful results for you.

- -- 
Henryk Plötz
Grüße aus Berlin
~ Help Microsoft fight software piracy: Give Linux to a friend today! ~
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7-ecc0.1.6 (GNU/Linux)

iD8DBQFHfWPJvI90l8kWns8RAsbvAJ4q15qdBUHl4M5YYofpg5nsUs193wCfa+pE
9OpHio3H76wJB2Vsvt1SksQ=
=EPKS
-----END PGP SIGNATURE-----
Index: src/GPhotoCamera.cs
===================================================================
--- src/GPhotoCamera.cs	(Revision 3524)
+++ src/GPhotoCamera.cs	(Arbeitskopie)
@@ -2,6 +2,7 @@
 using System.IO;
 using System.Collections;
 using LibGPhoto2;
+using Exif;
 using Gdk;
 
 public class GPhotoCamera
@@ -83,6 +84,11 @@
 		if (camera == null) 
 			throw new InvalidOperationException();
 
+		/*
+		 * FIXME This call can potentially take a long time (30 seconds or more)
+		 * and therefore should be run in a separate thread accompanied by an
+		 * indeterminate progress indicator window.
+		 */
 		camera.Init (context);
 		camera_fs = camera.GetFS ();
 
@@ -122,53 +128,50 @@
 	
 	public CameraFile GetFile (GPhotoCameraFile camfile)
 	{
-		int index = files.IndexOf (camfile);
-		return GetFile (index);
+		return GetFileByType (camfile, CameraFileType.Normal);
 	}
 	
 	public CameraFile GetFile (int index)
 	{
-		if (camera_fs == null || files == null || index < 0 || index >= files.Count) 
-			return null;
-
-		GPhotoCameraFile selected_file = (GPhotoCameraFile)files [index];		
-		if (selected_file.NormalFile == null)
-		{
-			selected_file.NormalFile = camera_fs.GetFile (selected_file.Directory, 
-								      selected_file.FileName, 
-								      CameraFileType.Normal,
-								      context);
-		}
-		
-		return selected_file.NormalFile;
+		return GetFileByType (index, CameraFileType.Normal);
 	}
 	
 	public CameraFile GetPreview (GPhotoCameraFile camfile)
 	{
-		int index = files.IndexOf (camfile);
-		return GetPreview (index);
+		return GetFileByType (camfile, CameraFileType.Preview);
 	}
 	
 	public CameraFile GetPreview (int index)
 	{      
+		return GetFileByType (index, CameraFileType.Preview);
+	}
+	
+	public CameraFile GetFileByType (GPhotoCameraFile camfile, CameraFileType type)
+	{
+		int index = files.IndexOf (camfile);
+		return GetFileByType (index, type);
+	}
+	
+	public CameraFile GetFileByType (int index, CameraFileType type)
+	{      
 		if (camera_fs == null || files == null || index < 0 || index >= files.Count) 
 			return null;
 
 		GPhotoCameraFile selected_file = (GPhotoCameraFile) files [index];		
 
-		if (selected_file.PreviewFile == null) {
+		if (selected_file.GetByType (type) == null) {
 			try {
-				selected_file.PreviewFile = camera_fs.GetFile (selected_file.Directory,
+				selected_file.SetByType (type, camera_fs.GetFile (selected_file.Directory,
 									       selected_file.FileName,
-									       CameraFileType.Preview,
-									       context);
+									       type,
+									       context));
 			} catch (System.Exception e) {
 				System.Console.WriteLine (e.ToString ());
-				selected_file.PreviewFile = null;
+				selected_file.SetByType (type, null);
 			}
 		}
 		
-		return selected_file.PreviewFile;
+		return selected_file.GetByType (type);
 	}
 	
 	public Pixbuf GetPreviewPixbuf (GPhotoCameraFile camfile)
@@ -192,6 +195,17 @@
 		return null;
 	}
 	
+	public ExifData GetExifData (GPhotoCameraFile camfile)
+	{
+		CameraFile cfile = GetFileByType (camfile, CameraFileType.Exif);
+		if (cfile != null) {
+			byte[] bytedata = cfile.GetDataAndSize ();
+			Console.WriteLine ("Got {0} bytes of Exif data", bytedata.Length);
+			return new ExifData (bytedata);
+		}
+		return null;
+	}
+	
 	public void SaveFile (int index, string filename)
 	{
 		if (filename == null) 
@@ -251,15 +265,13 @@
 {
 	string directory;
 	string filename;
-	CameraFile normal;
-	CameraFile preview;
+	// FIXME Magic number 5 is the number of items in enum CameraFileType
+	CameraFile[] files = new CameraFile[5];
 	
 	public GPhotoCameraFile (string dir, string name)
 	{
 		directory = dir;
 		filename = name;
-		normal = null;
-		preview = null;
 	}
 		
 	public string Directory {
@@ -274,31 +286,22 @@
 		}
 	}
 	
-	public CameraFile NormalFile {
-		get {
-			return normal;
-		}
-		set {
-			normal = value;
-		}
+	public CameraFile GetByType(CameraFileType type)
+	{
+		return files[(int)type];
 	}
 	
-	public CameraFile PreviewFile
+	public void SetByType(CameraFileType type, CameraFile file)
 	{
-		get {
-			return preview;
-		}
-		set {
-			preview = value;
-		}
+		files[(int)type] = file;
 	}
 	
 	public void ReleaseGPhotoResources()
 	{
-		if (normal != null) 
-			normal.Dispose ();
-
-		if (preview != null) 
-			preview.Dispose ();
+		foreach(CameraFile file in files) {
+			if(file != null)
+				file.Dispose ();
+		}
 	}
+	
 }
Index: src/CameraFileSelectionDialog.cs
===================================================================
--- src/CameraFileSelectionDialog.cs	(Revision 3524)
+++ src/CameraFileSelectionDialog.cs	(Arbeitskopie)
@@ -14,6 +14,7 @@
 using Gtk;
 using Glade;
 using LibGPhoto2;
+using Exif;
 using Mono.Unix;
 
 namespace FSpot {
@@ -150,6 +151,11 @@
 						pdialog.Hide ();
 					}
 					
+					ExifData edata = camera.GetExifData (file);
+					if (edata != null) {
+						Console.WriteLine ("Date taken: {0}", edata.LookupFirst (Exif.Tag.DateTimeOriginal).Value);
+					}
+					
 					Pixbuf scale = null;
 					if (load_thumb) {
 						Pixbuf thumbscale = camera.GetPreviewPixbuf (file);


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