No issues found
1 /*
2 * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 * Authors: Philip Van Hoof <philip@codeminded.be>
20 */
21
22 #include "config.h"
23
24 #include <locale.h>
25 #include <string.h>
26 #include <math.h>
27
28 #include <exempi/xmp.h>
29 #include <exempi/xmpconsts.h>
30
31 #include <glib-object.h>
32 #include <gio/gio.h>
33
34 #include <libtracker-common/tracker-ontologies.h>
35 #include <libtracker-common/tracker-utils.h>
36
37 #include "tracker-writeback-file.h"
38
39 #define TRACKER_TYPE_WRITEBACK_XMP (tracker_writeback_xmp_get_type ())
40
41 typedef struct TrackerWritebackXMP TrackerWritebackXMP;
42 typedef struct TrackerWritebackXMPClass TrackerWritebackXMPClass;
43
44 struct TrackerWritebackXMP {
45 TrackerWritebackFile parent_instance;
46 };
47
48 struct TrackerWritebackXMPClass {
49 TrackerWritebackFileClass parent_class;
50 };
51
52 static GType tracker_writeback_xmp_get_type (void) G_GNUC_CONST;
53 static gboolean writeback_xmp_update_file_metadata (TrackerWritebackFile *writeback_file,
54 GFile *file,
55 GPtrArray *values,
56 TrackerSparqlConnection *connection,
57 GCancellable *cancellable,
58 GError **error);
59 static const gchar * const *writeback_xmp_content_types (TrackerWritebackFile *writeback_file);
60
61 G_DEFINE_DYNAMIC_TYPE (TrackerWritebackXMP, tracker_writeback_xmp, TRACKER_TYPE_WRITEBACK_FILE);
62
63 static void
64 tracker_writeback_xmp_class_init (TrackerWritebackXMPClass *klass)
65 {
66 TrackerWritebackFileClass *writeback_file_class = TRACKER_WRITEBACK_FILE_CLASS (klass);
67
68 xmp_init ();
69
70 writeback_file_class->update_file_metadata = writeback_xmp_update_file_metadata;
71 writeback_file_class->content_types = writeback_xmp_content_types;
72 }
73
74 static void
75 tracker_writeback_xmp_class_finalize (TrackerWritebackXMPClass *klass)
76 {
77 xmp_terminate ();
78 }
79
80 static void
81 tracker_writeback_xmp_init (TrackerWritebackXMP *wbx)
82 {
83 }
84
85 static const gchar * const *
86 writeback_xmp_content_types (TrackerWritebackFile *wbf)
87 {
88 static const gchar *content_types[] = {
89 "image/png", /* .png files */
90 "sketch/png", /* .sketch.png files on Maemo*/
91 "image/jpeg", /* .jpg & .jpeg files */
92 "image/tiff", /* .tiff & .tif files */
93 "video/mp4", /* .mp4 files */
94 "video/3gpp", /* .3gpp files */
95 NULL
96 };
97
98 /* "image/gif" .gif files
99 "application/pdf" .pdf files
100 "application/rdf+xml" .xmp files
101 "application/postscript" .ps files
102 "application/x-shockwave-flash" .swf files
103 "video/quicktime" .mov files
104 "video/mpeg" .mpeg & .mpg files
105 "audio/mpeg" .mp3, etc files */
106
107 return content_types;
108 }
109
110 static gboolean
111 writeback_xmp_update_file_metadata (TrackerWritebackFile *wbf,
112 GFile *file,
113 GPtrArray *values,
114 TrackerSparqlConnection *connection,
115 GCancellable *cancellable,
116 GError **error)
117 {
118 gchar *path;
119 guint n;
120 XmpFilePtr xmp_files;
121 XmpPtr xmp;
122 #ifdef DEBUG_XMP
123 XmpStringPtr str;
124 #endif
125 GString *keywords = NULL;
126 const gchar *urn = NULL;
127
128 path = g_file_get_path (file);
129
130 xmp_files = xmp_files_open_new (path, XMP_OPEN_FORUPDATE);
131
132 if (!xmp_files) {
133 g_set_error (error,
134 G_IO_ERROR,
135 G_IO_ERROR_FAILED,
136 "Can't open '%s' for update with Exempi (Exempi error code = %d)",
137 path,
138 xmp_get_error ());
139 g_free (path);
140 return FALSE;
141 }
142
143 xmp = xmp_files_get_new_xmp (xmp_files);
144
145 if (!xmp) {
146 xmp = xmp_new_empty ();
147 }
148
149 #ifdef DEBUG_XMP
150 str = xmp_string_new ();
151 g_print ("\nBEFORE: ---- \n");
152 xmp_serialize_and_format (xmp, str, 0, 0, "\n", "\t", 1);
153 g_print ("%s\n", xmp_string_cstr (str));
154 xmp_string_free (str);
155 #endif
156
157 for (n = 0; n < values->len; n++) {
158 const GStrv row = g_ptr_array_index (values, n);
159
160 urn = row[1]; /* The urn is at 1 */
161
162 if (g_strcmp0 (row[2], TRACKER_NIE_PREFIX "title") == 0) {
163 xmp_delete_property (xmp, NS_EXIF, "Title");
164 xmp_set_property (xmp, NS_EXIF, "Title", row[3], 0);
165 xmp_delete_property (xmp, NS_DC, "title");
166 xmp_set_property (xmp, NS_DC, "title", row[3], 0);
167 }
168
169 if (g_strcmp0 (row[2], TRACKER_NCO_PREFIX "creator") == 0) {
170 TrackerSparqlCursor *cursor;
171 GError *error = NULL;
172 gchar *query;
173
174 query = g_strdup_printf ("SELECT ?fullname { "
175 " <%s> nco:fullname ?fullname "
176 "}", row[3]);
177 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
178 g_free (query);
179 if (!error) {
180 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
181 xmp_delete_property (xmp, NS_DC, "creator");
182 xmp_set_property (xmp, NS_DC, "creator",
183 tracker_sparql_cursor_get_string (cursor, 0, NULL),
184 0);
185 }
186 }
187 g_object_unref (cursor);
188 g_clear_error (&error);
189 }
190
191 if (g_strcmp0 (row[2], TRACKER_NCO_PREFIX "contributor") == 0) {
192 TrackerSparqlCursor *cursor;
193 GError *error = NULL;
194 gchar *query;
195
196 query = g_strdup_printf ("SELECT ?fullname { "
197 " <%s> nco:fullname ?fullname "
198 "}", row[3]);
199
200 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
201 g_free (query);
202 if (!error) {
203 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
204 xmp_delete_property (xmp, NS_DC, "contributor");
205 xmp_set_property (xmp, NS_DC, "contributor", tracker_sparql_cursor_get_string (cursor, 0, NULL), 0);
206 }
207 }
208 g_object_unref (cursor);
209 g_clear_error (&error);
210 }
211
212 if (g_strcmp0 (row[2], TRACKER_NIE_PREFIX "description") == 0) {
213 xmp_delete_property (xmp, NS_DC, "description");
214 xmp_set_property (xmp, NS_DC, "description", row[3], 0);
215 }
216
217 if (g_strcmp0 (row[2], TRACKER_NIE_PREFIX "copyright") == 0) {
218 xmp_delete_property (xmp, NS_EXIF, "Copyright");
219 xmp_set_property (xmp, NS_EXIF, "Copyright", row[3], 0);
220 }
221
222 if (g_strcmp0 (row[2], TRACKER_NIE_PREFIX "comment") == 0) {
223 xmp_delete_property (xmp, NS_EXIF, "UserComment");
224 xmp_set_property (xmp, NS_EXIF, "UserComment", row[3], 0);
225 }
226
227 if (g_strcmp0 (row[2], TRACKER_NIE_PREFIX "keyword") == 0) {
228 if (!keywords) {
229 keywords = g_string_new (row[3]);
230 } else {
231 g_string_append_printf (keywords, ", %s", row[3]);
232 }
233 }
234
235
236 if (g_strcmp0 (row[2], TRACKER_NAO_PREFIX "hasTag") == 0) {
237 TrackerSparqlCursor *cursor;
238 GError *error = NULL;
239 gchar *query;
240
241 query = g_strdup_printf ("SELECT ?label { "
242 " <%s> nao:prefLabel ?label "
243 "}", row[3]);
244
245 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
246 g_free (query);
247 if (!error) {
248 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
249 if (!keywords) {
250 keywords = g_string_new (tracker_sparql_cursor_get_string (cursor, 0, NULL));
251 } else {
252 g_string_append_printf (keywords, ", %s", tracker_sparql_cursor_get_string (cursor, 0, NULL));
253 }
254 }
255 }
256 g_object_unref (cursor);
257 g_clear_error (&error);
258 }
259
260 if (g_strcmp0 (row[2], TRACKER_NIE_PREFIX "contentCreated") == 0) {
261 xmp_delete_property (xmp, NS_EXIF, "Date");
262 xmp_set_property (xmp, NS_EXIF, "Date", row[3], 0);
263 xmp_delete_property (xmp, NS_DC, "date");
264 xmp_set_property (xmp, NS_DC, "date", row[3], 0);
265 }
266
267 if (g_strcmp0 (row[2], TRACKER_NFO_PREFIX "orientation") == 0) {
268
269 xmp_delete_property (xmp, NS_EXIF, "Orientation");
270
271 if (g_strcmp0 (row[3], TRACKER_NFO_PREFIX "orientation-top") == 0) {
272 xmp_set_property (xmp, NS_EXIF, "Orientation", "top - left", 0);
273 } else if (g_strcmp0 (row[3], TRACKER_NFO_PREFIX "orientation-top-mirror") == 0) {
274 xmp_set_property (xmp, NS_EXIF, "Orientation", "top - right", 0);
275 } else if (g_strcmp0 (row[3], TRACKER_NFO_PREFIX "orientation-bottom") == 0) {
276 xmp_set_property (xmp, NS_EXIF, "Orientation", "bottom - left", 0);
277 } else if (g_strcmp0 (row[3], TRACKER_NFO_PREFIX "orientation-bottom-mirror") == 0) {
278 xmp_set_property (xmp, NS_EXIF, "Orientation", "bottom - right", 0);
279 } else if (g_strcmp0 (row[3], TRACKER_NFO_PREFIX "orientation-left-mirror") == 0) {
280 xmp_set_property (xmp, NS_EXIF, "Orientation", "left - top", 0);
281 } else if (g_strcmp0 (row[3], TRACKER_NFO_PREFIX "orientation-right") == 0) {
282 xmp_set_property (xmp, NS_EXIF, "Orientation", "right - top", 0);
283 } else if (g_strcmp0 (row[3], TRACKER_NFO_PREFIX "orientation-right-mirror") == 0) {
284 xmp_set_property (xmp, NS_EXIF, "Orientation", "right - bottom", 0);
285 } else if (g_strcmp0 (row[3], TRACKER_NFO_PREFIX "orientation-left") == 0) {
286 xmp_set_property (xmp, NS_EXIF, "Orientation", "left - bottom", 0);
287 }
288 }
289
290 #ifdef SET_TYPICAL_CAMERA_FIELDS
291 /* Default we don't do this, we shouldn't overwrite fields that are
292 * typically set by the camera itself. What do we know (better) than
293 * the actual camera did, anyway? Even if the user overwrites them in
294 * the RDF store ... (does he know what he's doing anyway?) */
295
296 if (g_strcmp0 (row[2], TRACKER_NMM_PREFIX "meteringMode") == 0) {
297
298 xmp_delete_property (xmp, NS_EXIF, "MeteringMode");
299
300 /* 0 = Unknown
301 1 = Average
302 2 = CenterWeightedAverage
303 3 = Spot
304 4 = MultiSpot
305 5 = Pattern
306 6 = Partial
307 255 = other */
308
309 if (g_strcmp0 (row[3], TRACKER_NMM_PREFIX "metering-mode-center-weighted-average") == 0) {
310 xmp_set_property (xmp, NS_EXIF, "MeteringMode", "0", 0);
311 } else if (g_strcmp0 (row[3], TRACKER_NMM_PREFIX "metering-mode-average") == 0) {
312 xmp_set_property (xmp, NS_EXIF, "MeteringMode", "1", 0);
313 } else if (g_strcmp0 (row[3], TRACKER_NMM_PREFIX "metering-mode-spot") == 0) {
314 xmp_set_property (xmp, NS_EXIF, "MeteringMode", "3", 0);
315 } else if (g_strcmp0 (row[3], TRACKER_NMM_PREFIX "metering-mode-multispot") == 0) {
316 xmp_set_property (xmp, NS_EXIF, "MeteringMode", "4", 0);
317 } else if (g_strcmp0 (row[3], TRACKER_NMM_PREFIX "metering-mode-pattern") == 0) {
318 xmp_set_property (xmp, NS_EXIF, "MeteringMode", "5", 0);
319 } else if (g_strcmp0 (row[3], TRACKER_NMM_PREFIX "metering-mode-partial") == 0) {
320 xmp_set_property (xmp, NS_EXIF, "MeteringMode", "6", 0);
321 } else {
322 xmp_set_property (xmp, NS_EXIF, "MeteringMode", "255", 0);
323 }
324 }
325
326 if (g_strcmp0 (row[2], TRACKER_NMM_PREFIX "whiteBalance") == 0) {
327
328 xmp_delete_property (xmp, NS_EXIF, "WhiteBalance");
329
330 if (g_strcmp0 (row[3], TRACKER_NMM_PREFIX "white-balance-auto") == 0) {
331 /* 0 = Auto white balance
332 * 1 = Manual white balance */
333 xmp_set_property (xmp, NS_EXIF, "WhiteBalance", "0", 0);
334 } else {
335 xmp_set_property (xmp, NS_EXIF, "WhiteBalance", "1", 0);
336 }
337 }
338
339 if (g_strcmp0 (row[2], TRACKER_NMM_PREFIX "flash") == 0) {
340
341 xmp_delete_property (xmp, NS_EXIF, "Flash");
342
343 if (g_strcmp0 (row[3], TRACKER_NMM_PREFIX "flash-on") == 0) {
344 /* 0 = Flash did not fire
345 * 1 = Flash fired */
346 xmp_set_property (xmp, NS_EXIF, "Flash", "1", 0);
347 } else {
348 xmp_set_property (xmp, NS_EXIF, "Flash", "0", 0);
349 }
350 }
351
352
353 /* TODO: Don't write row[3] as-is here, read xmp_specification.pdf,
354 page 84 (bottom). */
355
356 if (g_strcmp0 (row[2], TRACKER_NMM_PREFIX "focalLength") == 0) {
357 xmp_delete_property (xmp, NS_EXIF, "FocalLength");
358 xmp_set_property (xmp, NS_EXIF, "FocalLength", row[3], 0);
359 }
360
361 if (g_strcmp0 (row[2], TRACKER_NMM_PREFIX "exposureTime") == 0) {
362 xmp_delete_property (xmp, NS_EXIF, "ExposureTime");
363 xmp_set_property (xmp, NS_EXIF, "ExposureTime", row[3], 0);
364 }
365
366 if (g_strcmp0 (row[2], TRACKER_NMM_PREFIX "isoSpeed") == 0) {
367 xmp_delete_property (xmp, NS_EXIF, "ISOSpeedRatings");
368 xmp_set_property (xmp, NS_EXIF, "ISOSpeedRatings", row[3], 0);
369 }
370
371 if (g_strcmp0 (row[2], TRACKER_NMM_PREFIX "fnumber") == 0) {
372 xmp_delete_property (xmp, NS_EXIF, "FNumber");
373 xmp_set_property (xmp, NS_EXIF, "FNumber", row[3], 0);
374 }
375
376
377 /* Totally deprecated: this uses nfo:Equipment nowadays */
378 if (g_strcmp0 (row[2], TRACKER_NMM_PREFIX "camera") == 0) {
379 gchar *work_on = g_strdup (row[3]);
380 gchar *ptr = strchr (work_on, ' ');
381
382 if (ptr) {
383
384 *ptr = '\0';
385 ptr++;
386
387 xmp_delete_property (xmp, NS_EXIF, "Make");
388 xmp_set_property (xmp, NS_EXIF, "Make", work_on, 0);
389 xmp_delete_property (xmp, NS_EXIF, "Model");
390 xmp_set_property (xmp, NS_EXIF, "Model", ptr, 0);
391 } else {
392 xmp_delete_property (xmp, NS_EXIF, "Make");
393 xmp_delete_property (xmp, NS_EXIF, "Model");
394 xmp_set_property (xmp, NS_EXIF, "Model", work_on, 0);
395 }
396
397 g_free (work_on);
398 }
399 #endif /* SET_TYPICAL_CAMERA_FIELDS */
400
401 if (g_strcmp0 (row[2], TRACKER_NFO_PREFIX "heading") == 0) {
402 xmp_delete_property (xmp, NS_EXIF, "GPSImgDirection");
403 xmp_set_property (xmp, NS_EXIF, "GPSImgDirection", row[3], 0);
404 }
405 }
406
407 if (urn != NULL) {
408 TrackerSparqlCursor *cursor;
409 GError *error = NULL;
410 gchar *query;
411
412 query = g_strdup_printf ("SELECT "
413 "nco:locality (?addr) "
414 "nco:region (?addr) "
415 "nco:streetAddress (?addr) "
416 "nco:country (?addr) "
417 "slo:altitude (?loc) "
418 "slo:longitude (?loc) "
419 "slo:latitude (?loc) "
420 "WHERE { <%s> slo:location ?loc . "
421 "?loc slo:postalAddress ?addr . }",
422 urn);
423
424 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
425 g_free (query);
426 if (!error) {
427 if (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
428 const gchar *city = NULL, *subl = NULL, *country = NULL,
429 *state = NULL, *altitude = NULL, *longitude = NULL,
430 *latitude = NULL;
431
432 if (tracker_sparql_cursor_get_value_type (cursor, 0) != TRACKER_SPARQL_VALUE_TYPE_UNBOUND) {
433 city = tracker_sparql_cursor_get_string (cursor, 0, NULL);
434 }
435
436 if (tracker_sparql_cursor_get_value_type (cursor, 1) != TRACKER_SPARQL_VALUE_TYPE_UNBOUND) {
437 state = tracker_sparql_cursor_get_string (cursor, 1, NULL);
438 }
439
440 if (tracker_sparql_cursor_get_value_type (cursor, 2) != TRACKER_SPARQL_VALUE_TYPE_UNBOUND) {
441 subl = tracker_sparql_cursor_get_string (cursor, 2, NULL);
442 }
443
444 if (tracker_sparql_cursor_get_value_type (cursor, 3) != TRACKER_SPARQL_VALUE_TYPE_UNBOUND) {
445 country = tracker_sparql_cursor_get_string (cursor, 3, NULL);
446 }
447
448 if (tracker_sparql_cursor_get_value_type (cursor, 4) != TRACKER_SPARQL_VALUE_TYPE_UNBOUND) {
449 altitude = tracker_sparql_cursor_get_string (cursor, 4, NULL);
450 }
451
452 if (tracker_sparql_cursor_get_value_type (cursor, 5) != TRACKER_SPARQL_VALUE_TYPE_UNBOUND) {
453 longitude = tracker_sparql_cursor_get_string (cursor, 5, NULL);
454 }
455
456 if (tracker_sparql_cursor_get_value_type (cursor, 6) != TRACKER_SPARQL_VALUE_TYPE_UNBOUND) {
457 latitude = tracker_sparql_cursor_get_string (cursor, 6, NULL);
458 }
459
460 /* TODO: A lot of these location fields are pretty vague and ambigious.
461 * We should go through them one by one and ensure that all of them are
462 * used sanely */
463
464 xmp_delete_property (xmp, NS_IPTC4XMP, "City");
465 xmp_delete_property (xmp, NS_PHOTOSHOP, "City");
466 if (city != NULL) {
467 xmp_set_property (xmp, NS_IPTC4XMP, "City", city, 0);
468 xmp_set_property (xmp, NS_PHOTOSHOP, "City", city, 0);
469 }
470
471 xmp_delete_property (xmp, NS_IPTC4XMP, "State");
472 xmp_delete_property (xmp, NS_IPTC4XMP, "Province");
473 xmp_delete_property (xmp, NS_PHOTOSHOP, "State");
474 if (state != NULL) {
475 xmp_set_property (xmp, NS_IPTC4XMP, "State", state, 0);
476 xmp_set_property (xmp, NS_IPTC4XMP, "Province", state, 0);
477 xmp_set_property (xmp, NS_PHOTOSHOP, "State", state, 0);
478 }
479
480 xmp_delete_property (xmp, NS_IPTC4XMP, "SubLocation");
481 xmp_delete_property (xmp, NS_PHOTOSHOP, "Location");
482 if (subl != NULL) {
483 xmp_set_property (xmp, NS_IPTC4XMP, "SubLocation", subl, 0);
484 xmp_set_property (xmp, NS_PHOTOSHOP, "Location", subl, 0);
485 }
486
487 xmp_delete_property (xmp, NS_PHOTOSHOP, "Country");
488 xmp_delete_property (xmp, NS_IPTC4XMP, "Country");
489 xmp_delete_property (xmp, NS_IPTC4XMP, "PrimaryLocationName");
490 xmp_delete_property (xmp, NS_IPTC4XMP, "CountryName");
491 if (country != NULL) {
492 xmp_set_property (xmp, NS_PHOTOSHOP, "Country", country, 0);
493 xmp_set_property (xmp, NS_IPTC4XMP, "Country", country, 0);
494 xmp_set_property (xmp, NS_IPTC4XMP, "PrimaryLocationName", country, 0);
495 xmp_set_property (xmp, NS_IPTC4XMP, "CountryName", country, 0);
496 }
497
498 xmp_delete_property (xmp, NS_EXIF, "GPSAltitude");
499 if (altitude != NULL) {
500 xmp_set_property (xmp, NS_EXIF, "GPSAltitude", altitude, 0);
501 }
502
503 xmp_delete_property (xmp, NS_EXIF, "GPSLongitude");
504 if (longitude != NULL) {
505 double coord = atof (longitude);
506 double degrees, minutes;
507 gchar *val;
508
509 minutes = modf (coord, °rees);
510
511 val = g_strdup_printf ("%3d,%f%c",
512 (int) fabs(degrees),
513 minutes,
514 coord >= 0 ? 'E' : 'W');
515
516 xmp_set_property (xmp, NS_EXIF, "GPSLongitude", val, 0);
517
518 g_free (val);
519 }
520
521 xmp_delete_property (xmp, NS_EXIF, "GPSLatitude");
522 if (latitude != NULL) {
523 double coord = atof (latitude);
524 double degrees, minutes;
525 gchar *val;
526
527 minutes = modf (coord, °rees);
528
529 val = g_strdup_printf ("%3d,%f%c",
530 (int) fabs(degrees),
531 minutes,
532 coord >= 0 ? 'N' : 'S');
533
534 xmp_set_property (xmp, NS_EXIF, "GPSLatitude", val, 0);
535
536 g_free (val);
537 }
538 }
539 }
540
541 g_object_unref (cursor);
542 g_clear_error (&error);
543 }
544
545 if (keywords) {
546 xmp_delete_property (xmp, NS_DC, "subject");
547 xmp_set_property (xmp, NS_DC, "subject", keywords->str, 0);
548 g_string_free (keywords, TRUE);
549 }
550
551 #ifdef DEBUG_XMP
552 g_print ("\nAFTER: ---- \n");
553 str = xmp_string_new ();
554 xmp_serialize_and_format (xmp, str, 0, 0, "\n", "\t", 1);
555 g_print ("%s\n", xmp_string_cstr (str));
556 xmp_string_free (str);
557 g_print ("\n --------- \n");
558 #endif
559
560 if (xmp_files_can_put_xmp (xmp_files, xmp)) {
561 xmp_files_put_xmp (xmp_files, xmp);
562 }
563
564 /* Note: We don't currently use XMP_CLOSE_SAFEUPDATE because it uses
565 * a hidden temporary file in the same directory, which is then
566 * renamed to the final name. This triggers two events:
567 * - DELETE(A) + MOVE(.hidden->A)
568 * and we really don't want the first DELETE(A) here
569 */
570 xmp_files_close (xmp_files, XMP_CLOSE_NOOPTION);
571
572 xmp_free (xmp);
573 xmp_files_free (xmp_files);
574 g_free (path);
575
576 return TRUE;
577 }
578
579 TrackerWriteback *
580 writeback_module_create (GTypeModule *module)
581 {
582 tracker_writeback_xmp_register_type (module);
583
584 return g_object_new (TRACKER_TYPE_WRITEBACK_XMP, NULL);
585 }
586
587 const gchar * const *
588 writeback_module_get_rdf_types (void)
589 {
590 static const gchar *rdf_types[] = {
591 TRACKER_NFO_PREFIX "Image",
592 TRACKER_NFO_PREFIX "Audio",
593 TRACKER_NFO_PREFIX "Video",
594 NULL
595 };
596
597 return rdf_types;
598 }