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 Copyright (C) 2026 Guillaume Stutin.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "system/mem_alloc.h"
20#include "common/paths.h" // DT_PATH_MAX
21#include "common/conf.h"
22#include "import_jobs.h"
23#include "import_sequence.h"
24#include "common/collection.h"
25#include "common/datetime.h"
26#include "metadata/exif.h"
28#include "metadata/metadata.h"
29#include "common/styles.h"
30#include "control/control.h"
31#include "common/film.h"
32#include "common/image.h"
34
35#ifndef _WIN32
36#endif
37#include <string.h>
38#include <glib/gstdio.h>
39#include "common/utility.h"
40#ifdef __APPLE__
41#include "osx/osx.h"
42#endif
43#ifdef _WIN32
44#endif
45
46
54gboolean _create_dir(const char *path)
55{
56 if(g_mkdir_with_parents(path, 0755) == -1)
57 {
58 fprintf(stderr, "failed to create directory %s.\n", path);
59 dt_control_log(_("Impossible to create directory %s.\nThe target may be full or read-only.\n"), path);
60 return TRUE;
61 }
62 return FALSE;
63}
64
72gchar *_path_cleanup(gchar *path_in)
73{
74 gchar *clean = dt_cleanup_separators(path_in);
75 gchar *path_out = dt_util_remove_whitespace(clean);
76 dt_free(clean);
77 return path_out;
78}
79
80gchar *dt_build_filename_from_pattern(const char *const filename, const int index, dt_image_t *img, dt_control_import_t *data)
81{
84 // Borrowed references, like every other dt_variables_params_t caller (see iop/watermark.c):
85 // dt_variables_params_destroy() does not own/free filename or jobcode.
86 params->filename = filename;
87 params->sequence = index;
88 params->jobcode = data->jobcode;
89 params->imgid = UNKNOWN_IMAGE;
90 params->img = img;
92
95 {
97 return NULL;
98 }
99
100 gchar *file_expand = dt_variables_expand(params, data->target_file_pattern, FALSE);
101 gchar *path_expand = dt_variables_expand(params, data->target_subfolder_pattern, FALSE);
102 if(IS_NULL_PTR(file_expand) || IS_NULL_PTR(path_expand))
103 {
104 dt_free(file_expand);
105 dt_free(path_expand);
107 return NULL;
108 }
109
110 // remove this if we decide to do the correction on user's settings directly
111 gchar *file = _path_cleanup(file_expand);
112 gchar *path = _path_cleanup(path_expand);
113 dt_free(file_expand);
114 dt_free(path_expand);
115 if(IS_NULL_PTR(file) || IS_NULL_PTR(path))
116 {
117 dt_free(file);
118 dt_free(path);
120 return NULL;
121 }
122
123 gchar *dir = g_build_path(G_DIR_SEPARATOR_S, data->base_folder, path, (char *) NULL);
124 gchar *target_dir = IS_NULL_PTR(dir) ? NULL : dt_util_normalize_path(dir);
125 gchar *res = IS_NULL_PTR(target_dir) ? NULL : g_build_path(G_DIR_SEPARATOR_S, target_dir, file, (char *) NULL);
126
127 if(!IS_NULL_PTR(res))
128 {
129 data->target_dir = target_dir;
130 target_dir = NULL;
131 dt_print(DT_DEBUG_PRINT, "[Import] Importing file to %s\n", res);
132 }
133
134 dt_free(file);
135 dt_free(path);
136 dt_free(dir);
137 dt_free(target_dir);
139 return res;
140}
141
148gboolean _file_exist(const char *dest_file_path)
149{
150 return !IS_NULL_PTR(dest_file_path) && dest_file_path[0] && g_file_test(dest_file_path, G_FILE_TEST_EXISTS);
151}
152
160gboolean _copy_file(const char *filename, const char *dest_file_path)
161{
162 GFile *in = g_file_new_for_path(filename);
163 GFile *out = g_file_new_for_path(dest_file_path);
164
165 gboolean res = g_file_copy(in, out, G_FILE_COPY_NONE, 0, 0, 0, NULL);
166 if(!res) dt_print(DT_DEBUG_IMPORT, "[Import] Could not copy the file %s to %s\n", filename, dest_file_path);
167
168 g_object_unref(in);
169 g_object_unref(out);
170
171 return res;
172}
173
181const int32_t _import_job(dt_control_import_t *data, gchar *img_path_to_db)
182{
183 gchar *dirname = g_strdup(dt_util_path_get_dirname(img_path_to_db));
184
185 dt_film_t film;
186 const int32_t filmid = dt_film_new(&film, dirname);
187 // Regular imports skip the per-file DT_SIGNAL_IMAGE_IMPORT (large batches
188 // would otherwise raise it hundreds of times). Folder survey imports one or
189 // a few files at a time, and Studio Capture needs that signal to know which
190 // image to display as soon as it lands, so raise it for that case only.
191 const int32_t imgid = dt_image_import(filmid, img_path_to_db, data->folder_survey);
192 dt_free(dirname);
193 return imgid;
194}
195
208void dt_import_duplicate_get_dest_name(char *xmp_dest_name, const char *dest_file_path, const int counter)
209{
210 char *norm_dest_file = dt_util_normalize_path(dest_file_path);
211 char *ext = norm_dest_file + safe_strlen(norm_dest_file);
212 while(*ext != '.' && ext > norm_dest_file) ext--;
213 const size_t name_len = safe_strlen(norm_dest_file) - safe_strlen(ext);
214
215 if(counter == 0)
216 g_snprintf(xmp_dest_name, DT_PATH_MAX, "%s.xmp", norm_dest_file);
217 else
218 g_snprintf(xmp_dest_name, DT_PATH_MAX, "%.*s_%.2d%s.xmp", (int)name_len, norm_dest_file, counter, ext);
219
220 dt_print(DT_DEBUG_IMPORT, "[Import] XMP destination name: %s\n", xmp_dest_name);
221
222 dt_free(norm_dest_file);
223}
224
232int _import_copy_xmp(const char *const filename, const gchar *dest_file_path)
233{
234 int xmp_cntr = 0;
235
236 GList *xmp_files = dt_image_find_xmps(filename); // the first xmp will be the original
237 if(g_list_length(xmp_files) > 0)
238 {
239 for(GList *current_xmp = xmp_files; current_xmp; current_xmp = g_list_next(current_xmp))
240 {
241 char *xmp_source = g_strdup((char*) current_xmp->data);
242 gchar xmp_dest_name[DT_PATH_MAX] = { 0 };
243 dt_import_duplicate_get_dest_name(xmp_dest_name, dest_file_path, xmp_cntr);
244
245 // folder already created and writable, just copy.
246 int success = _copy_file(xmp_source, xmp_dest_name);
247 dt_print(DT_DEBUG_IMPORT, "[Import] copying %s to %s %s\n", xmp_source, xmp_dest_name,
248 (success) ? "succeeded" : "failed");
249 if(success) xmp_cntr++;
250 dt_free(xmp_source);
251 }
252 }
253 g_list_free(xmp_files);
254 xmp_files = NULL;
255 return xmp_cntr;
256}
257
258int _import_copy_txt(const char *const filename, const char *dest_file_path)
259{
260 char *txt_source = dt_image_get_text_path_from_path(filename);
261 if(IS_NULL_PTR(txt_source)) return 0;
262
263 char *txt_dest = dt_image_build_text_path_from_path(dest_file_path);
264 int success = 0;
265
266 if(!IS_NULL_PTR(txt_dest))
267 {
268 success = _copy_file(txt_source, txt_dest);
269 dt_print(DT_DEBUG_IMPORT, "[Import] copying %s to %s %s\n", txt_source, txt_dest,
270 (success) ? "succeeded" : "failed");
271 }
272
273 dt_free(txt_dest);
274 dt_free(txt_source);
275 return success ? 1 : 0;
276}
277
289int _import_copy_file(const char *const filename, const char *const dest_file_path, dt_control_import_t *data,
290 gchar *img_path_to_db, size_t pathname_len, GList **discarded)
291{
292 dt_print(DT_DEBUG_IMPORT, "[Import] Image %s will be copied into %s\n", filename, dest_file_path);
293
294 int copied = -1;
295 char *unique_destination = NULL;
296 const char *copy_destination = dest_file_path;
297
298 gboolean exists = _file_exist(copy_destination);
299
300 // Resolve a name collision according to the requested policy. UNIQUE rewrites
301 // the destination to a free "<stem>_NN.<ext>" so the copy proceeds normally.
302 if(exists && data->on_conflict == DT_IMPORT_ONCONFLICT_UNIQUE)
303 {
304 const char *dot = strrchr(copy_destination, '.');
305 const int stem_len = dot ? (int)(dot - copy_destination) : (int)strlen(copy_destination);
306 const char *ext = dot ? dot : "";
307 for(int seq = 1; seq < 10000; seq++)
308 {
309 dt_free(unique_destination);
310 unique_destination = g_strdup_printf("%.*s_%02d%s", stem_len, copy_destination, seq, ext);
311 if(!_file_exist(unique_destination)) break;
312 }
313 copy_destination = unique_destination;
314 exists = FALSE;
315 }
316
317 // OVERWRITE deletes the stale destination so g_file_copy() (G_FILE_COPY_NONE)
318 // does not fail on an existing target.
319 if(exists && data->on_conflict == DT_IMPORT_ONCONFLICT_OVERWRITE)
320 {
321 g_unlink(copy_destination);
322 exists = FALSE;
323 }
324
325 if(exists)
326 {
327 *discarded = g_list_prepend(*discarded, g_strdup(filename));
328 g_strlcpy(img_path_to_db, copy_destination, pathname_len);
329 dt_print(DT_DEBUG_IMPORT, "[Import] File copy skipped, the target file %s already exists on the destination.\n", copy_destination);
330 copied = 0;
331 goto cleanup;
332 }
333
335 dt_print(DT_DEBUG_PRINT, "[Import] target folder %s already exists. Nothing to do.\n", data->target_dir);
336 else if(_create_dir(data->target_dir))
337 {
338 fprintf(stdout, "[Import] Unable to create the target folder %s.\n", data->target_dir);
339 goto cleanup;
340 }
341
343 {
344 fprintf(stdout, "[Import] Not allowed to write in the %s folder.\n", data->target_dir);
345 goto cleanup;
346 }
347
348 if(!_copy_file(filename, copy_destination))
349 {
350 fprintf(stderr, "[Import] Unable to copy the file %s to %s.\n", filename, copy_destination);
351 goto cleanup;
352 }
353
354 _import_copy_xmp(filename, copy_destination);
355 _import_copy_txt(filename, copy_destination);
356 g_strlcpy(img_path_to_db, copy_destination, pathname_len);
357 copied = 1;
358
359cleanup:
360 dt_free(unique_destination)
361 return copied;
362}
363
364void _write_xmp_id(const char *filename, int32_t imgid)
365{
366 GList *res = dt_metadata_get(imgid, "Xmp.darktable.image_id", NULL);
367 if(!IS_NULL_PTR(res))
368 {
369 // Image ID is already set in metadata, don't overwrite it
370 g_list_free_full(res, dt_free_gpointer);
371 res = NULL;
372 return;
373 }
374 // else : init it
375 GError *error = NULL;
376 GFile *gfile = g_file_new_for_path(filename);
377 GFileInfo *info = g_file_query_info(gfile,
378 G_FILE_ATTRIBUTE_STANDARD_NAME ","
379 G_FILE_ATTRIBUTE_TIME_MODIFIED,
380 G_FILE_QUERY_INFO_NONE, NULL, &error);
381 const char *fn = g_file_info_get_name(info);
382
383 const time_t datetime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
384 char dt_txt[DT_DATETIME_EXIF_LENGTH];
385 dt_datetime_unix_to_exif(dt_txt, sizeof(dt_txt), &datetime);
386 const char *id = g_strconcat(fn, "-", dt_txt, NULL);
387 dt_metadata_set(imgid, "Xmp.darktable.image_id", id, FALSE);
388 g_object_unref(info);
389 g_object_unref(gfile);
390 g_clear_error(&error);
391}
392
406int32_t _import_image(const GList *img, dt_control_import_t *data, const char *final_destination, GList **discarded, int *xmps)
407{
408 const char *filename = (const char*) img->data;
409
410 gchar img_path_to_db[DT_PATH_MAX] = { 0 };
411 int copy_status = 0;
412
413 if(data->copy)
414 {
415 copy_status = _import_copy_file(filename, final_destination, data, img_path_to_db, sizeof(img_path_to_db), discarded);
416 if(copy_status < 0) return UNKNOWN_IMAGE;
417 }
418 else
419 // destination = origin, nothing to do
420 g_strlcpy(img_path_to_db, filename, sizeof(img_path_to_db));
421
422 if(img_path_to_db[0] == 0)
423 {
424 fprintf(stderr, "[Import] Could not import file from disk: empty file path\n");
425 return UNKNOWN_IMAGE;
426 }
427
428 const int32_t imgid = _import_job(data, img_path_to_db);
429 if(imgid <= 0)
430 {
431 dt_control_alert(_("Import problem"), _("Error importing file in collection"), img_path_to_db);
432 return UNKNOWN_IMAGE;
433 }
434
435 *xmps = dt_image_read_duplicates(imgid, img_path_to_db, FALSE);
436 dt_print(DT_DEBUG_IMPORT, "[Import] Found and imported %i XMP for %s.\n", *xmps, img_path_to_db);
437 dt_print(DT_DEBUG_IMPORT, "[Import] successfully imported %s in DB at imgid %i\n", img_path_to_db, imgid);
438
439 if(!data->delete_source || copy_status != 1) goto apply_styles;
440
441 int identical = 0;
442 GStatBuf source_stat;
443 GStatBuf destination_stat;
444 if(g_stat(filename, &source_stat) == 0
445 && g_stat(img_path_to_db, &destination_stat) == 0
446 && source_stat.st_size == destination_stat.st_size)
447 {
448 FILE *source = g_fopen(filename, "rb");
449 FILE *destination = g_fopen(img_path_to_db, "rb");
450 if(!IS_NULL_PTR(source) && !IS_NULL_PTR(destination))
451 identical = dt_util_streams_equal(source, destination);
452 if(!IS_NULL_PTR(source)) fclose(source);
453 if(!IS_NULL_PTR(destination)) fclose(destination);
454 }
455
456 if(identical != 1)
457 {
458 dt_control_alert(_("Import problem"), _("The imported file differs from the original, which was not deleted"),
459 filename);
460 }
461 else if(g_unlink(filename) != 0)
462 {
463 dt_control_alert(_("Import problem"),
464 _("The imported file was verified but the original could not be deleted"), filename);
465 }
466
467apply_styles:
468 if(IS_NULL_PTR(data->styles)) return imgid;
469
470 dt_hm_batch_state_t batch = { 0 };
471 for(GList *s = data->styles; s; s = g_list_next(s))
472 {
473 const char *style_name = (const char *)s->data;
474 const int32_t style_id = dt_styles_get_id_by_name(style_name);
475 if(style_id <= 0) continue;
476 dt_styles_apply_to_image_merge(style_name, style_id, imgid, DT_HISTORY_MERGE_APPEND, &batch);
477 }
480
481 return imgid;
482}
483
487static void _refresh_progress_counter(dt_job_t *job, const int elements, const int index,
488 const gboolean folder_survey)
489{
490 gchar message[128] = { 0 };
491 double fraction = (double)index / (double)elements;
492 if(folder_survey)
493 snprintf(message, sizeof(message),
494 ngettext("Capture: importing %i/%i image", "Capture: importing %i/%i images", index),
495 index, elements);
496 else
497 snprintf(message, sizeof(message),
498 ngettext("Importing %i/%i image", "Importing %i/%i images", index),
499 index, elements);
501 dt_control_job_set_progress(job, fraction);
502 g_usleep(100);
503}
504
506{
508 dt_control_import_t *data = params->data;
509
510 int index = 0;
511 int xmps = 0; // number of xmps imported in db.
512 int32_t imgid = UNKNOWN_IMAGE;
513 int32_t last_imgid = UNKNOWN_IMAGE;
514 gint64 last_collection_refresh = 0;
515 dt_import_sequence_t sequences = {
516 .captures = g_hash_table_new_full(g_str_hash, g_str_equal, dt_free_gpointer, NULL),
517 .destinations = g_hash_table_new_full(g_str_hash, g_str_equal, dt_free_gpointer, NULL),
518 };
519
520 // What this import may do to the view the user is in, decided once from what the import IS. An
521 // automatic (folder survey) one never moves them: Studio Capture displays the capture itself,
522 // from DT_SIGNAL_IMAGE_IMPORT, and the survey outlives its atelier. A requested one shows the
523 // grid as images arrive, and opens the darkroom instead if it turns out to have imported one.
526 const char *no_image_message = _("No image imported!");
527 if(data->folder_survey)
528 {
529 first_image_policy = DT_COLLECTION_IMPORT_VIEW_KEEP;
530 single_image_policy = DT_COLLECTION_IMPORT_VIEW_KEEP;
531 no_image_message = _("Capture: No image imported!");
532 }
533
545 for(GList *img = g_list_first(data->imgs); img; img = g_list_next(img))
546 {
547 const char *filename = (const char *)img->data;
548 int capture_sequence = 0;
549 gboolean import_ready = !data->copy;
550 gchar *destination = NULL;
551 dt_image_t *expansion_image = NULL;
552 imgid = UNKNOWN_IMAGE;
553
554 dt_print(DT_DEBUG_IMPORT, "[Import] starting import of image #%i...\n", index);
555
556 _refresh_progress_counter(job, data->elements, index, data->folder_survey);
557 if(!data->copy) goto import_image;
558
559 capture_sequence = dt_import_sequence_assign(&sequences, filename);
560 expansion_image = dt_alloc_align(sizeof(dt_image_t));
561 if(IS_NULL_PTR(expansion_image)) goto complete_image;
562 dt_image_init(expansion_image);
563
564 if(!IS_NULL_PTR(strstr(data->target_file_pattern, "$(EXIF"))
565 || !IS_NULL_PTR(strstr(data->target_subfolder_pattern, "$(EXIF")))
566 dt_exif_read(expansion_image, filename);
567
568 const dt_import_destination_t request = { filename, expansion_image, data, capture_sequence };
569 destination = dt_import_sequence_destination(&sequences, &request);
570 import_ready = !IS_NULL_PTR(destination);
571
572import_image:
573 if(import_ready) imgid = _import_image(img, data, destination, &data->discarded, &xmps);
574complete_image:
575 dt_free(destination)
576 dt_free_align(expansion_image);
577 if(!IS_NULL_PTR(data->file_imported))
578 data->file_imported((const char *)img->data, imgid > UNKNOWN_IMAGE, data->callback_data);
579
580 if(imgid <= UNKNOWN_IMAGE) continue;
581 last_imgid = imgid;
582
583 // On the first image, try to switch the current filmroll to the imported image's folder.
584 // dt_collection_load_filmroll() silently declines to do anything (no collection refresh)
585 // when it cannot switch folders, e.g. the collect module is not on the "Folders" tab. In
586 // that case a single imported image would never show up until the user reloads the
587 // collection by hand (issue #860). So always run a collection update afterwards: it
588 // re-runs the current query and makes newly-imported matching images appear.
589 if(index == 0)
590 dt_collection_load_filmroll(dt_collection_get_global(), imgid, first_image_policy, TRUE);
591
592 // known_image_folder is NULL: in copy mode the image's final DB location can be a
593 // completely different, pattern-generated folder from its original source path, which is
594 // all this loop has at hand -- dt_collection_notify_imported() must resolve it fresh.
595 dt_collection_notify_imported(imgid, NULL, &last_collection_refresh);
596
597 index++;
598 }
599
600 g_hash_table_destroy(sequences.destinations);
601 g_hash_table_destroy(sequences.captures);
602
603 if(index == 0)
604 {
605 dt_conf_set_int("ui_last/nb_imported", index);
606 dt_control_log("%s", no_image_message);
607 fprintf(stderr, "No image imported!\n\n");
608 return 1;
609 }
610
611 // Guarantee the final state is reflected even if the last few images landed inside the throttle window.
613 dt_conf_set_int("ui_last/nb_imported", index);
614
615 // Don't open the picture in darkroom if more than 1 xmp (= duplicates) has been imported: the
616 // single file then stands for several images in the DB and none of them is the obvious one to
617 // open. Zero xmp is the ordinary case of a file with no sidecar (and of a library configured
618 // not to write any), still exactly one image: open it like any other single import.
619 if(index == 1 && xmps <= 1)
620 {
621 // A requested single image opens in the darkroom, which is announcement enough -- only the
622 // survey, which stays where it is, says anything.
623 if(data->folder_survey) dt_control_log(_("Capture: imported 1 image."));
624 dt_collection_load_filmroll(dt_collection_get_global(), last_imgid, single_image_policy, TRUE);
625 return 0;
626 }
627
629 ? ngettext("Capture: imported %d image.", "Capture: imported %d images.", index)
630 : ngettext("Imported %d image", "Imported %d images", index), index);
631 fprintf(stdout, "%d files imported in database.\n\n", index);
632
633 return 0;
634}
635
637{
638 g_date_time_unref(data->datetime);
639 dt_free(data->jobcode);
640 dt_free(data->base_folder);
643 dt_free(data->target_dir);
644 if(data->styles)
645 {
646 g_list_free_full(data->styles, dt_free_gpointer);
647 data->styles = NULL;
648 }
651 data->callback_data = NULL;
652
653 // GList of pathes stored as *char. We need to free the list and the *char
654 if(data->discarded)
655 {
656 g_list_free_full(data->discarded, dt_free_gpointer);
657 data->discarded = NULL;
658 }
659 if(data->imgs)
660 {
661 g_list_free_full(data->imgs, dt_free_gpointer);
662 data->imgs = NULL;
663 }
664}
665
666/* Installed by the GUI at startup; absent under ansel-cli, where the discarded list is
667 * simply freed with the rest -- a headless import has nobody to show a dialog to. */
669
674
675/* The discarded-files recap dialog lives in gui/import.c now: it was the only GUI
676 * code in this job file, and the only reason a control-layer TU included
677 * widgets/ and gui/ headers. The job reaches it through the handler below. */
678
680{
682 dt_control_import_t *data = params->data;
683
684 // Display a recap of files that weren't copied
685 if(g_list_length(data->discarded) > 0 && _discarded_files_handler)
686 {
687 // Called on THIS worker thread: getting onto the GUI main loop is the handler's
688 // business (g_main_context_invoke is GTK machinery, not a job's). The handler owns
689 // freeing data and params, whenever it is done with them.
691 }
692 else
693 {
695 dt_free(data);
697 }
698}
699
701{
703 if(IS_NULL_PTR(params)) return NULL;
704
705 params->data = g_malloc0(sizeof(dt_control_import_t));
706 if(IS_NULL_PTR(params->data))
707 {
709 return NULL;
710 }
711 return params;
712}
713
715{
717 if(IS_NULL_PTR(job)) return NULL;
720 {
722 return NULL;
723 }
724 memcpy(params->data, &data, sizeof(dt_control_import_t));
725 params->index = NULL;
726 dt_control_job_add_progress(job, _("import"), FALSE);
728 return job;
729}
730
732{
734 if(IS_NULL_PTR(job))
735 {
736 // Report every source as failed so asynchronous clients can release their queued state.
737 for(GList *img = g_list_first(data.imgs); img; img = g_list_next(img))
738 if(!IS_NULL_PTR(data.file_imported))
739 data.file_imported((const char *)img->data, FALSE, data.callback_data);
740
742 return 1;
743 }
744
746}
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 cleanup(dt_imageio_module_format_t *self)
Definition avif.c:170
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_alert(const char *title, const char *message, const char *item)
Tell the user something that must not go unseen: what it says stays true after a toast would have fad...
Definition control.c:883
void dt_control_log(const char *msg,...)
Definition control.c:839
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:654
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:511
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
static void * _control_import_alloc()
gboolean _create_dir(const char *path)
Creates folders from path. Returns TRUE if success.
Definition import_jobs.c:54
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:80
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 char *final_destination, 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.
int _import_copy_xmp(const char *const filename, const gchar *dest_file_path)
Attempt to find all sidecar XMP files along an image file and import (copy) it to destination.
int _import_copy_file(const char *const filename, const char *const dest_file_path, 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.
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:72
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)
char * dt_import_sequence_destination(dt_import_sequence_t *sequences, const dt_import_destination_t *request)
Expand and reserve a destination, retrying a sibling collision exactly once.
int dt_import_sequence_assign(dt_import_sequence_t *sequences, const char *filename)
Assign a capture number, retaining it even when subsequent expansion fails.
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
Definition jobs.c:138
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
Definition jobs.c:419
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:132
void dt_control_job_set_progress(dt_job_t *job, double value)
Definition jobs.c:701
void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
Definition jobs.c:687
void dt_control_job_set_progress_message(dt_job_t *job, const char *message)
Definition jobs.c:695
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
Definition jobs.c:115
void dt_control_job_dispose(_dt_job_t *job)
Definition jobs.c:156
@ DT_JOB_QUEUE_USER_FG
Definition jobs.h:55
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
@ 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
Borrowed inputs for one destination expansion.
Job-local numbering and reservation state; table lifetime belongs to the worker.
GHashTable * destinations
typedef double((*spd)(unsigned long int wavelength, double TempK))
gchar * dt_cleanup_separators(gchar *string)
Definition utility.c:1091
gboolean dt_util_dir_exist(const char *dir)
Definition utility.c:394
int dt_util_streams_equal(FILE *first, FILE *second)
Compare the remaining bytes of two borrowed binary streams.
Definition utility.c:94
gchar * dt_util_path_get_dirname(const gchar *filename)
Definition utility.c:800
gchar * dt_util_normalize_path(const gchar *_input)
Definition utility.c:706
size_t safe_strlen(const char *str)
check if the string is empty or NULL before calling strlen()
Definition utility.c:117
gboolean dt_util_test_writable_dir(const char *path)
Definition utility.c:371
gchar * dt_util_remove_whitespace(const gchar *path)
Definition utility.c:1102