Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
import_jobs.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2025-2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18#include "system/mem_alloc.h"
19#include "common/paths.h" // DT_PATH_MAX
20#include "common/conf.h"
21#include "import_jobs.h"
22#include "common/collection.h"
23#include "common/datetime.h"
24#include "metadata/exif.h"
26#include "metadata/metadata.h"
27#include "common/styles.h"
28#include "control/control.h"
29#include "common/film.h"
30#include "common/image.h"
32
33#ifndef _WIN32
34#endif
35#include <string.h>
36#include <glib/gstdio.h>
37#include "common/utility.h"
38#ifdef __APPLE__
39#include "osx/osx.h"
40#endif
41#ifdef _WIN32
42#endif
43
44
52gboolean _create_dir(const char *path)
53{
54 if(g_mkdir_with_parents(path, 0755) == -1)
55 {
56 fprintf(stderr, "failed to create directory %s.\n", path);
57 dt_control_log(_("Impossible to create directory %s.\nThe target may be full or read-only.\n"), path);
58 return TRUE;
59 }
60 return FALSE;
61}
62
70gchar *_path_cleanup(gchar *path_in)
71{
72 gchar *clean = dt_cleanup_separators(path_in);
73 gchar *path_out = dt_util_remove_whitespace(clean);
74 dt_free(clean);
75 return path_out;
76}
77
78gchar *dt_build_filename_from_pattern(const char *const filename, const int index, dt_image_t *img, dt_control_import_t *data)
79{
82 // Borrowed references, like every other dt_variables_params_t caller (see iop/watermark.c):
83 // dt_variables_params_destroy() does not own/free filename or jobcode.
84 params->filename = filename;
85 params->sequence = index;
86 params->jobcode = data->jobcode;
87 params->imgid = UNKNOWN_IMAGE;
88 params->img = img;
90
91 gchar *file_expand = dt_variables_expand(params, data->target_file_pattern, FALSE);
92 gchar *path_expand = dt_variables_expand(params, data->target_subfolder_pattern, FALSE);
93
94 // remove this if we decide to do the correction on user's settings directly
95 gchar *file = _path_cleanup(file_expand);
96 gchar *path = _path_cleanup(path_expand);
97 dt_free(file_expand);
98 dt_free(path_expand);
99
100 gchar *dir = g_build_path(G_DIR_SEPARATOR_S, data->base_folder, path, (char *) NULL);
102 gchar *res = g_build_path(G_DIR_SEPARATOR_S, data->target_dir, file, (char *) NULL);
103
104 dt_print(DT_DEBUG_PRINT, "[Import] Importing file to %s\n", res);
105
106 dt_free(file);
107 dt_free(path);
108 dt_free(dir);
110 return res;
111}
112
119gboolean _file_exist(const char *dest_file_path)
120{
121 return !IS_NULL_PTR(dest_file_path) && dest_file_path[0] && g_file_test(dest_file_path, G_FILE_TEST_EXISTS);
122}
123
131gboolean _copy_file(const char *filename, const char *dest_file_path)
132{
133 GFile *in = g_file_new_for_path(filename);
134 GFile *out = g_file_new_for_path(dest_file_path);
135
136 gboolean res = g_file_copy(in, out, G_FILE_COPY_NONE, 0, 0, 0, NULL);
137 if(!res) dt_print(DT_DEBUG_IMPORT, "[Import] Could not copy the file %s to %s\n", filename, dest_file_path);
138
139 g_object_unref(in);
140 g_object_unref(out);
141
142 return res;
143}
144
152const int32_t _import_job(dt_control_import_t *data, gchar *img_path_to_db)
153{
154 gchar *dirname = g_strdup(dt_util_path_get_dirname(img_path_to_db));
155
156 dt_film_t film;
157 const int32_t filmid = dt_film_new(&film, dirname);
158 // Regular imports skip the per-file DT_SIGNAL_IMAGE_IMPORT (large batches
159 // would otherwise raise it hundreds of times). Folder survey imports one or
160 // a few files at a time, and Studio Capture needs that signal to know which
161 // image to display as soon as it lands, so raise it for that case only.
162 const int32_t imgid = dt_image_import(filmid, img_path_to_db, data->folder_survey);
163 dt_free(dirname);
164 return imgid;
165}
166
179void dt_import_duplicate_get_dest_name(char *xmp_dest_name, const char *dest_file_path, const int counter)
180{
181 char *norm_dest_file = dt_util_normalize_path(dest_file_path);
182 char *ext = norm_dest_file + safe_strlen(norm_dest_file);
183 while(*ext != '.' && ext > norm_dest_file) ext--;
184 const size_t name_len = safe_strlen(norm_dest_file) - safe_strlen(ext);
185
186 if(counter == 0)
187 g_snprintf(xmp_dest_name, DT_PATH_MAX, "%s.xmp", norm_dest_file);
188 else
189 g_snprintf(xmp_dest_name, DT_PATH_MAX, "%.*s_%.2d%s.xmp", (int)name_len, norm_dest_file, counter, ext);
190
191 dt_print(DT_DEBUG_IMPORT, "[Import] XMP destination name: %s\n", xmp_dest_name);
192
193 dt_free(norm_dest_file);
194}
195
203int _import_copy_xmp(const char *const filename, gchar *dest_file_path)
204{
205 int xmp_cntr = 0;
206
207 GList *xmp_files = dt_image_find_xmps(filename); // the first xmp will be the original
208 if(g_list_length(xmp_files) > 0)
209 {
210 for(GList *current_xmp = xmp_files; current_xmp; current_xmp = g_list_next(current_xmp))
211 {
212 char *xmp_source = g_strdup((char*) current_xmp->data);
213 gchar xmp_dest_name[DT_PATH_MAX] = { 0 };
214 dt_import_duplicate_get_dest_name(xmp_dest_name, dest_file_path, xmp_cntr);
215
216 // folder already created and writable, just copy.
217 int success = _copy_file(xmp_source, xmp_dest_name);
218 dt_print(DT_DEBUG_IMPORT, "[Import] copying %s to %s %s\n", xmp_source, xmp_dest_name,
219 (success) ? "succeeded" : "failed");
220 if(success) xmp_cntr++;
221 dt_free(xmp_source);
222 }
223 }
224 g_list_free(xmp_files);
225 xmp_files = NULL;
226 return xmp_cntr;
227}
228
229int _import_copy_txt(const char *const filename, const char *dest_file_path)
230{
231 char *txt_source = dt_image_get_text_path_from_path(filename);
232 if(IS_NULL_PTR(txt_source)) return 0;
233
234 char *txt_dest = dt_image_build_text_path_from_path(dest_file_path);
235 int success = 0;
236
237 if(!IS_NULL_PTR(txt_dest))
238 {
239 success = _copy_file(txt_source, txt_dest);
240 dt_print(DT_DEBUG_IMPORT, "[Import] copying %s to %s %s\n", txt_source, txt_dest,
241 (success) ? "succeeded" : "failed");
242 }
243
244 dt_free(txt_dest);
245 dt_free(txt_source);
246 return success ? 1 : 0;
247}
248
259int _import_copy_file(const char *const filename, const int index, dt_control_import_t *data, gchar *img_path_to_db, size_t pathname_len, GList **discarded)
260{
261 dt_image_t *img = dt_alloc_align(sizeof(dt_image_t)); // dt_image_t is 64-aligned, see #1212
262 dt_image_init(img);
263
264 // Generate file I/O only if the pattern is using EXIF variables.
265 // Otherwise, discard it since it's really expensive if the file is on external/remote storage.
266 // This is mandatory BEFORE expanding variables in pattern
267 if(strstr(data->target_file_pattern, "$(EXIF") != NULL
268 || strstr(data->target_subfolder_pattern, "$(EXIF") != NULL )
269 {
270 dt_print(DT_DEBUG_IMPORT, "[Import] EXIF will be read for %s because the pattern needs it (performance penalty)\n", filename);
271 dt_exif_read(img, filename);
272 }
273
274 gchar *dest_file_path = dt_build_filename_from_pattern(filename, index, img, data);
275 dt_print(DT_DEBUG_IMPORT, "[Import] Image %s will be copied into %s\n", filename, dest_file_path);
276 dt_free_align(img);
277
278 int process = TRUE;
279 int copied = 0;
280
281 gboolean exists = _file_exist(dest_file_path);
282
283 // Resolve a name collision according to the requested policy. UNIQUE rewrites
284 // the destination to a free "<stem>_NN.<ext>" so the copy proceeds normally.
285 if(exists && data->on_conflict == DT_IMPORT_ONCONFLICT_UNIQUE)
286 {
287 const char *dot = strrchr(dest_file_path, '.');
288 const int stem_len = dot ? (int)(dot - dest_file_path) : (int)strlen(dest_file_path);
289 const char *ext = dot ? dot : "";
290 char *unique = NULL;
291 for(int seq = 1; seq < 10000; seq++)
292 {
293 dt_free(unique);
294 unique = g_strdup_printf("%.*s_%02d%s", stem_len, dest_file_path, seq, ext);
295 if(!_file_exist(unique)) break;
296 }
297 dt_free(dest_file_path);
298 dest_file_path = unique;
299 exists = FALSE;
300 }
301
302 // OVERWRITE deletes the stale destination so g_file_copy() (G_FILE_COPY_NONE)
303 // does not fail on an existing target.
304 if(exists && data->on_conflict == DT_IMPORT_ONCONFLICT_OVERWRITE)
305 {
306 g_unlink(dest_file_path);
307 exists = FALSE;
308 }
309
310 if(!exists)
311 {
312 if(!dt_util_dir_exist(data->target_dir))
314 else
315 dt_print(DT_DEBUG_PRINT, "[Import] target folder %s already exists. Nothing to do.\n", data->target_dir);
316
317 if(process)
319 else
320 fprintf(stdout, "[Import] Unable to create the target folder %s.\n", data->target_dir);
321
322 if(process)
323 {
324 process = _copy_file(filename, dest_file_path);
325 copied = process;
326 }
327 else
328 fprintf(stdout, "[Import] Not allowed to write in the %s folder.\n", data->target_dir);
329
330 if(process)
331 {
332 _import_copy_xmp(filename, dest_file_path);
333 _import_copy_txt(filename, dest_file_path);
334 }
335
336 if(process)
337 g_strlcpy(img_path_to_db, dest_file_path, pathname_len);
338 else
339 fprintf(stderr, "[Import] Unable to copy the file %s to %s.\n", img_path_to_db, dest_file_path);
340 }
341 else
342 {
343 // SKIP: keep and import the pre-existing destination file.
344 *discarded = g_list_prepend(*discarded, g_strdup(filename));
345 g_strlcpy(img_path_to_db, dest_file_path, pathname_len);
346 dt_print(DT_DEBUG_IMPORT, "[Import] File copy skipped, the target file %s already exists on the destination.\n", dest_file_path);
347 }
348
349 dt_free(dest_file_path);
350 return process ? copied : -1;
351}
352
353void _write_xmp_id(const char *filename, int32_t imgid)
354{
355 GList *res = dt_metadata_get(imgid, "Xmp.darktable.image_id", NULL);
356 if(!IS_NULL_PTR(res))
357 {
358 // Image ID is already set in metadata, don't overwrite it
359 g_list_free_full(res, dt_free_gpointer);
360 res = NULL;
361 return;
362 }
363 // else : init it
364 GError *error = NULL;
365 GFile *gfile = g_file_new_for_path(filename);
366 GFileInfo *info = g_file_query_info(gfile,
367 G_FILE_ATTRIBUTE_STANDARD_NAME ","
368 G_FILE_ATTRIBUTE_TIME_MODIFIED,
369 G_FILE_QUERY_INFO_NONE, NULL, &error);
370 const char *fn = g_file_info_get_name(info);
371
372 const time_t datetime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
373 char dt_txt[DT_DATETIME_EXIF_LENGTH];
374 dt_datetime_unix_to_exif(dt_txt, sizeof(dt_txt), &datetime);
375 const char *id = g_strconcat(fn, "-", dt_txt, NULL);
376 dt_metadata_set(imgid, "Xmp.darktable.image_id", id, FALSE);
377 g_object_unref(info);
378 g_object_unref(gfile);
379 g_clear_error(&error);
380}
381
390int32_t _import_image(const GList *img, dt_control_import_t *data, const int index, GList **discarded, int *xmps)
391{
392 const char *filename = (const char*) img->data;
393
394 gchar img_path_to_db[DT_PATH_MAX] = { 0 };
395 gboolean process_error = FALSE;
396 int copy_status = 0;
397 int32_t imgid = UNKNOWN_IMAGE;
398
399 if(data->copy)
400 {
401 // Copy the file to destination folder, expanding variables internally
402 copy_status = _import_copy_file(filename, index + 1, data, img_path_to_db, sizeof(img_path_to_db), discarded);
403 process_error = copy_status < 0;
404 }
405 else
406 // destination = origin, nothing to do
407 g_strlcpy(img_path_to_db, filename, sizeof(img_path_to_db));
408
409 if(process_error)
410 ;
411 else if(img_path_to_db[0] == 0)
412 fprintf(stderr, "[Import] Could not import file from disk: empty file path\n");
413 else
414 {
415 imgid = _import_job(data, img_path_to_db);
416
417 if(imgid == UNKNOWN_IMAGE)
418 {
419 dt_control_log(_("Error importing file in collection: %s"), img_path_to_db);
420 fprintf(stderr, "[Import] Error importing file in collection: %s", img_path_to_db);
421 }
422 else
423 {
424 // read all sidecar files (including the original one) and import them if not found in db.
425 *xmps = dt_image_read_duplicates(imgid, img_path_to_db, FALSE);
426 dt_print(DT_DEBUG_IMPORT, "[Import] Found and imported %i XMP for %s.\n", *xmps, img_path_to_db);
427 dt_print(DT_DEBUG_IMPORT, "[Import] successfully imported %s in DB at imgid %i\n", img_path_to_db, imgid);
428
429 if(data->delete_source && copy_status == 1)
430 {
431 // Compare the complete source and destination byte streams before
432 // deleting files from temporary ingest storage.
433 gboolean identical = FALSE;
434 GStatBuf source_stat;
435 GStatBuf destination_stat;
436 if(g_stat(filename, &source_stat) == 0
437 && g_stat(img_path_to_db, &destination_stat) == 0
438 && source_stat.st_size == destination_stat.st_size)
439 {
440 FILE *source = g_fopen(filename, "rb");
441 FILE *destination = g_fopen(img_path_to_db, "rb");
442 if(!IS_NULL_PTR(source) && !IS_NULL_PTR(destination))
443 {
444 const size_t buffer_size = 64 * 1024;
445 unsigned char *source_buffer = malloc(buffer_size);
446 unsigned char *destination_buffer = malloc(buffer_size);
447 identical = !IS_NULL_PTR(source_buffer) && !IS_NULL_PTR(destination_buffer);
448
449 while(identical)
450 {
451 const size_t source_read = fread(source_buffer, 1, buffer_size, source);
452 const size_t destination_read = fread(destination_buffer, 1, buffer_size, destination);
453 if(source_read != destination_read
454 || memcmp(source_buffer, destination_buffer, source_read))
455 identical = FALSE;
456
457 if(source_read < buffer_size)
458 {
459 if(ferror(source) || ferror(destination)) identical = FALSE;
460 break;
461 }
462 }
463
464 dt_free(source_buffer);
465 dt_free(destination_buffer);
466 }
467
468 if(!IS_NULL_PTR(source)) fclose(source);
469 if(!IS_NULL_PTR(destination)) fclose(destination);
470 }
471
472 if(identical)
473 {
474 if(g_unlink(filename) != 0)
475 dt_control_log(_("The imported file was verified but the original could not be deleted: %s"), filename);
476 }
477 else
478 dt_control_log(_("The imported file differs from the original, which was not deleted: %s"), filename);
479 }
480
481 // Studio capture auto-styling: replace the freshly imported default
482 // history with the first style, then stack the remaining styles in the
483 // user-defined order (source wins on conflicts).
484 if(!IS_NULL_PTR(data->styles))
485 {
486 dt_hm_batch_state_t batch = { 0 };
487 for(GList *s = data->styles; s; s = g_list_next(s))
488 {
489 const char *style_name = (const char *)s->data;
490 const int32_t style_id = dt_styles_get_id_by_name(style_name);
491 if(style_id <= 0) continue;
492 dt_styles_apply_to_image_merge(style_name, style_id, imgid, DT_HISTORY_MERGE_APPEND, &batch);
493 }
495
496 // The styles were written straight to DB: reload cached metadata, drop
497 // the stale mipmap and refresh thumbnails (lighttable + filmstrip).
499 }
500 }
501 }
502
503 return imgid;
504}
505
509static void _refresh_progress_counter(dt_job_t *job, const int elements, const int index,
510 const gboolean folder_survey)
511{
512 gchar message[128] = { 0 };
513 double fraction = (double)index / (double)elements;
514 if(folder_survey)
515 snprintf(message, sizeof(message),
516 ngettext("Capture: importing %i/%i image", "Capture: importing %i/%i images", index),
517 index, elements);
518 else
519 snprintf(message, sizeof(message),
520 ngettext("Importing %i/%i image", "Importing %i/%i images", index),
521 index, elements);
523 dt_control_job_set_progress(job, fraction);
524 g_usleep(100);
525}
526
528{
530 dt_control_import_t *data = params->data;
531
532 int index = 0;
533 int xmps = 0; // number of xmps imported in db.
534 int32_t imgid = UNKNOWN_IMAGE;
535 gint64 last_collection_refresh = 0;
536
537 // What this import may do to the view the user is in, decided once from what the import IS. An
538 // automatic (folder survey) one never moves them: Studio Capture displays the capture itself,
539 // from DT_SIGNAL_IMAGE_IMPORT, and the survey outlives its atelier. A requested one shows the
540 // grid as images arrive, and opens the darkroom instead if it turns out to have imported one.
541 const dt_collection_import_view_t first_image_policy = data->folder_survey
544 const dt_collection_import_view_t single_image_policy = data->folder_survey
547
548 for(GList *img = g_list_first(data->imgs); img; img = g_list_next(img))
549 {
550 dt_print(DT_DEBUG_IMPORT, "[Import] starting import of image #%i...\n", index);
551
552 _refresh_progress_counter(job, data->elements, index, data->folder_survey);
553 imgid = _import_image(img, data, index, &data->discarded, &xmps);
554 if(!IS_NULL_PTR(data->file_imported))
555 data->file_imported((const char *)img->data, imgid > UNKNOWN_IMAGE, data->callback_data);
556
557 if(imgid > UNKNOWN_IMAGE)
558 {
559 // On the first image, try to switch the current filmroll to the imported image's folder.
560 // dt_collection_load_filmroll() silently declines to do anything (no collection refresh)
561 // when it cannot switch folders, e.g. the collect module is not on the "Folders" tab. In
562 // that case a single imported image would never show up until the user reloads the
563 // collection by hand (issue #860). So always run a collection update afterwards: it
564 // re-runs the current query and makes newly-imported matching images appear.
565 if(index == 0)
566 dt_collection_load_filmroll(dt_collection_get_global(), imgid, first_image_policy, TRUE);
567
568 // known_image_folder is NULL: in copy mode the image's final DB location can be a
569 // completely different, pattern-generated folder from its original source path, which is
570 // all this loop has at hand -- dt_collection_notify_imported() must resolve it fresh.
571 dt_collection_notify_imported(imgid, NULL, &last_collection_refresh);
572
573 index++;
574 }
575 }
576
577 // Guarantee the final state is reflected even if the last few images landed inside the throttle window.
578 if(index > 0)
580
581 dt_conf_set_int("ui_last/nb_imported", index);
582
583 if(index == 0)
584 {
585 dt_control_log(data->folder_survey ? _("Capture: No image imported!") : _("No image imported!"));
586 fprintf(stderr, "No image imported!\n\n");
587 return 1;
588 }
589
590 // Don't open the picture in darkroom if more than 1 xmp (= duplicates) has been imported: the
591 // single file then stands for several images in the DB and none of them is the obvious one to
592 // open. Zero xmp is the ordinary case of a file with no sidecar (and of a library configured
593 // not to write any), still exactly one image: open it like any other single import.
594 if(index == 1 && xmps <= 1)
595 {
596 // A requested single image opens in the darkroom, which is announcement enough -- only the
597 // survey, which stays where it is, says anything.
598 if(data->folder_survey) dt_control_log(_("Capture: imported 1 image."));
599 dt_collection_load_filmroll(dt_collection_get_global(), imgid, single_image_policy, TRUE);
600 return 0;
601 }
602
604 ? ngettext("Capture: imported %d image.", "Capture: imported %d images.", index)
605 : ngettext("Imported %d image", "Imported %d images", index), index);
606 fprintf(stdout, "%d files imported in database.\n\n", index);
607
608 return 0;
609}
610
612{
613 g_date_time_unref(data->datetime);
614 dt_free(data->jobcode);
615 dt_free(data->base_folder);
618 dt_free(data->target_dir);
619 if(data->styles)
620 {
621 g_list_free_full(data->styles, dt_free_gpointer);
622 data->styles = NULL;
623 }
626 data->callback_data = NULL;
627
628 // GList of pathes stored as *char. We need to free the list and the *char
629 if(data->discarded)
630 {
631 g_list_free_full(data->discarded, dt_free_gpointer);
632 data->discarded = NULL;
633 }
634 if(data->imgs)
635 {
636 g_list_free_full(data->imgs, dt_free_gpointer);
637 data->imgs = NULL;
638 }
639}
640
641/* Installed by the GUI at startup; absent under ansel-cli, where the discarded list is
642 * simply freed with the rest -- a headless import has nobody to show a dialog to. */
644
649
650/* The discarded-files recap dialog lives in gui/import.c now: it was the only GUI
651 * code in this job file, and the only reason a control-layer TU included
652 * widgets/ and gui/ headers. The job reaches it through the handler below. */
653
655{
657 dt_control_import_t *data = params->data;
658
659 // Display a recap of files that weren't copied
660 if(g_list_length(data->discarded) > 0 && _discarded_files_handler)
661 {
662 // Called on THIS worker thread: getting onto the GUI main loop is the handler's
663 // business (g_main_context_invoke is GTK machinery, not a job's). The handler owns
664 // freeing data and params, whenever it is done with them.
666 }
667 else
668 {
670 dt_free(data);
672 }
673}
674
676{
678 if(IS_NULL_PTR(params)) return NULL;
679
680 params->data = g_malloc0(sizeof(dt_control_import_t));
681 if(IS_NULL_PTR(params->data))
682 {
684 return NULL;
685 }
686 return params;
687}
688
690{
692 if(IS_NULL_PTR(job)) return NULL;
694 if(IS_NULL_PTR(params))
695 {
697 return NULL;
698 }
699 memcpy(params->data, &data, sizeof(dt_control_import_t));
700 params->index = NULL;
701 dt_control_job_add_progress(job, _("import"), FALSE);
703 return job;
704}
705
707{
709 if(IS_NULL_PTR(job))
710 {
711 // Report every source as failed so asynchronous clients can release their queued state.
712 for(GList *img = g_list_first(data.imgs); img; img = g_list_next(img))
713 if(!IS_NULL_PTR(data.file_imported))
714 data.file_imported((const char *)img->data, FALSE, data.callback_data);
715
717 return 1;
718 }
719
721}
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
Definition ashift.c:3179
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
Definition collection.c:837
dt_collection_t * dt_collection_get_global(void)
Definition collection.c:138
void dt_collection_notify_imported(const int32_t imgid, const gchar *known_image_folder, gint64 *last_refresh_us)
void dt_collection_load_filmroll(dt_collection_t *collection, const int32_t imgid, dt_collection_import_view_t view_policy, gboolean set_mouse_over)
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:155
@ DT_COLLECTION_CHANGE_NEW_QUERY
Definition collection.h:162
dt_collection_import_view_t
Definition collection.h:269
@ DT_COLLECTION_IMPORT_VIEW_KEEP
Definition collection.h:273
@ DT_COLLECTION_IMPORT_VIEW_GRID
Definition collection.h:275
@ DT_COLLECTION_IMPORT_VIEW_IMAGE
Definition collection.h:277
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_conf_set_int(const char *name, int val)
int32_t dt_image_import(const int32_t film_id, const char *filename, gboolean raise_signals)
char * dt_image_get_text_path_from_path(const char *image_path)
void dt_image_init(dt_image_t *img)
GList * dt_image_find_xmps(const char *filename)
int dt_image_read_duplicates(const uint32_t id, const char *filename, const gboolean clear_selection)
char * dt_image_build_text_path_from_path(const char *image_path)
void dt_image_history_changed(const int32_t imgid, const gboolean refresh_filmstrip)
void dt_control_log(const char *msg,...)
Definition control.c:824
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:651
void * dt_control_image_enumerator_alloc()
void dt_control_image_enumerator_cleanup(void *p)
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
Definition darktable.c:508
gboolean dt_datetime_unix_to_exif(char *exif, const size_t exif_size, const time_t *unix)
Definition datetime.c:212
#define DT_DATETIME_EXIF_LENGTH
Definition datetime.h:39
const int res
Definition dtpthread.h:351
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1994
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
int dt_film_new(dt_film_t *film, const char *directory)
Definition film.c:129
void dt_hm_batch_state_cleanup(dt_hm_batch_state_t *batch)
Release resources held by a batch state (the cached order). Safe to call on a zeroed state.
@ DT_HISTORY_MERGE_APPEND
#define UNKNOWN_IMAGE
Definition image.h:78
int _import_copy_file(const char *const filename, const int index, dt_control_import_t *data, gchar *img_path_to_db, size_t pathname_len, GList **discarded)
copy a file to a destination path after checking if everything is allright.
static void * _control_import_alloc()
int _import_copy_xmp(const char *const filename, gchar *dest_file_path)
Attempt to find all sidecar XMP files along an image file and import (copy) it to destination.
gboolean _create_dir(const char *path)
Creates folders from path. Returns TRUE if success.
Definition import_jobs.c:52
static void _control_import_job_cleanup(void *p)
gchar * dt_build_filename_from_pattern(const char *const filename, const int index, dt_image_t *img, dt_control_import_t *data)
Build a full path for a given image file, given a pattern.
Definition import_jobs.c:78
gboolean _copy_file(const char *filename, const char *dest_file_path)
Just copy a file. Returns 1 if success.
static dt_control_import_discarded_handler_t _discarded_files_handler
void dt_import_duplicate_get_dest_name(char *xmp_dest_name, const char *dest_file_path, const int counter)
Gets the computed xmp file name with apropriate number for import copy. It computes a duplicate name ...
int32_t _import_image(const GList *img, dt_control_import_t *data, const int index, GList **discarded, int *xmps)
process to copy (or not) and import an image to database.
int dt_control_import(dt_control_import_t data)
Process a list of images to import with or without copying the files on an arbitrary hard-drive.
static int32_t _control_import_job_run(dt_job_t *job)
static dt_job_t * _control_import_job_create(dt_control_import_t data)
static void _refresh_progress_counter(dt_job_t *job, const int elements, const int index, const gboolean folder_survey)
Update import progress with wording matching the import origin.
gboolean _file_exist(const char *dest_file_path)
Tests if file exist. Returns 1 if so.
const int32_t _import_job(dt_control_import_t *data, gchar *img_path_to_db)
Add an image entry in the database and returns its imgID.
void _write_xmp_id(const char *filename, int32_t imgid)
gchar * _path_cleanup(gchar *path_in)
Replaces separator depending of the current OS and removes whitespaces.
Definition import_jobs.c:70
void dt_control_import_set_discarded_files_handler(dt_control_import_discarded_handler_t handler)
int _import_copy_txt(const char *const filename, const char *dest_file_path)
void dt_control_import_data_free(dt_control_import_t *data)
@ DT_IMPORT_ONCONFLICT_UNIQUE
Definition import_jobs.h:39
@ DT_IMPORT_ONCONFLICT_OVERWRITE
Definition import_jobs.h:38
void(* dt_control_import_discarded_handler_t)(struct dt_control_image_enumerator_t *params)
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:137
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:407
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:131
void dt_control_job_set_progress(dt_job_t *job, double value)
Definition jobs.c:628
void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
Definition jobs.c:614
void dt_control_job_set_progress_message(dt_job_t *job, const char *message)
Definition jobs.c:622
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:114
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:155
@ DT_JOB_QUEUE_USER_FG
Definition jobs.h:54
@ DT_DEBUG_PRINT
Definition logging.h:65
@ DT_DEBUG_IMPORT
Definition logging.h:77
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
void dt_metadata_set(const int32_t imgid, const char *key, const char *value, const gboolean undo_on)
GList * dt_metadata_get(const int id, const char *key, uint32_t *count)
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
int dt_styles_apply_to_image_merge(const char *name, const int style_id, const int32_t newimgid, const dt_history_merge_strategy_t mode, dt_hm_batch_state_t *batch)
int32_t dt_styles_get_id_by_name(const char *name)
char * dt_variables_expand(dt_variables_params_t *params, gchar *source, gboolean iterate)
void dt_variables_params_destroy(dt_variables_params_t *params)
void dt_variables_params_init(dt_variables_params_t **params)
void dt_variables_set_datetime(dt_variables_params_t *params, GDateTime *datetime)
dt_import_onconflict_t on_conflict
Definition import_jobs.h:51
GDateTime * datetime
Definition import_jobs.h:45
GDestroyNotify callback_data_free
Definition import_jobs.h:89
void(* file_imported)(const char *source, gboolean success, gpointer user_data)
Optional per-file completion method.
Definition import_jobs.h:87
char * target_subfolder_pattern
Definition import_jobs.h:65
typedef double((*spd)(unsigned long int wavelength, double TempK))
gchar * dt_cleanup_separators(gchar *string)
Definition utility.c:1059
gboolean dt_util_dir_exist(const char *dir)
Definition utility.c:371
gchar * dt_util_path_get_dirname(const gchar *filename)
Definition utility.c:777
gchar * dt_util_normalize_path(const gchar *_input)
Definition utility.c:683
size_t safe_strlen(const char *str)
check if the string is empty or NULL before calling strlen()
Definition utility.c:94
gboolean dt_util_test_writable_dir(const char *path)
Definition utility.c:348
gchar * dt_util_remove_whitespace(const gchar *path)
Definition utility.c:1070