diff --git a/data/darktableconfig.xml.in b/data/darktableconfig.xml.in index 83eadf8a35..39ed8d43d7 100644 --- a/data/darktableconfig.xml.in +++ b/data/darktableconfig.xml.in @@ -1524,6 +1524,22 @@ file naming pattern used for a import session + + session/conflict_padding + int + 2 + burst file name conflict number padding + set the padding length for conflict resolution (e.g., 001, 002). + + + + session/import_existing_sidecar + bool + true + import existing sidecar files + import existing sidecar files (XMP, IPTC, etc.) when importing images + + plugins/lighttable/layout int diff --git a/src/common/import_session.c b/src/common/import_session.c index e83ef4de62..4d0c4efa0c 100644 --- a/src/common/import_session.c +++ b/src/common/import_session.c @@ -266,48 +266,42 @@ const char *dt_import_session_filename(struct dt_import_session_t *self, gboolea char *pattern = _import_session_filename_pattern(); if(pattern == NULL) { - dt_print(DT_DEBUG_ALWAYS, "[import_session] Failed to get session filaname pattern.\n"); + dt_print(DT_DEBUG_ALWAYS, "[import_session] Failed to get session filename pattern.\n"); return NULL; } + self->vp->retry_count = 0; + /* verify that expanded path and filename yields a unique file */ const char *path = dt_import_session_path(self, TRUE); if(use_filename) result_fname = g_strdup(self->vp->filename); else - result_fname = _import_session_filename_from_pattern(self, pattern); - - char *fname = g_build_path(G_DIR_SEPARATOR_S, path, result_fname, (char *)NULL); - char *previous_fname = fname; - if(g_file_test(fname, G_FILE_TEST_EXISTS) == TRUE) { - dt_print(DT_DEBUG_ALWAYS, "[import_session] File %s exists.\n", fname); do { - /* file exists, yield a new filename */ + /* generate filename based on the current retry_count */ g_free(result_fname); result_fname = _import_session_filename_from_pattern(self, pattern); - fname = g_build_path(G_DIR_SEPARATOR_S, path, result_fname, (char *)NULL); - dt_print(DT_DEBUG_ALWAYS, "[import_session] Testing %s.\n", fname); - /* check if same filename was yielded as before */ - if(strcmp(previous_fname, fname) == 0) + char *test_path = g_build_path(G_DIR_SEPARATOR_S, path, result_fname, (char *)NULL); + + if(g_file_test(test_path, G_FILE_TEST_EXISTS) == TRUE) { - g_free(previous_fname); - g_free(fname); - dt_control_log(_( - "couldn't expand to a unique filename for session, please check your import session settings.")); - return NULL; + dt_print(DT_DEBUG_ALWAYS, "[import_session] File %s exists, retrying.\n", test_path); + self->vp->retry_count++; + g_free(test_path); + } + else + { + g_free(test_path); + break; } - g_free(previous_fname); - previous_fname = fname; - - } while(g_file_test(fname, G_FILE_TEST_EXISTS) == TRUE); + } while(TRUE); } - g_free(previous_fname); g_free(pattern); self->current_filename = result_fname; diff --git a/src/common/variables.c b/src/common/variables.c index 1474cc32e8..820f88414b 100644 --- a/src/common/variables.c +++ b/src/common/variables.c @@ -914,6 +914,14 @@ static char *_get_base_value(dt_variables_params_t *params, char **variable) else if(_has_prefix(variable, "DARKTABLE.NAME") || _has_prefix(variable, "DARKTABLE_NAME")) result = g_strdup(PACKAGE_NAME); + + else if(_has_prefix(variable, "CONFLICT_PADDING")) + { + int pad_length = dt_conf_get_int("session/conflict_padding"); + if(pad_length < 0) pad_length = 0; + result = g_strdup_printf("%0*u", pad_length, params->retry_count); + } + else { // go past what looks like an invalid variable. we only expect to diff --git a/src/common/variables.h b/src/common/variables.h index 86052a9a3d..a5d616a94c 100644 --- a/src/common/variables.h +++ b/src/common/variables.h @@ -29,6 +29,9 @@ typedef struct dt_variables_params_t /** used for expanding variables that uses filename $(FILE_FOLDER) $(FILE_NAME) and $(FILE_EXTENSION). */ const gchar *filename; + /** used for conflict resolution in filename expansion */ + int retry_count; + /** used for expanding variable $(JOBCODE) */ const gchar *jobcode; diff --git a/src/control/jobs/control_jobs.c b/src/control/jobs/control_jobs.c index a9fab6f0ea..27bceab782 100644 --- a/src/control/jobs/control_jobs.c +++ b/src/control/jobs/control_jobs.c @@ -1566,7 +1566,7 @@ static int32_t dt_control_export_job_run(dt_job_t *job) { // IPTC character encoding not set by user, so we set the default utf8 here settings->metadata_export = dt_util_dstrcat(settings->metadata_export, - "\1%s\1%s", + "\1%s\1%s", iptc_envelope_characterset, "\x1b%G"); // ESC % G } @@ -2265,6 +2265,59 @@ void dt_control_write_sidecar_files() FALSE)); } +static gboolean _copy_file(const char *source, const char *destination) +{ + gchar *data = NULL; + gsize size = 0; + + if(!g_file_get_contents(source, &data, &size, NULL)) + { + dt_print(DT_DEBUG_CONTROL, "[import_from] failed to read file `%s`", source); + return FALSE; + } + + if(!g_file_set_contents(destination, data, size, NULL)) + { + dt_print(DT_DEBUG_CONTROL, "[import_from] failed to write file `%s`", destination); + g_free(data); + return FALSE; + } + + g_free(data); + return TRUE; +} + +static void _copy_timestamps(const char *source, const char *destination) +{ + struct stat statbuf; + if(stat(source, &statbuf) == 0) + { +#ifdef _WIN32 + struct utimbuf times; + times.actime = statbuf.st_atime; + times.modtime = statbuf.st_mtime; + utime(destination, ×); +#else + struct timeval times[2]; + times[0].tv_sec = statbuf.st_atime; + times[1].tv_sec = statbuf.st_mtime; +#ifdef __APPLE__ +#ifndef _POSIX_SOURCE + times[0].tv_usec = statbuf.st_atimespec.tv_nsec * 0.001; + times[1].tv_usec = statbuf.st_mtimespec.tv_nsec * 0.001; +#else + times[0].tv_usec = statbuf.st_atimensec * 0.001; + times[1].tv_usec = statbuf.st_mtimensec * 0.001; +#endif +#else + times[0].tv_usec = statbuf.st_atim.tv_nsec * 0.001; + times[1].tv_usec = statbuf.st_mtim.tv_nsec * 0.001; +#endif + utimes(destination, times); +#endif + } +} + static int _control_import_image_copy(const char *filename, char **prev_filename, char **prev_output, @@ -2308,37 +2361,37 @@ static int _control_import_image_copy(const char *filename, g_free(basename); } - if(!g_file_set_contents(output, data, size, NULL)) + if(!_copy_file(filename, output)) { - dt_print(DT_DEBUG_CONTROL, "[import_from] failed to write file %s\n", output); + dt_print(DT_DEBUG_CONTROL, "[import_from] failed to copy file %s", filename); res = FALSE; } else { -#ifdef _WIN32 - struct utimbuf times; - times.actime = statbuf.st_atime; - times.modtime = statbuf.st_mtime; - utime(output, ×); // set origin file timestamps -#else - struct timeval times[2]; - times[0].tv_sec = statbuf.st_atime; - times[1].tv_sec = statbuf.st_mtime; -#ifdef __APPLE__ -#ifndef _POSIX_SOURCE - times[0].tv_usec = statbuf.st_atimespec.tv_nsec * 0.001; - times[1].tv_usec = statbuf.st_mtimespec.tv_nsec * 0.001; -#else - times[0].tv_usec = statbuf.st_atimensec * 0.001; - times[1].tv_usec = statbuf.st_mtimensec * 0.001; -#endif -#else - times[0].tv_usec = statbuf.st_atim.tv_nsec * 0.001; - times[1].tv_usec = statbuf.st_mtim.tv_nsec * 0.001; -#endif - utimes(output, times); // set origin file timestamps -#endif + _copy_timestamps(filename, output); + } + gboolean import_existing_sidecar = dt_conf_get_bool("session/import_existing_sidecar"); + if(import_existing_sidecar) + { + char *xml_filename = g_strdup_printf("%s.xmp", filename); + if(g_file_test(xml_filename, G_FILE_TEST_EXISTS)) + { + char *xml_output = g_strdup_printf("%s.xmp", output); + if(_copy_file(xml_filename, xml_output)) + _copy_timestamps(xml_filename, xml_output); + else + { + dt_print(DT_DEBUG_CONTROL, "[import_from] failed to copy sidecar %s", xml_filename); + res = FALSE; + } + g_free(xml_output); + } + g_free(xml_filename); + } + + if(res) + { const dt_imgid_t imgid = dt_image_import(dt_import_session_film_id(session), output, FALSE, FALSE); if(!imgid) dt_control_log(_("error loading file `%s'"), output);