From ffbdb89fe3e49e1c32b96565520e3faf567638c7 Mon Sep 17 00:00:00 2001 From: Nikolaos Karaolidis Date: Sat, 7 Dec 2024 17:40:02 +0000 Subject: [PATCH] Add more darktable options Signed-off-by: Nikolaos Karaolidis --- .../darktable/better-copy-and-import.patch | 265 ++++++++++++++++++ .../user/configs/gui/darktable/default.nix | 19 ++ 2 files changed, 284 insertions(+) create mode 100644 hosts/common/user/configs/gui/darktable/better-copy-and-import.patch diff --git a/hosts/common/user/configs/gui/darktable/better-copy-and-import.patch b/hosts/common/user/configs/gui/darktable/better-copy-and-import.patch new file mode 100644 index 0000000..b5179b9 --- /dev/null +++ b/hosts/common/user/configs/gui/darktable/better-copy-and-import.patch @@ -0,0 +1,265 @@ +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); diff --git a/hosts/common/user/configs/gui/darktable/default.nix b/hosts/common/user/configs/gui/darktable/default.nix index 22b65c5..42d7c61 100644 --- a/hosts/common/user/configs/gui/darktable/default.nix +++ b/hosts/common/user/configs/gui/darktable/default.nix @@ -4,6 +4,14 @@ }: { pkgs, ... }: { + nixpkgs.overlays = [ + (final: prev: { + darktable = prev.darktable.overrideAttrs (oldAttrs: { + patches = oldAttrs.patches or [ ] ++ [ ./better-copy-and-import.patch ]; + }); + }) + ]; + environment.persistence = { "/persist"."${home}/.config/darktable" = { }; "/cache"."${home}/.cache/darktable" = { }; @@ -28,6 +36,17 @@ "rating_one_double_tap" = true; "run_crawler_on_start" = true; "ui_last/theme" = "darktable-elegant-darker"; + "ui_last/grouping" = true; + "plugins/darkroom/lut3d/def_path" = "${home}/.config/darktable/luts"; + "opencl" = false; + "plugins/lighttable/overlays/1/0" = 0; + "plugins/lighttable/overlays/1/1" = 3; + "plugins/lighttable/overlays/1/2" = 3; + "plugins/darkroom/modulegroups/last_preset" = "modules: all"; + "session/base_directory_pattern" = "${home}/Pictures/Darktable"; + "session/filename_pattern" = "$(EXIF.YEAR)-$(EXIF.MONTH)-$(EXIF.DAY)_$(EXIF.HOUR)-$(EXIF.MINUTE)-$(EXIF.SECOND)_$(CONFLICT_PADDING).$(FILE_EXTENSION)"; + "session/sub_directory_pattern" = ""; + "setup_import_directory" = true; }; "darktable/luts".source = "${hald-clut}/HaldCLUT";