f-spot r4313 - in trunk: . src src/Core src/Jobs
- From: thomasvm svn gnome org
- To: svn-commits-list gnome org
- Subject: f-spot r4313 - in trunk: . src src/Core src/Jobs
- Date: Sat, 6 Sep 2008 18:52:16 +0000 (UTC)
Author: thomasvm
Date: Sat Sep 6 18:52:15 2008
New Revision: 4313
URL: http://svn.gnome.org/viewvc/f-spot?rev=4313&view=rev
Log:
Add duplicate detection to the import window. Fix bgo #169646
Added:
trunk/src/Jobs/CalculateHashJob.cs
Modified:
trunk/ChangeLog
trunk/src/Core/Photo.cs
trunk/src/Core/PhotoVersion.cs
trunk/src/Core/PhotosChanges.cs
trunk/src/FileImportBackend.cs
trunk/src/ImportBackend.cs
trunk/src/ImportCommand.cs
trunk/src/Makefile.am
trunk/src/PhotoStore.cs
trunk/src/TagStore.cs
trunk/src/Updater.cs
trunk/src/f-spot.glade
Modified: trunk/src/Core/Photo.cs
==============================================================================
--- trunk/src/Core/Photo.cs (original)
+++ trunk/src/Core/Photo.cs Sat Sep 6 18:52:15 2008
@@ -207,6 +207,18 @@
changes.RatingChanged = true;
}
}
+
+ private string md5_sum;
+ public string MD5Sum {
+ get { return md5_sum; }
+ set {
+ if (md5_sum == value)
+ return;
+
+ md5_sum = value;
+ changes.MD5SumChanged = true;
+ }
+ }
// Version management
public const int OriginalVersionId = 1;
@@ -254,9 +266,9 @@
// This doesn't check if a version of that name already exists,
// it's supposed to be used only within the Photo and PhotoStore classes.
- internal void AddVersionUnsafely (uint version_id, System.Uri uri, string name, bool is_protected)
+ internal void AddVersionUnsafely (uint version_id, System.Uri uri, string md5_sum, string name, bool is_protected)
{
- Versions [version_id] = new PhotoVersion (this, version_id, uri, name, is_protected);
+ Versions [version_id] = new PhotoVersion (this, version_id, uri, md5_sum, name, is_protected);
highest_version_id = Math.Max (version_id, highest_version_id);
changes.AddVersion (version_id);
@@ -272,7 +284,9 @@
if (VersionNameExists (name))
throw new ApplicationException ("A version with that name already exists");
highest_version_id ++;
- Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, uri, name, is_protected);
+ string md5_sum = GenerateMD5 (uri);
+
+ Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, uri, name, md5_sum, is_protected);
changes.AddVersion (highest_version_id);
return highest_version_id;
@@ -416,6 +430,7 @@
{
System.Uri new_uri = GetUriForVersionName (name, System.IO.Path.GetExtension (VersionUri (base_version_id).AbsolutePath));
System.Uri original_uri = VersionUri (base_version_id);
+ string md5_sum = MD5Sum;
if (VersionNameExists (name))
throw new Exception ("This version name already exists");
@@ -440,9 +455,13 @@
// try {
// Mono.Unix.Native.Syscall.chown(new_path, Mono.Unix.Native.Syscall.getuid (), stat.st_gid);
// } catch (Exception) {}
+ //
+ } else {
+ md5_sum = Photo.GenerateMD5 (new_uri);
}
highest_version_id ++;
- Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, new_uri, name, is_protected);
+
+ Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, new_uri, md5_sum, name, is_protected);
changes.AddVersion (highest_version_id);
@@ -466,7 +485,7 @@
continue;
highest_version_id ++;
- Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, version.Uri, name, is_protected);
+ Versions [highest_version_id] = new PhotoVersion (this, highest_version_id, version.Uri, version.MD5Sum, name, is_protected);
changes.AddVersion (highest_version_id);
@@ -597,9 +616,54 @@
return tags.Contains (tag);
}
+ //
+ // MD5 Calculator
+ //
+ private static System.Security.Cryptography.MD5 md5_generator;
+
+ private static System.Security.Cryptography.MD5 MD5Generator {
+ get {
+ if (md5_generator == null)
+ md5_generator = new System.Security.Cryptography.MD5CryptoServiceProvider ();
+
+ return md5_generator;
+ }
+ }
+
+ private static IDictionary<System.Uri, string> md5_cache = new Dictionary<System.Uri, string> ();
+
+ public static void ResetMD5Cache () {
+ if (md5_cache != null)
+ md5_cache.Clear ();
+ }
+
+ public static string GenerateMD5 (System.Uri uri)
+ {
+ try {
+ if (md5_cache.ContainsKey (uri)) {
+ Log.DebugFormat("Return cache hit for {0}", uri);
+ return md5_cache [uri];
+ }
+
+ using (Gdk.Pixbuf pixbuf = ThumbnailGenerator.Create (uri))
+ {
+ byte[] serialized = PixbufSerializer.Serialize (pixbuf);
+ byte[] md5 = MD5Generator.ComputeHash (serialized);
+ string md5_string = Convert.ToBase64String (md5);
+
+ md5_cache.Add (uri, md5_string);
+ return md5_string;
+ }
+ } catch (Exception e) {
+ Log.DebugFormat("Failed to create MD5Sum for Uri {0}; {1}", uri, e.Message);
+ }
+
+ return string.Empty;
+ }
+
// Constructor
- public Photo (uint id, long unix_time, System.Uri uri)
+ public Photo (uint id, long unix_time, System.Uri uri, string md5_sum)
: base (id)
{
if (uri == null)
@@ -609,10 +673,11 @@
description = String.Empty;
rating = 0;
+ this.md5_sum = md5_sum;
// Note that the original version is never stored in the photo_versions table in the
// database.
- AddVersionUnsafely (OriginalVersionId, uri, Catalog.GetString ("Original"), true);
+ AddVersionUnsafely (OriginalVersionId, uri, md5_sum, Catalog.GetString ("Original"), true);
}
}
}
Modified: trunk/src/Core/PhotoVersion.cs
==============================================================================
--- trunk/src/Core/PhotoVersion.cs (original)
+++ trunk/src/Core/PhotoVersion.cs Sat Sep 6 18:52:15 2008
@@ -5,6 +5,7 @@
* Ettore Perazzoli <ettore perazzoli org>
* Larry Ewing <lewing gnome org>
* Stephane Delcroix <stephane delcroix org>
+ * Thomas Van Machelen <thomas vanmachelen gmail com>
*
* This is free software. See COPYING for details.
*/
@@ -16,6 +17,7 @@
Photo photo;
uint version_id;
System.Uri uri;
+ string md5_sum;
string name;
bool is_protected;
@@ -52,6 +54,11 @@
uri = value;
}
}
+
+ public string MD5Sum {
+ get { return md5_sum; }
+ internal set { md5_sum = value; }
+ }
public uint VersionId {
get { return version_id; }
@@ -65,11 +72,12 @@
get { return photo.Rating; }
}
- public PhotoVersion (Photo photo, uint version_id, System.Uri uri, string name, bool is_protected)
+ public PhotoVersion (Photo photo, uint version_id, System.Uri uri, string md5_sum, string name, bool is_protected)
{
this.photo = photo;
this.version_id = version_id;
this.uri = uri;
+ this.md5_sum = md5_sum;
this.name = name;
this.is_protected = is_protected;
}
Modified: trunk/src/Core/PhotosChanges.cs
==============================================================================
--- trunk/src/Core/PhotosChanges.cs (original)
+++ trunk/src/Core/PhotosChanges.cs Sat Sep 6 18:52:15 2008
@@ -25,6 +25,7 @@
Description = 0x10,
RollId = 0x20,
Data = 0x40,
+ MD5Sum = 0x80
}
Changes changes = Changes.None;
@@ -108,6 +109,17 @@
}
}
+ public bool MD5SumChanged {
+ get { return (changes & Changes.MD5Sum) == Changes.MD5Sum ; }
+ set {
+ if (value)
+ changes |= Changes.MD5Sum;
+ else
+ changes &= ~Changes.MD5Sum;
+ }
+
+ }
+
public static PhotosChanges operator | (PhotosChanges c1, PhotosChanges c2)
{
PhotosChanges changes = new PhotosChanges ();
Modified: trunk/src/FileImportBackend.cs
==============================================================================
--- trunk/src/FileImportBackend.cs (original)
+++ trunk/src/FileImportBackend.cs Sat Sep 6 18:52:15 2008
@@ -23,11 +23,13 @@
TagStore tag_store = FSpot.Core.Database.Tags;
bool recurse;
bool copy;
+ bool include_duplicates;
string [] base_paths;
Tag [] tags;
Gtk.Window parent;
int count;
+ int duplicate_count;
XmpTagsImporter xmptags;
ArrayList import_info;
@@ -117,6 +119,7 @@
xmptags = new XmpTagsImporter (store, tag_store);
roll = rolls.Create ();
+ Photo.ResetMD5Cache ();
return import_info.Count;
}
@@ -191,9 +194,11 @@
return dest;
}
- public override bool Step (out Photo photo, out Pixbuf thumbnail, out int count)
+ public override bool Step (out StepStatusInfo status_info)
{
- thumbnail = null;
+ Photo photo = null;
+ Pixbuf thumbnail = null;
+ bool is_duplicate = false;
if (import_info == null)
throw new ImportException ("Prepare() was not called");
@@ -209,41 +214,62 @@
string destination = info.OriginalPath;
if (copy)
destination = ChooseLocation (info.OriginalPath, directories);
-
+
// Don't copy if we are already home
if (info.OriginalPath == destination) {
info.DestinationPath = destination;
- photo = store.Create (info.DestinationPath, roll.Id, out thumbnail);
+
+ if (!include_duplicates)
+ photo = store.CheckForDuplicate (UriUtils.PathToFileUri (destination));
+
+ if (photo == null)
+ photo = store.Create (info.DestinationPath, roll.Id, out thumbnail);
+ else
+ is_duplicate = true;
} else {
System.IO.File.Copy (info.OriginalPath, destination);
info.DestinationPath = destination;
- photo = store.Create (info.DestinationPath, info.OriginalPath, roll.Id, out thumbnail);
+ if (!include_duplicates)
+ photo = store.CheckForDuplicate (UriUtils.PathToFileUri (destination));
- try {
- File.SetAttributes (destination, File.GetAttributes (info.DestinationPath) & ~FileAttributes.ReadOnly);
- DateTime create = File.GetCreationTime (info.OriginalPath);
- File.SetCreationTime (info.DestinationPath, create);
- DateTime mod = File.GetLastWriteTime (info.OriginalPath);
- File.SetLastWriteTime (info.DestinationPath, mod);
- } catch (IOException) {
- // we don't want an exception here to be fatal.
+ if (photo == null)
+ {
+ photo = store.Create (info.DestinationPath, info.OriginalPath, roll.Id, out thumbnail);
+
+
+ try {
+ File.SetAttributes (destination, File.GetAttributes (info.DestinationPath) & ~FileAttributes.ReadOnly);
+ DateTime create = File.GetCreationTime (info.OriginalPath);
+ File.SetCreationTime (info.DestinationPath, create);
+ DateTime mod = File.GetLastWriteTime (info.OriginalPath);
+ File.SetLastWriteTime (info.DestinationPath, mod);
+ } catch (IOException) {
+ // we don't want an exception here to be fatal.
+ }
+ }
+ else
+ {
+ is_duplicate = true;
}
}
- if (tags != null) {
- foreach (Tag t in tags) {
- photo.AddTag (t);
+ if (!is_duplicate)
+ {
+ if (tags != null) {
+ foreach (Tag t in tags) {
+ photo.AddTag (t);
+ }
+ needs_commit = true;
}
- needs_commit = true;
- }
- needs_commit |= xmptags.Import (photo, info.DestinationPath, info.OriginalPath);
+ needs_commit |= xmptags.Import (photo, info.DestinationPath, info.OriginalPath);
- if (needs_commit)
- store.Commit(photo);
-
- info.Photo = photo;
+ if (needs_commit)
+ store.Commit(photo);
+
+ info.Photo = photo;
+ }
} catch (System.Exception e) {
System.Console.WriteLine ("Error importing {0}{2}{1}", info.OriginalPath, e.ToString (), Environment.NewLine);
if (thumbnail != null)
@@ -266,7 +292,11 @@
}
this.count ++;
- count = this.count;
+
+ if (is_duplicate)
+ this.duplicate_count ++;
+
+ status_info = new StepStatusInfo (photo, thumbnail, this.count, is_duplicate);
return (!abort && count != import_info.Count);
}
@@ -322,18 +352,26 @@
import_info = null;
xmptags.Finish();
- count = 0;
- //rolls.EndImport(); // Clean up the imported session.
+ Photo.ResetMD5Cache ();
+
+ if (count == duplicate_count)
+ rolls.Remove (roll);
+
+ count = duplicate_count = 0;
+ //rolls.EndImport(); // Clean up the imported session.
}
- public FileImportBackend (PhotoStore store, string [] base_paths, bool recurse, Gtk.Window parent) : this (store, base_paths, false, recurse, null, parent) {}
+ public FileImportBackend (PhotoStore store, string [] base_paths, bool recurse, Gtk.Window parent) : this (store, base_paths, false, recurse, false, null, parent) {}
+
+ public FileImportBackend (PhotoStore store, string [] base_paths, bool copy, bool recurse, Tag [] tags, Gtk.Window parent) : this (store, base_paths, copy, recurse, false, null, parent) {}
- public FileImportBackend (PhotoStore store, string [] base_paths, bool copy, bool recurse, Tag [] tags, Gtk.Window parent)
+ public FileImportBackend (PhotoStore store, string [] base_paths, bool copy, bool recurse, bool include_duplicates, Tag [] tags, Gtk.Window parent)
{
this.store = store;
this.copy = copy;
this.base_paths = base_paths;
this.recurse = recurse;
+ this.include_duplicates = include_duplicates;
this.tags = tags;
this.parent = parent;
}
Modified: trunk/src/ImportBackend.cs
==============================================================================
--- trunk/src/ImportBackend.cs (original)
+++ trunk/src/ImportBackend.cs Sat Sep 6 18:52:15 2008
@@ -7,7 +7,7 @@
public abstract int Prepare ();
// Import one picture. Returns false when done; then you have to call Finish().
- public abstract bool Step (out Photo photo, out Pixbuf thumbnail, out int count);
+ public abstract bool Step (out StepStatusInfo import_info);
// Cancel importing.
public abstract void Cancel ();
Modified: trunk/src/ImportCommand.cs
==============================================================================
--- trunk/src/ImportCommand.cs (original)
+++ trunk/src/ImportCommand.cs Sat Sep 6 18:52:15 2008
@@ -321,6 +321,7 @@
[Glade.Widget] Gtk.OptionMenu source_option_menu;
[Glade.Widget] Gtk.ScrolledWindow icon_scrolled;
[Glade.Widget] Gtk.ScrolledWindow photo_scrolled;
+ [Glade.Widget] Gtk.CheckButton duplicate_check;
[Glade.Widget] Gtk.CheckButton recurse_check;
[Glade.Widget] Gtk.CheckButton copy_check;
[Glade.Widget] Gtk.Button ok_button;
@@ -431,10 +432,8 @@
}
private bool Step ()
- {
- Photo photo;
- Pixbuf thumbnail;
- int count;
+ {
+ StepStatusInfo status_info;
bool ongoing = true;
if (importer == null)
@@ -443,27 +442,27 @@
try {
// FIXME this is really just an incredibly ugly way of dealing
// with the recursive DoImport loops we sometimes get into
- ongoing = importer.Step (out photo, out thumbnail, out count);
+ ongoing = importer.Step (out status_info);
} catch (ImportException e){
System.Console.WriteLine (e);
return false;
}
- if (photo == null || thumbnail == null) {
+ if (!status_info.IsDuplicate && (status_info.Photo == null || status_info.Thumbnail == null)) {
Console.WriteLine ("Could not import file");
} else {
//icon_scrolled.Visible = true;
- collection.Add (photo);
-
+ if (!status_info.IsDuplicate)
+ collection.Add (status_info.Photo);
//grid.AddThumbnail (thumbnail);
}
- if (thumbnail != null)
- thumbnail.Dispose ();
+ if (status_info.Thumbnail != null)
+ status_info.Thumbnail.Dispose ();
- if (count < total)
- UpdateProgressBar (count + 1, total);
+ if (status_info.Count < total)
+ UpdateProgressBar (status_info.Count + 1, total);
if (ongoing && total > 0)
return true;
@@ -600,6 +599,7 @@
this.Dialog.DefaultResponse = ResponseType.Ok;
//import_folder_entry.Activated += HandleEntryActivate;
+ duplicate_check.Toggled += HandleRecurseToggled;
recurse_check.Toggled += HandleRecurseToggled;
copy_check.Toggled += HandleRecurseToggled;
@@ -776,9 +776,13 @@
bool recurse = true;
if (recurse_check != null)
recurse = recurse_check.Active;
+
+ bool include_duplicates = false;
+ if (include_duplicates != null)
+ include_duplicates = duplicate_check.Active;
// importer = new FileImportBackend (store, pathimport, copy, recurse, null);
- importer = new FileImportBackend (store, pathimport, copy, recurse, null, Dialog);
+ importer = new FileImportBackend (store, pathimport, copy, recurse, include_duplicates, null, Dialog);
AllowFinish = false;
total = importer.Prepare ();
@@ -848,3 +852,48 @@
#endif
}
+
+public class StepStatusInfo {
+ private Photo photo;
+ private Pixbuf thumbnail;
+ private int count;
+ private bool is_duplicate;
+
+ public Photo Photo {
+ get {
+ return photo;
+ }
+ }
+
+ public Pixbuf Thumbnail {
+ get {
+ return thumbnail;
+ }
+ }
+
+ public int Count {
+ get {
+ return count;
+ }
+ }
+
+ public bool IsDuplicate {
+ get {
+ return is_duplicate;
+ }
+ }
+
+ public StepStatusInfo (Photo photo, Pixbuf thumbnail, int count, bool is_duplicate)
+ {
+ this.photo = photo;
+ this.thumbnail = thumbnail;
+ this.count = count;
+ this.is_duplicate = is_duplicate;
+ }
+
+ public StepStatusInfo (Photo photo, Pixbuf thumbnail, int count)
+ : this (photo, thumbnail, count, false)
+ { }
+}
+
+
Added: trunk/src/Jobs/CalculateHashJob.cs
==============================================================================
--- (empty file)
+++ trunk/src/Jobs/CalculateHashJob.cs Sat Sep 6 18:52:15 2008
@@ -0,0 +1,50 @@
+/*
+ * Jobs/CalculateHashJob.cs
+ *
+ * Author(s)
+ * Thomas Van Machelen <thomas vanmachelen gmail com>
+ *
+ * This is free software. See COPYING for details.
+ */
+
+using System;
+using Banshee.Kernel;
+using FSpot.Utils;
+
+namespace FSpot.Jobs {
+ public class CalculateHashJob : Job
+ {
+ public CalculateHashJob (uint id, string job_options, int run_at, JobPriority job_priority, bool persistent)
+ : this (id, job_options, DbUtils.DateTimeFromUnixTime (run_at), job_priority, persistent)
+ {
+ }
+
+ public CalculateHashJob (uint id, string job_options, DateTime run_at, JobPriority job_priority, bool persistent)
+ : base (id, job_options, job_priority, run_at, persistent)
+ {
+ }
+
+ public static CalculateHashJob Create (JobStore job_store, uint photo_id)
+ {
+ return (CalculateHashJob) job_store.CreatePersistent (typeof(FSpot.Jobs.CalculateHashJob), photo_id.ToString ());
+ }
+
+ protected override bool Execute ()
+ {
+ //this will add some more reactivity to the system
+ System.Threading.Thread.Sleep (200);
+
+ uint photo_id = Convert.ToUInt32 (JobOptions);
+ Log.DebugFormat ("Calculating Hash {0}...", photo_id);
+
+ try {
+ Photo photo = FSpot.Core.Database.Photos.Get (Convert.ToUInt32 (photo_id)) as Photo;
+ FSpot.Core.Database.Photos.UpdateMD5Sum (photo);
+ } catch (System.Exception e) {
+ Log.DebugFormat ("Error Calculating Hash for photo {0}: {1}", JobOptions, e.Message);
+ }
+ return false;
+ }
+ }
+}
+
Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am (original)
+++ trunk/src/Makefile.am Sat Sep 6 18:52:15 2008
@@ -174,6 +174,7 @@
$(srcdir)/Imaging/Tiff.cs \
$(srcdir)/JobStore.cs \
$(srcdir)/Jobs/SyncMetadataJob.cs \
+ $(srcdir)/Jobs/CalculateHashJob.cs \
$(srcdir)/Loupe.cs \
$(srcdir)/MainWindow.cs \
$(srcdir)/MemorySurface.cs \
Modified: trunk/src/PhotoStore.cs
==============================================================================
--- trunk/src/PhotoStore.cs (original)
+++ trunk/src/PhotoStore.cs Sat Sep 6 18:52:15 2008
@@ -123,7 +123,8 @@
" description TEXT NOT NULL, " +
" roll_id INTEGER NOT NULL, " +
" default_version_id INTEGER NOT NULL, " +
- " rating INTEGER NULL " +
+ " rating INTEGER NULL, " +
+ " md5_sum TEXT NULL " +
")");
@@ -141,11 +142,42 @@
" version_id INTEGER, " +
" name STRING, " +
" uri STRING NOT NULL," +
- " protected BOOLEAN, " +
+ " md5_sum STRING NOT NULL," +
+ " protected BOOLEAN, " +
" UNIQUE (photo_id, version_id) " +
")");
}
+ public Photo CheckForDuplicate (System.Uri uri) {
+ // Here we can go wild in comparing photos,
+ // for now we check on uri and md5
+ Photo found = GetByUri (uri);
+
+ if (found != null)
+ return found;
+
+ string md5 = Photo.GenerateMD5 (uri);
+ Gnome.Vfs.FileInfo info = new Gnome.Vfs.FileInfo (uri.ToString (), FileInfoOptions.GetMimeType);
+
+ Photo[] md5_matches = GetByMD5 (md5);
+
+ foreach (Photo match in md5_matches)
+ {
+ Gnome.Vfs.FileInfo match_info = new Gnome.Vfs.FileInfo (match.DefaultVersionUri.ToString (), FileInfoOptions.GetMimeType);
+
+ // same mimetype?
+ if (info.MimeType != match_info.MimeType)
+ continue;
+
+ // other comparisons?
+
+ // TODO? load pixbuf and compare sizes?
+
+ return match;
+ }
+
+ return null;
+ }
[Obsolete ("Use Create (Uri, uint, out Pixbuf) instead")]
public Photo Create (string path, uint roll_id, out Pixbuf thumbnail)
@@ -170,18 +202,23 @@
using (FSpot.ImageFile img = FSpot.ImageFile.Create (orig_uri)) {
long unix_time = DbUtils.UnixTimeFromDateTime (img.Date);
string description = img.Description != null ? img.Description.Split ('\0') [0] : String.Empty;
+ string md5_sum = Photo.GenerateMD5 (new_uri);
+
+ uint id = (uint) Database.Execute (
+ new DbCommand (
+ "INSERT INTO photos (time, uri, description, roll_id, default_version_id, rating, md5_sum) " +
+ "VALUES (:time, :uri, :description, :roll_id, :default_version_id, :rating, :md5_sum)",
+ "time", unix_time,
+ "uri", new_uri.OriginalString,
+ "description", description,
+ "roll_id", roll_id,
+ "default_version_id", Photo.OriginalVersionId,
+ "rating", "0",
+ "md5_sum", md5_sum
+ )
+ );
- uint id = (uint) Database.Execute (new DbCommand (
- "INSERT INTO photos (time, uri, description, roll_id, default_version_id, rating) " +
- "VALUES (:time, :uri, :description, :roll_id, :default_version_id, :rating)",
- "time", unix_time,
- "uri", new_uri.OriginalString,
- "description", description,
- "roll_id", roll_id,
- "default_version_id", Photo.OriginalVersionId,
- "rating", "0"));
-
- photo = new Photo (id, unix_time, new_uri);
+ photo = new Photo (id, unix_time, new_uri, md5_sum);
AddToCache (photo);
photo.Loaded = true;
@@ -194,7 +231,13 @@
private void GetVersions (Photo photo)
{
- SqliteDataReader reader = Database.Query(new DbCommand("SELECT version_id, name, uri, protected FROM photo_versions WHERE photo_id = :id", "id", photo.Id));
+ SqliteDataReader reader = Database.Query(
+ new DbCommand("SELECT version_id, name, uri, md5_sum, protected " +
+ "FROM photo_versions " +
+ "WHERE photo_id = :id",
+ "id", photo.Id
+ )
+ );
while (reader.Read ()) {
uint version_id = Convert.ToUInt32 (reader [0]);
@@ -204,8 +247,9 @@
#else
System.Uri uri = new System.Uri (reader[2].ToString (), true);
#endif
- bool is_protected = Convert.ToBoolean (reader[3]);
- photo.AddVersionUnsafely (version_id, uri, name, is_protected);
+ string md5_sum = reader[3].ToString ();
+ bool is_protected = Convert.ToBoolean (reader[4]);
+ photo.AddVersionUnsafely (version_id, uri, md5_sum, name, is_protected);
}
reader.Close();
}
@@ -223,7 +267,7 @@
}
private void GetAllVersions () {
- SqliteDataReader reader = Database.Query("SELECT photo_id, version_id, name, uri, protected FROM photo_versions");
+ SqliteDataReader reader = Database.Query("SELECT photo_id, version_id, name, uri, md5_sum, protected FROM photo_versions");
while (reader.Read ()) {
uint id = Convert.ToUInt32 (reader [0]);
@@ -247,8 +291,9 @@
#else
System.Uri uri = new System.Uri (reader[3].ToString (), true);
#endif
- bool is_protected = Convert.ToBoolean (reader[4]);
- photo.AddVersionUnsafely (version_id, uri, name, is_protected);
+ string md5_sum = reader[4].ToString ();
+ bool is_protected = Convert.ToBoolean (reader[5]);
+ photo.AddVersionUnsafely (version_id, uri, md5_sum, name, is_protected);
}
/*
@@ -293,17 +338,23 @@
if (photo != null)
return photo;
- SqliteDataReader reader = Database.Query(new DbCommand("SELECT time, uri, description, roll_id, default_version_id, rating "
- + "FROM photos WHERE id = :id", "id", id));
+ SqliteDataReader reader = Database.Query(
+ new DbCommand("SELECT time, uri, description, roll_id, default_version_id, rating, md5_sum " +
+ "FROM photos " +
+ "WHERE id = :id", "id", id
+ )
+ );
if (reader.Read ()) {
photo = new Photo (id,
Convert.ToInt64 (reader [0]),
#if MONO_2_0
- new System.Uri (reader [1].ToString ()));
+ new System.Uri (reader [1].ToString ()),
#else
- new System.Uri (reader [1].ToString (), true));
+ new System.Uri (reader [1].ToString (), true),
#endif
+ reader[6].ToString ()
+ );
photo.Description = reader[2].ToString ();
photo.RollId = Convert.ToUInt32 (reader[3]);
@@ -333,6 +384,7 @@
Photo photo = null;
uint timer = Log.DebugTimerStart ();
+
SqliteDataReader reader = Database.Query (new DbCommand ("SELECT id, time, description, roll_id, default_version_id, rating " +
" FROM photos " +
" LEFT JOIN photo_versions AS pv ON photos.id = pv.photo_id" +
@@ -341,7 +393,8 @@
if (reader.Read ()) {
photo = new Photo (Convert.ToUInt32 (reader [0]),
Convert.ToInt64 (reader [1]),
- uri);
+ uri,
+ reader[6].ToString ());
photo.Description = reader[2].ToString ();
photo.RollId = Convert.ToUInt32 (reader[3]);
@@ -354,8 +407,10 @@
if (photo == null)
return null;
- if (LookupInCache (photo.Id) as Photo != null)
- return LookupInCache (photo.Id) as Photo;
+ Photo cached = LookupInCache (photo.Id) as Photo;
+
+ if (cached != null)
+ return cached;
AddToCache (photo);
@@ -365,6 +420,63 @@
return photo;
}
+ public Photo[] GetByMD5 (string md5_sum)
+ {
+ List<Photo> photos = new List<Photo> ();
+
+ SqliteDataReader reader = Database.Query (
+ new DbCommand ("SELECT DISTINCT " +
+ "id, time, photos.uri, description, roll_id, default_version_id, rating " +
+ "FROM photos " +
+ "LEFT JOIN photo_versions " +
+ "ON photos.id = photo_versions.photo_id " +
+ "WHERE photos.md5_sum = :md5_sum " +
+ "OR photo_versions.md5_sum = :md5_sum",
+ "md5_sum", md5_sum
+ )
+ );
+
+ while (reader.Read ()) {
+ Photo photo = new Photo (Convert.ToUInt32 (reader [0]),
+ Convert.ToInt64 (reader [1]),
+#if MONO_2_0
+ new System.Uri (reader [2].ToString ()),
+#else
+ new System.Uri (reader [2].ToString (), true),
+#endif
+ md5_sum
+ );
+
+ photo.Description = reader[3].ToString ();
+ photo.RollId = Convert.ToUInt32 (reader[4]);
+ photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);
+ photo.Rating = Convert.ToUInt32 (reader [6]);
+ photo.MD5Sum = md5_sum;
+
+ // get cached if possible
+ Photo cached = LookupInCache (photo.Id) as Photo;
+
+ if (cached != null)
+ {
+ photos.Add (cached);
+ continue;
+ }
+
+ // Add to cache and fully load if not found in cache
+ AddToCache (photo);
+
+ GetTags (photo);
+ GetVersions (photo);
+
+ // add to collection
+ photos.Add (photo);
+ }
+
+ reader.Close();
+
+ return photos.ToArray ();
+ }
+
public void Remove (Tag []tags)
{
Photo [] photos = Query (tags, String.Empty, null, null);
@@ -426,20 +538,26 @@
private PhotoChanges Update (Photo photo) {
PhotoChanges changes = photo.Changes;
// Update photo.
- if (changes.DescriptionChanged || changes.DefaultVersionIdChanged || changes.TimeChanged || changes.UriChanged || changes.RatingChanged )
- Database.ExecuteNonQuery (new DbCommand (
- "UPDATE photos SET description = :description, " +
- "default_version_id = :default_version_id, " +
- "time = :time, " +
- "uri = :uri, " +
- "rating = :rating " +
- "WHERE id = :id ",
- "description", photo.Description,
- "default_version_id", photo.DefaultVersionId,
- "time", DbUtils.UnixTimeFromDateTime (photo.Time),
- "uri", photo.VersionUri (Photo.OriginalVersionId).OriginalString,
- "rating", String.Format ("{0}", photo.Rating),
- "id", photo.Id));
+ if (changes.DescriptionChanged || changes.DefaultVersionIdChanged || changes.TimeChanged || changes.UriChanged || changes.RatingChanged || changes.MD5SumChanged )
+ Database.ExecuteNonQuery (
+ new DbCommand (
+ "UPDATE photos " +
+ "SET description = :description, " +
+ " default_version_id = :default_version_id, " +
+ " time = :time, " +
+ " uri = :uri, " +
+ " rating = :rating, " +
+ " md5_sum = :md5_sum " +
+ "WHERE id = :id ",
+ "description", photo.Description,
+ "default_version_id", photo.DefaultVersionId,
+ "time", DbUtils.UnixTimeFromDateTime (photo.Time),
+ "uri", photo.VersionUri (Photo.OriginalVersionId).OriginalString,
+ "rating", String.Format ("{0}", photo.Rating),
+ "md5_sum", photo.MD5Sum,
+ "id", photo.Id
+ )
+ );
// Update tags.
if (changes.TagsRemoved != null)
@@ -493,6 +611,38 @@
photo.Changes = null;
return changes;
}
+
+ public void UpdateMD5Sum (Photo photo) {
+ string md5_sum = Photo.GenerateMD5 (photo.VersionUri (Photo.OriginalVersionId));
+ photo.MD5Sum = md5_sum;
+
+ Database.ExecuteNonQuery (
+ new DbCommand (
+ "UPDATE photos " +
+ "SET md5_sum = :md5_sum " +
+ "WHERE ID = :id",
+ "md5_sum", md5_sum,
+ "id", photo.Id
+ )
+ );
+
+ bool needs_commit = false;
+
+ foreach (uint version_id in photo.VersionIds) {
+ if (version_id == Photo.OriginalVersionId)
+ continue;
+
+ PhotoVersion version = photo.GetVersion (version_id) as PhotoVersion;
+
+ string version_md5_sum = Photo.GenerateMD5 (version.Uri);
+ version.MD5Sum = version_md5_sum;
+
+ needs_commit = true;
+ }
+
+ if (needs_commit)
+ Commit (photo);
+ }
// Dbus
public event ItemsAddedHandler ItemsAddedOverDBus;
@@ -750,11 +900,12 @@
photo = new Photo (id,
Convert.ToInt64 (reader [1]),
#if MONO_2_0
- new System.Uri (reader [2].ToString ()));
+ new System.Uri (reader [2].ToString ()),
#else
- new System.Uri (reader [2].ToString (), true));
+ new System.Uri (reader [2].ToString (), true),
#endif
-
+ reader [6].ToString ()
+ );
photo.Description = reader[3].ToString ();
photo.RollId = Convert.ToUInt32 (reader[4]);
photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);
@@ -814,7 +965,8 @@
"photos.description, " +
"photos.roll_id, " +
"photos.default_version_id, " +
- "photos.rating " +
+ "photos.rating, " +
+ "photos.md5_sum " +
"FROM photos " +
"WHERE uri LIKE :uri " +
"AND uri NOT LIKE :uri_",
@@ -940,7 +1092,8 @@
"photos.description, " +
"photos.roll_id, " +
"photos.default_version_id, " +
- "photos.rating " +
+ "photos.rating, " +
+ "photos.md5_sum " +
"FROM photos ");
if (range != null) {
Modified: trunk/src/TagStore.cs
==============================================================================
--- trunk/src/TagStore.cs (original)
+++ trunk/src/TagStore.cs Sat Sep 6 18:52:15 2008
@@ -35,7 +35,8 @@
public static byte [] Serialize (Pixbuf pixbuf)
{
Pixdata pixdata = new Pixdata ();
- pixdata.FromPixbuf (pixbuf, true); // FIXME GTK# shouldn't this be a constructor or something?
+ IntPtr raw_pixdata = pixdata.FromPixbuf (pixbuf, true); // FIXME GTK# shouldn't this be a constructor or something?
+ // It's probably because we need the IntPtr to free it afterwards
uint data_length;
IntPtr raw_data = gdk_pixdata_serialize (ref pixdata, out data_length);
@@ -43,8 +44,8 @@
byte [] data = new byte [data_length];
Marshal.Copy (raw_data, data, 0, (int) data_length);
- GLib.Marshaller.Free (raw_data);
-
+ GLib.Marshaller.Free (new IntPtr[] { raw_data, raw_pixdata });
+
return data;
}
}
Modified: trunk/src/Updater.cs
==============================================================================
--- trunk/src/Updater.cs (original)
+++ trunk/src/Updater.cs Sat Sep 6 18:52:15 2008
@@ -274,7 +274,65 @@
"SELECT photo_id, version_id, name, uri, protected FROM {0}", tmp_photo_versions));
});
- // Update to version 14.0
+ // Update to version 16.0
+ AddUpdate (new Version (16,0), delegate () {
+ string temp_table = MoveTableToTemp ("photos");
+
+ Execute ("CREATE TABLE photos ( " +
+ " id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
+ " time INTEGER NOT NULL, " +
+ " uri STRING NOT NULL, " +
+ " description TEXT NOT NULL, " +
+ " roll_id INTEGER NOT NULL, " +
+ " default_version_id INTEGER NOT NULL, " +
+ " rating INTEGER NULL, " +
+ " md5_sum TEXT NULL " +
+ ")"
+ );
+
+ Execute (string.Format ("INSERT INTO photos (id, time, uri, description, roll_id, " +
+ "default_version_id, rating, md5_sum) " +
+ "SELECT id, time, uri, description, roll_id, " +
+ " default_version_id, rating, '' " +
+ "FROM {0} ",
+ temp_table
+ )
+ );
+
+
+ string temp_versions_table = MoveTableToTemp ("photo_versions");
+
+ Console.WriteLine("{0} - {1}", temp_table, temp_versions_table);
+
+ Execute ("CREATE TABLE photo_versions ( " +
+ " photo_id INTEGER, " +
+ " version_id INTEGER, " +
+ " name STRING, " +
+ " uri STRING NOT NULL," +
+ " md5_sum STRING NOT NULL," +
+ " protected BOOLEAN " +
+ ")");
+
+ Execute (string.Format ("INSERT INTO photo_versions (photo_id, version_id, name, uri, md5_sum, protected) " +
+ "SELECT photo_id, version_id, name, uri, '', protected " +
+ "FROM {0} ",
+ temp_versions_table
+ )
+ );
+
+ // This is kind of hacky but should be a lot faster on
+ // large photo databases
+ Execute (string.Format ("INSERT INTO jobs (job_type, job_options, run_at, job_priority) " +
+ "SELECT '{0}', id, {1}, {2} " +
+ "FROM photos ",
+ typeof(Jobs.CalculateHashJob).ToString (),
+ FSpot.Utils.DbUtils.UnixTimeFromDateTime (DateTime.Now),
+ 0
+ )
+ );
+ }, true);
+
+ // Update to version 17.0
//AddUpdate (new Version (14,0),delegate () {
// do update here
//});
Modified: trunk/src/f-spot.glade
==============================================================================
--- trunk/src/f-spot.glade (original)
+++ trunk/src/f-spot.glade Sat Sep 6 18:52:15 2008
@@ -4408,6 +4408,22 @@
</packing>
</child>
<child>
+ <widget class="GtkCheckButton" id="duplicate_check">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Include Duplicates</property>
+ <property name="use_underline">True</property>
+ <property name="response_id">0</property>
+ <property name="active">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
<widget class="GtkCheckButton" id="copy_check">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -4420,7 +4436,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">3</property>
+ <property name="position">4</property>
</packing>
</child>
<child>
@@ -4437,7 +4453,7 @@
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
- <property name="position">3</property>
+ <property name="position">5</property>
</packing>
</child>
</widget>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]