1 /*
2 This file is part of darktable,
3 Copyright (C) 2014-2021 darktable developers.
4
5 darktable 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 darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20
21 #include "common/film.h"
22 #include "common/import_session.h"
23 #include "common/variables.h"
24 #include "control/conf.h"
25 #include "control/control.h"
26
27 /* TODO: Investigate if we can make one import session instance thread safe
28 eg. having several background jobs working with same instance.
29 */
30
31 typedef struct dt_import_session_t
32 {
33 uint32_t ref;
34
35 dt_film_t *film;
36 dt_variables_params_t *vp;
37
38 const gchar *current_path;
39 const gchar *current_filename;
40
41 } dt_import_session_t;
42
43
_import_session_cleanup_filmroll(dt_import_session_t * self)44 static void _import_session_cleanup_filmroll(dt_import_session_t *self)
45 {
46 if(self->film == NULL) return;
47 /* if current filmroll for session is empty, remove it */
48 if(dt_film_is_empty(self->film->id))
49 {
50 dt_film_remove(self->film->id);
51 if(self->current_path != NULL && g_file_test(self->current_path, G_FILE_TEST_IS_DIR) && dt_util_is_dir_empty(self->current_path))
52 {
53 // no need to ask for rmdir as it'll be re-created if it's needed
54 // by another import session with same path params
55 g_rmdir(self->current_path);
56 self->current_path = NULL;
57 }
58 }
59 dt_film_cleanup(self->film);
60
61 g_free(self->film);
62 self->film = NULL;
63 }
64
65
_import_session_initialize_filmroll(dt_import_session_t * self,const char * path)66 static gboolean _import_session_initialize_filmroll(dt_import_session_t *self, const char *path)
67 {
68 int32_t film_id;
69
70 /* cleanup of previously used filmroll */
71 _import_session_cleanup_filmroll(self);
72
73 /* recursively create directories, abort if failed */
74 if(g_mkdir_with_parents(path, 0755) == -1)
75 {
76 fprintf(stderr, "failed to create session path %s.\n", path);
77 _import_session_cleanup_filmroll(self);
78 return TRUE;
79 }
80
81 /* open one or initialize a filmroll for the session */
82 self->film = (dt_film_t *)g_malloc0(sizeof(dt_film_t));
83 film_id = dt_film_new(self->film, path);
84 if(film_id == 0)
85 {
86 fprintf(stderr, "[import_session] Failed to initialize film roll.\n");
87 _import_session_cleanup_filmroll(self);
88 return TRUE;
89 }
90
91 /* every thing is good lets setup current path */
92 self->current_path = path;
93
94 return FALSE;
95 }
96
97
_import_session_migrate_old_config()98 static void _import_session_migrate_old_config()
99 {
100 /* TODO: check if old config exists, migrate to new and remove old */
101 }
102
103
_import_session_path_pattern()104 static char *_import_session_path_pattern()
105 {
106 char *res;
107 char *base;
108 char *sub;
109
110 res = NULL;
111 base = dt_conf_get_string("session/base_directory_pattern");
112 sub = dt_conf_get_string("session/sub_directory_pattern");
113
114 if(!sub || !base)
115 {
116 fprintf(stderr, "[import_session] No base or subpath configured...\n");
117 goto bail_out;
118 }
119
120 #ifdef WIN32
121 res = g_build_path("/", base, sub, (char *)NULL);
122 #else
123 res = g_build_path(G_DIR_SEPARATOR_S, base, sub, (char *)NULL);
124 #endif
125
126 bail_out:
127 g_free(base);
128 g_free(sub);
129 return res;
130 }
131
132
_import_session_filename_pattern()133 static char *_import_session_filename_pattern()
134 {
135 char *name;
136
137 name = dt_conf_get_string("session/filename_pattern");
138 if(!name)
139 {
140 fprintf(stderr, "[import_session] No name configured...\n");
141 return NULL;
142 }
143
144 return name;
145 }
146
147
dt_import_session_new()148 struct dt_import_session_t *dt_import_session_new()
149 {
150 dt_import_session_t *is;
151
152 is = (dt_import_session_t *)g_malloc0(sizeof(dt_import_session_t));
153
154 dt_variables_params_init(&is->vp);
155
156 /* migrate old configuration */
157 _import_session_migrate_old_config();
158 return is;
159 }
160
161
dt_import_session_destroy(struct dt_import_session_t * self)162 void dt_import_session_destroy(struct dt_import_session_t *self)
163 {
164 if(--self->ref != 0) return;
165
166 /* cleanup of session import film roll */
167 _import_session_cleanup_filmroll(self);
168
169 dt_variables_params_destroy(self->vp);
170
171 g_free(self);
172 }
173
dt_import_session_ready(struct dt_import_session_t * self)174 gboolean dt_import_session_ready(struct dt_import_session_t *self)
175 {
176 return (self->film && self->film->id);
177 }
178
dt_import_session_ref(struct dt_import_session_t * self)179 void dt_import_session_ref(struct dt_import_session_t *self)
180 {
181 self->ref++;
182 }
183
dt_import_session_unref(struct dt_import_session_t * self)184 void dt_import_session_unref(struct dt_import_session_t *self)
185 {
186 self->ref--;
187 }
188
dt_import_session_import(struct dt_import_session_t * self)189 void dt_import_session_import(struct dt_import_session_t *self)
190 {
191 const int32_t id = dt_image_import(self->film->id, self->current_filename, TRUE, TRUE);
192 if(id)
193 {
194 DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_VIEWMANAGER_THUMBTABLE_ACTIVATE, id);
195 dt_control_queue_redraw();
196 }
197 }
198
199
dt_import_session_set_name(struct dt_import_session_t * self,const char * name)200 void dt_import_session_set_name(struct dt_import_session_t *self, const char *name)
201 {
202 /* free previous jobcode name */
203 g_free((void *)self->vp->jobcode);
204
205 self->vp->jobcode = g_strdup(name);
206
207 /* setup new filmroll if path has changed */
208 dt_import_session_path(self, FALSE);
209 }
210
211
dt_import_session_set_time(struct dt_import_session_t * self,time_t time)212 void dt_import_session_set_time(struct dt_import_session_t *self, time_t time)
213 {
214 dt_variables_set_time(self->vp, time);
215 }
216
217
218 void
dt_import_session_set_exif_time(struct dt_import_session_t * self,time_t exif_time)219 dt_import_session_set_exif_time(struct dt_import_session_t *self, time_t exif_time)
220 {
221 dt_variables_set_exif_time(self->vp, exif_time);
222 }
223
224
dt_import_session_set_filename(struct dt_import_session_t * self,const char * filename)225 void dt_import_session_set_filename(struct dt_import_session_t *self, const char *filename)
226 {
227 self->vp->filename = filename;
228 }
229
230
dt_import_session_film_id(struct dt_import_session_t * self)231 int32_t dt_import_session_film_id(struct dt_import_session_t *self)
232 {
233 if(self->film) return self->film->id;
234
235 return -1;
236 }
237
238
dt_import_session_name(struct dt_import_session_t * self)239 const char *dt_import_session_name(struct dt_import_session_t *self)
240 {
241 return self->vp->jobcode;
242 }
243
244 /* This returns a unique filename using session path **and** the filename.
245 If current is true we will use the original filename otherwise use the pattern.
246 */
dt_import_session_filename(struct dt_import_session_t * self,gboolean use_filename)247 const char *dt_import_session_filename(struct dt_import_session_t *self, gboolean use_filename)
248 {
249 const char *path;
250 char *fname, *previous_fname;
251 char *pattern;
252 gchar *result_fname;
253
254 /* expand next filename */
255 g_free((void *)self->current_filename);
256 self->current_filename = NULL;
257
258 pattern = _import_session_filename_pattern();
259 if(pattern == NULL)
260 {
261 fprintf(stderr, "[import_session] Failed to get session filaname pattern.\n");
262 return NULL;
263 }
264
265 /* verify that expanded path and filename yields a unique file */
266 path = dt_import_session_path(self, TRUE);
267
268 if(use_filename)
269 result_fname = g_strdup(self->vp->filename);
270 else
271 result_fname = dt_variables_expand(self->vp, pattern, TRUE);
272
273 previous_fname = fname = g_build_path(G_DIR_SEPARATOR_S, path, result_fname, (char *)NULL);
274 if(g_file_test(fname, G_FILE_TEST_EXISTS) == TRUE)
275 {
276 fprintf(stderr, "[import_session] File %s exists.\n", fname);
277 do
278 {
279 /* file exists, yield a new filename */
280 g_free(result_fname);
281 result_fname = dt_variables_expand(self->vp, pattern, TRUE);
282 fname = g_build_path(G_DIR_SEPARATOR_S, path, result_fname, (char *)NULL);
283
284 fprintf(stderr, "[import_session] Testing %s.\n", fname);
285 /* check if same filename was yielded as before */
286 if(strcmp(previous_fname, fname) == 0)
287 {
288 g_free(previous_fname);
289 g_free(fname);
290 dt_control_log(_(
291 "couldn't expand to a unique filename for session, please check your import session settings."));
292 return NULL;
293 }
294
295 g_free(previous_fname);
296 previous_fname = fname;
297
298 } while(g_file_test(fname, G_FILE_TEST_EXISTS) == TRUE);
299 }
300
301 g_free(previous_fname);
302 g_free(pattern);
303
304 self->current_filename = result_fname;
305 fprintf(stderr, "[import_session] Using filename %s.\n", self->current_filename);
306
307 return self->current_filename;
308 }
309
_import_session_path(struct dt_import_session_t * self,gboolean current)310 static const char *_import_session_path(struct dt_import_session_t *self, gboolean current)
311 {
312 char *pattern;
313 char *new_path;
314 const gboolean currentok = dt_util_test_writable_dir(self->current_path);
315 fprintf(stderr, " _import_session_path testing `%s' %i", self->current_path, currentok);
316
317 if(current && self->current_path != NULL)
318 {
319 // the current path might not be a writable directory so test for that
320 if(currentok) return self->current_path;
321 // the current path is not valid so we can't cleanup
322 self->current_path = NULL;
323 return NULL;
324 }
325 /* check if expanded path differs from current */
326 pattern = _import_session_path_pattern();
327 if(pattern == NULL)
328 {
329 fprintf(stderr, "[import_session] Failed to get session path pattern.\n");
330 return NULL;
331 }
332
333 new_path = dt_variables_expand(self->vp, pattern, FALSE);
334 g_free(pattern);
335
336 /* did the session path change ? */
337 if(self->current_path && strcmp(self->current_path, new_path) == 0)
338 {
339 g_free(new_path);
340 if(currentok) return self->current_path;
341 }
342
343 if(!currentok) self->current_path = NULL;
344 /* we need to initialize a new filmroll for the new path */
345 if(_import_session_initialize_filmroll(self, new_path) != 0)
346 {
347 g_free(new_path);
348 return NULL;
349 }
350 return self->current_path;
351 }
352
dt_import_session_path(struct dt_import_session_t * self,gboolean current)353 const char *dt_import_session_path(struct dt_import_session_t *self, gboolean current)
354 {
355 const char *path = _import_session_path(self, current);
356 if(path == NULL)
357 {
358 fprintf(stderr, "[import_session] Failed to get session path.\n");
359 dt_control_log(_("requested session path not available. "
360 "device not mounted?"));
361 }
362 return path;
363 }
364
365 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
366 // vim: shiftwidth=2 expandtab tabstop=2 cindent
367 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
368