1
+ --[[
2
+
3
+ dbmaint.lua - perform database maintenance
4
+
5
+ Copyright (C) 2024 Bill Ferguson <wpferguson.com>.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation; either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ ]]
20
+ --[[
21
+ dbmaint - perform database maintenance
22
+
23
+ Perform database maintenance to clean up missing images and filmstrips.
24
+
25
+ ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
26
+ None
27
+
28
+ USAGE
29
+ * start dbmaint from script_manager
30
+ * scan for missing film rolls or missing images
31
+ * look at the results and choose to delete or not
32
+
33
+ BUGS, COMMENTS, SUGGESTIONS
34
+ Bill Ferguson <wpferguson.com>
35
+
36
+ CHANGES
37
+ ]]
38
+
39
+ local dt = require " darktable"
40
+ local du = require " lib/dtutils"
41
+ local df = require " lib/dtutils.file"
42
+ local log = require " lib/dtutils.log"
43
+
44
+
45
+ -- - - - - - - - - - - - - - - - - - - - - - - -
46
+ -- C O N S T A N T S
47
+ -- - - - - - - - - - - - - - - - - - - - - - - -
48
+
49
+ local MODULE <const> = " dbmaint"
50
+ local DEFAULT_LOG_LEVEL <const> = log .error
51
+ local PS <const> = dt .configuration .running_os == " windows" and " \\ " or " /"
52
+
53
+ -- - - - - - - - - - - - - - - - - - - - - - - -
54
+ -- A P I C H E C K
55
+ -- - - - - - - - - - - - - - - - - - - - - - - -
56
+
57
+ du .check_min_api_version (" 7.0.0" , MODULE ) -- choose the minimum version that contains the features you need
58
+
59
+
60
+ -- - - - - - - - - - - - - - - - - - - - - - - - - -
61
+ -- I 1 8 N
62
+ -- - - - - - - - - - - - - - - - - - - - - - - - - -
63
+
64
+ local gettext = dt .gettext .gettext
65
+
66
+ dt .gettext .bindtextdomain (MODULE , dt .configuration .config_dir .. " /lua/locale/" )
67
+
68
+ local function _ (msgid )
69
+ return gettext (MODULE , msgid )
70
+ end
71
+
72
+
73
+ -- - - - - - - - - - - - - - - - - - - - - - - - - -
74
+ -- S C R I P T M A N A G E R I N T E G R A T I O N
75
+ -- - - - - - - - - - - - - - - - - - - - - - - - - -
76
+
77
+ local script_data = {}
78
+
79
+ script_data .destroy = nil -- function to destory the script
80
+ script_data .destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet
81
+ script_data .restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
82
+ script_data .show = nil -- only required for libs since the destroy_method only hides them
83
+
84
+ script_data .metadata = {
85
+ name = " dbmaint" ,
86
+ purpose = _ (" perform database maintenance" ),
87
+ author = " Bill Ferguson <wpferguson.com>" ,
88
+ help = " https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/dbmaint/"
89
+ }
90
+
91
+
92
+ -- - - - - - - - - - - - - - - - - - - - - - - -
93
+ -- L O G L E V E L
94
+ -- - - - - - - - - - - - - - - - - - - - - - - -
95
+
96
+ log .log_level (DEFAULT_LOG_LEVEL )
97
+
98
+ -- - - - - - - - - - - - - - - - - - - - - - - -
99
+ -- N A M E S P A C E
100
+ -- - - - - - - - - - - - - - - - - - - - - - - -
101
+
102
+ local dbmaint = {}
103
+
104
+ -- - - - - - - - - - - - - - - - - - - - - - - -
105
+ -- G L O B A L V A R I A B L E S
106
+ -- - - - - - - - - - - - - - - - - - - - - - - -
107
+
108
+ dbmaint .main_widget = nil
109
+
110
+ -- - - - - - - - - - - - - - - - - - - - - - - -
111
+ -- A L I A S E S
112
+ -- - - - - - - - - - - - - - - - - - - - - - - -
113
+
114
+ local namespace = dbmaint
115
+
116
+ -- - - - - - - - - - - - - - - - - - - - - - - -
117
+ -- F U N C T I O N S
118
+ -- - - - - - - - - - - - - - - - - - - - - - - -
119
+
120
+ local function scan_film_rolls ()
121
+ local missing_films = {}
122
+
123
+ for _ , filmroll in ipairs (dt .films ) do
124
+ if not df .check_if_file_exists (filmroll .path ) then
125
+ table.insert (missing_films , filmroll )
126
+ end
127
+ end
128
+
129
+ return missing_films
130
+ end
131
+
132
+ local function scan_images (film )
133
+ local missing_images = {}
134
+
135
+ if film then
136
+ for i = 1 , # film do
137
+ local image = film [i ]
138
+ log .log_msg (log .debug , " checking " .. image .filename )
139
+ if not df .check_if_file_exists (image .path .. PS .. image .filename ) then
140
+ log .log_msg (log .info , image .filename .. " not found" )
141
+ table.insert (missing_images , image )
142
+ end
143
+ end
144
+ end
145
+
146
+ return missing_images
147
+ end
148
+
149
+ local function remove_missing_film_rolls (list )
150
+ for _ , filmroll in ipairs (list ) do
151
+ filmroll :delete (true )
152
+ end
153
+ end
154
+
155
+ -- force the lighttable to reload
156
+
157
+ local function refresh_lighttable (film )
158
+ local rules = dt .gui .libs .collect .filter ()
159
+ dt .gui .libs .collect .filter (rules )
160
+ end
161
+
162
+ local function remove_missing_images (list )
163
+ local film = list [1 ].film
164
+ for _ , image in ipairs (list ) do
165
+ image :delete ()
166
+ end
167
+ refresh_lighttable (film )
168
+ end
169
+
170
+ local function install_module ()
171
+ if not namespace .module_installed then
172
+ dt .register_lib (
173
+ MODULE , -- Module name
174
+ _ (" DB maintenance" ), -- Visible name
175
+ true , -- expandable
176
+ true , -- resetable
177
+ {[dt .gui .views .lighttable ] = {" DT_UI_CONTAINER_PANEL_LEFT_CENTER" , 0 }}, -- containers
178
+ namespace .main_widget ,
179
+ nil ,-- view_enter
180
+ nil -- view_leave
181
+ )
182
+ namespace .module_installed = true
183
+ end
184
+ end
185
+
186
+ -- - - - - - - - - - - - - - - - - - - - - - - -
187
+ -- U S E R I N T E R F A C E
188
+ -- - - - - - - - - - - - - - - - - - - - - - - -
189
+
190
+ dbmaint .list_widget = dt .new_widget (" text_view" ){
191
+ editable = false ,
192
+ reset_callback = function (this )
193
+ this .text = " "
194
+ end
195
+ }
196
+
197
+ dbmaint .chooser = dt .new_widget (" combobox" ){
198
+ label = " scan for" ,
199
+ selected = 1 ,
200
+ " film rolls" , " images" ,
201
+ reset_callback = function (this )
202
+ this .selected = 1
203
+ end
204
+ }
205
+
206
+ dbmaint .scan_button = dt .new_widget (" button" ){
207
+ label = " scan" ,
208
+ tooltip = " click to scan for missing film rolls/files" ,
209
+ clicked_callback = function (this )
210
+ local found = nil
211
+ local found_text = " "
212
+ if dbmaint .chooser .value == " film rolls" then
213
+ found = scan_film_rolls ()
214
+ if # found > 0 then
215
+ for _ , film in ipairs (found ) do
216
+ local dir_name = du .split (film .path , PS )
217
+ found_text = found_text .. dir_name [# dir_name ] .. " \n "
218
+ end
219
+ end
220
+ else
221
+ log .log_msg (log .debug , " checking path " .. dt .collection [1 ].path .. " for missing files" )
222
+ found = scan_images (dt .collection [1 ].film )
223
+ if # found > 0 then
224
+ for _ , image in ipairs (found ) do
225
+ found_text = found_text .. image .filename .. " \n "
226
+ end
227
+ end
228
+ end
229
+ if # found > 0 then
230
+ dbmaint .list_widget .text = found_text
231
+ dbmaint .found = found
232
+ dbmaint .remove_button .sensitive = true
233
+ end
234
+ end ,
235
+ reset_callback = function (this )
236
+ dbmaint .found = nil
237
+ end
238
+ }
239
+
240
+ dbmaint .remove_button = dt .new_widget (" button" ){
241
+ label = " remove" ,
242
+ tooltip = " remove missing film rolls/images" ,
243
+ sensitive = false ,
244
+ clicked_callback = function (this )
245
+ if dbmaint .chooser .value == " film rolls" then
246
+ remove_missing_film_rolls (dbmaint .found )
247
+ else
248
+ remove_missing_images (dbmaint .found )
249
+ end
250
+ dbmaint .found = nil
251
+ dbmaint .list_widget .text = " "
252
+ this .sensitive = false
253
+ end ,
254
+ reset_callback = function (this )
255
+ this .sensitive = false
256
+ end
257
+ }
258
+
259
+ dbmaint .main_widget = dt .new_widget (" box" ){
260
+ orientation = " vertical" ,
261
+ dt .new_widget (" section_label" ){label = " missing items" },
262
+ dbmaint .list_widget ,
263
+ dt .new_widget (" label" ){label = " " },
264
+ dbmaint .chooser ,
265
+ dt .new_widget (" label" ){label = " " },
266
+ dbmaint .scan_button ,
267
+ dbmaint .remove_button
268
+ }
269
+
270
+ -- - - - - - - - - - - - - - - - - - - - - - - -
271
+ -- D A R K T A B L E I N T E G R A T I O N
272
+ -- - - - - - - - - - - - - - - - - - - - - - - -
273
+
274
+ local function destroy ()
275
+ dt .gui .libs [MODULE ].visible = false
276
+
277
+ if namespace .event_registered then
278
+ dt .destroy_event (MODULE , " view-changed" )
279
+ end
280
+
281
+ return
282
+ end
283
+
284
+ local function restart ()
285
+ dt .gui .libs [MODULE ].visible = true
286
+
287
+ return
288
+ end
289
+
290
+ script_data .destroy = destroy
291
+ script_data .restart = restart
292
+ script_data .destroy_method = " hide"
293
+ script_data .show = restart
294
+
295
+ -- - - - - - - - - - - - - - - - - - - - - - - -
296
+ -- E V E N T S
297
+ -- - - - - - - - - - - - - - - - - - - - - - - -
298
+
299
+ if dt .gui .current_view ().id == " lighttable" then
300
+ install_module ()
301
+ else
302
+ if not namespace .event_registered then
303
+ dt .register_event (MODULE , " view-changed" ,
304
+ function (event , old_view , new_view )
305
+ if new_view .name == " lighttable" and old_view .name == " darkroom" then
306
+ install_module ()
307
+ end
308
+ end
309
+ )
310
+ namespace .event_registered = true
311
+ end
312
+ end
313
+
314
+ return script_data
0 commit comments