sangoro 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '0979a808540bb82afbdded5353b2a410db01cd46f15f7652bfacbc79d6300c0d'
4
+ data.tar.gz: 52875e9902027701639b3f25853f5fbb876c26e89e17089944bac535cef44d02
5
+ SHA512:
6
+ metadata.gz: 6b1051b2740b34f666b53bdbd40859f03e0a310b921f55a8c30cece21ec6fd75c1004453a9b8466ea2eae115ec15049c24a7302a9ec5490e061cc201ca236c3b
7
+ data.tar.gz: eaf3807eae9cc1a0a41b9b26557397a7477b5fdd67fc91ffe334c1ec84e916b0ed0fe35c414f75e88c7f9d806882d0fe25c2c979457d50161daf44961c240532
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Anna Foerster
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Sangoro
2
+ A Ruby program to change the exif creation time stamp of JPEGs or PNGs.<br>
3
+ Demo: https://www.youtube.com/watch?v=yAoQi_NcPWY
4
+
5
+ # Installation
6
+ Following needs to be installed prior to running the script:
7
+ - ruby (v >= 2.3.3)
8
+
9
+ Gems:
10
+ - gtk3 (```gem install gtk3```)
11
+ - fileutils (```gem install fileutils```)
12
+ - fastimage (```gem install fastimage```)
13
+ - mini_exiftool (```gem install mini_exiftool```)
14
+
15
+ On Mac you might need the exiftool installed. I recommend installing it using the Brew package manager:
16
+ ```brew install exiftool```
17
+
18
+ After the you installed ruby and the gems, install the gem:
19
+ ```git install sangoro```
20
+ Now you can just run ```sangoro``` in your command line.
21
+
22
+ # Usage
23
+ 1. Select a JPEG/PNG file by clicking on "Select image"
24
+ 2. You will see the file name and the creation date & time on the right side, if available.
25
+ 3. Now you can specify by how many hours, minutes and/or seconds you want the time stamp to move. You also need to choose whether to move the timestamp forward or back.
26
+ 4. If you want to apply this change to all images in the folder, check the box below.
27
+ 5. Click "Apply".
28
+ 6. You are done. The creation timestamp of the selected image(s) was adjusted as specified.
data/bin/sangoro ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/sangoro/user_interface'
3
+
4
+ interface = Sangoro::UserInterface.new
5
+
6
+ interface.run
@@ -0,0 +1,311 @@
1
+ require 'gtk3'
2
+ require 'fileutils'
3
+ require 'fastimage'
4
+ require 'mini_exiftool'
5
+
6
+ module Sangoro
7
+
8
+ class UserInterface
9
+
10
+ attr_reader :window, :apply_all_btn, :forward_btn, :backward_btn, :apply_btn, :new_hour_ent, :new_min_ent, :new_sec_ent, :img_name_box, :img_time_box
11
+ def initialize
12
+ @hour_set = false
13
+ @min_set = false
14
+ @sec_set = false
15
+ @new_hour_val = 0
16
+ @new_min_val = 0
17
+ @new_sec_val = 0
18
+ @new_hour_ent = Gtk::Entry.new
19
+ @new_hour_ent.set_max_length(2)
20
+ @new_hour_ent.set_width_chars(5)
21
+ @new_min_ent = Gtk::Entry.new
22
+ @new_min_ent.set_max_length(2)
23
+ @new_min_ent.set_width_chars(5)
24
+ @new_sec_ent = Gtk::Entry.new
25
+ @new_sec_ent.set_max_length(2)
26
+ @new_sec_ent.set_width_chars(5)
27
+ @photo = MiniExiftool.new()
28
+ @success_label = Gtk::Label.new()
29
+
30
+ # define the window, the boxes and the buttons
31
+ define_window
32
+ define_boxes
33
+ define_buttons
34
+
35
+ end
36
+
37
+ # define the window
38
+ def define_window
39
+ @window = Gtk::Window.new
40
+ @window.title = "sangoro"
41
+ @window.border_width = 20
42
+ @window.signal_connect("destroy") {
43
+ Gtk.main_quit
44
+ false
45
+ }
46
+ end
47
+
48
+ # define the individual boxes
49
+ def define_boxes
50
+ @apply_cancel_box = Gtk::Box.new(:horizontal, 0)
51
+ @for_back_box = Gtk::Box.new(:vertical, 0)
52
+ @forward_back_box = Gtk::Box.new(:vertical, 0)
53
+ @img_name_box = Gtk::Box.new(:horizontal, 0)
54
+ @img_name_time_box = Gtk::Box.new(:vertical, 0)
55
+ @img_time_box = Gtk::Box.new(:horizontal, 0)
56
+ @main_box = Gtk::Box.new(:vertical, 0)
57
+ @meta_data_box = Gtk::Box.new(:vertical, 0)
58
+ @new_time_box = Gtk::Box.new(:horizontal, 0)
59
+ @select_box = Gtk::Box.new(:horizontal, 0)
60
+ end
61
+
62
+ # define all necessary buttons
63
+ def define_buttons
64
+ @apply_all_btn = Gtk::CheckButton.new("apply to all images in the folder accordingly")
65
+ @forward_btn = Gtk::RadioButton.new(:label => "forward")
66
+ @backward_btn = Gtk::RadioButton.new(:label => "back", :member => @forward_btn)
67
+ @select_img_btn = Gtk::Button.new(:label => "Select image", :use_underline => nil, :stock_id => nil)
68
+ @apply_btn = Gtk::Button.new(:label => "Apply", :use_underline => nil, :stock_id => nil)
69
+ @quit_btn = Gtk::Button.new(:label => "Quit", :use_underline => nil, :stock_id => nil)
70
+ toggle_btn_sensitivity(false)
71
+ end
72
+
73
+ def toggle_btn_sensitivity(sensitive_mode)
74
+ @apply_all_btn.sensitive = sensitive_mode
75
+ @forward_btn.sensitive = sensitive_mode
76
+ @backward_btn.sensitive = sensitive_mode
77
+ @apply_btn.sensitive = sensitive_mode
78
+ @new_hour_ent.sensitive = sensitive_mode
79
+ @new_min_ent.sensitive = sensitive_mode
80
+ @new_sec_ent.sensitive = sensitive_mode
81
+ end
82
+
83
+ # define the actions for the SELECT button
84
+ def run_select_action(img_name_text, img_time_text)
85
+ @hour_set = false
86
+ @min_set = false
87
+ @sec_set = false
88
+ @new_hour_ent.set_text("")
89
+ @new_min_ent.set_text("")
90
+ @new_sec_ent.set_text("")
91
+ @apply_all_btn.set_active(false)
92
+ @forward_btn.set_active(true)
93
+ @success_label.set_markup("<span font_desc='0'>Creation time successfully changed!</span>")
94
+ dialog = Gtk::FileChooserDialog.new(:title => "select image", :parent => @window, :action => Gtk::FileChooserAction::OPEN,
95
+ :buttons => [[Gtk::Stock::OPEN, Gtk::ResponseType::ACCEPT], [Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL]])
96
+
97
+ # process the selected image
98
+ if dialog.run == Gtk::ResponseType::ACCEPT
99
+ selected_file = dialog.filename
100
+ @photo = MiniExiftool.new(selected_file)
101
+ img_name_text.set_text(" #{File.basename(selected_file)}")
102
+ if @photo.datetimeoriginal
103
+ img_time_text.set_text(" #{@photo.datetimeoriginal.to_s}")
104
+ toggle_btn_sensitivity(true)
105
+ else
106
+ img_time_text.set_text(" No creation time available")
107
+ toggle_btn_sensitivity(false)
108
+ @success_label.set_markup("<span font_desc='13' color='orange'>The original creation timestamp could not be detected. Therefore, it is not possible to change the timestamp.</span>")
109
+ end
110
+
111
+ dialog.destroy
112
+
113
+ return [selected_file, img_name_text, img_time_text]
114
+ end
115
+ end
116
+
117
+ # prepare the box for the original meta data of an image
118
+ def prepare_orig_meta(label_text, box)
119
+ label = Gtk::Label.new("#{label_text}: ")
120
+ text = Gtk::Label.new(" --")
121
+ box.pack_start(label, :expand => false, :fill => false, :padding => 0)
122
+ box.pack_start(text, :expand => false, :fill => false, :padding => 10)
123
+ return [label, text]
124
+ end
125
+
126
+ # process the time shift entered by the user
127
+ def process_time_unit(time_ent)
128
+ new_time = []
129
+ single_digits = ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09"]
130
+ if (time_ent.text.to_i.to_s == time_ent.text) || single_digits.include?(time_ent.text)
131
+ new_time << time_ent.text.to_i
132
+ new_time << true
133
+ else
134
+ time_ent.set_text("")
135
+ end
136
+ return new_time
137
+ end
138
+
139
+ # define the actions for the ACCEPT button
140
+ def run_accept_action(img_time_text, selected_file)
141
+ count = 0
142
+ time_diff = 0
143
+ image_wording = "image"
144
+ if @hour_set
145
+ time_diff += @new_hour_val*3600
146
+ end
147
+ if @min_set
148
+ time_diff += @new_min_val*60
149
+ end
150
+ if @sec_set
151
+ time_diff += @new_sec_val
152
+ end
153
+
154
+ if @backward_btn.active?
155
+ time_diff *= -1
156
+ end
157
+
158
+ # apply the new time for the selected image only
159
+ if !@apply_all_btn.active?
160
+ count = change_time_single(time_diff, count, img_time_text)
161
+ # apply the new time for all images in the folder
162
+ else
163
+ count = change_time_all(selected_file, time_diff, count, img_time_text)
164
+ image_wording = "images" if count > 1
165
+ end
166
+
167
+ @success_label.set_markup("<span font_desc='13' color='green'>Creation time successfully changed! #{count} #{image_wording} affected.</span>")
168
+ end
169
+
170
+ #change the creation time for a single image
171
+ def change_time_single(time_difference, count, img_time_text)
172
+ if @photo.datetimeoriginal
173
+ @photo.datetimeoriginal += time_difference
174
+ img_time_text.set_markup("<span color='green'> #{@photo.datetimeoriginal.to_s} </span>")
175
+ count += 1
176
+ end
177
+ if @photo.createdate
178
+ @photo.createdate += time_difference
179
+ end
180
+ @photo.save
181
+ return count
182
+ end
183
+
184
+ # change the creation time for all images in the folder
185
+ def change_time_all(selected_file, time_difference, count, img_time_text)
186
+ dir_path = File.dirname(selected_file)
187
+ all_images = []
188
+ all_entries = Dir.entries(dir_path)
189
+ all_entries.each do |el|
190
+ el.downcase!
191
+ if el.include?(".jpg") || el.include?(".jpeg")
192
+ all_images << el
193
+ end
194
+ end
195
+
196
+ all_images.each do |im|
197
+ miniexif_img = MiniExiftool.new("#{dir_path}/#{im}")
198
+ if miniexif_img.datetimeoriginal
199
+ miniexif_img.datetimeoriginal += time_difference
200
+ count += 1
201
+ end
202
+ if miniexif_img.createdate
203
+ miniexif_img.createdate += time_difference
204
+ end
205
+ miniexif_img.save
206
+ if im == File.basename(selected_file)
207
+ img_time_text.set_markup("<span color='green'> #{miniexif_img.datetimeoriginal.to_s} </span>")
208
+ end
209
+ end
210
+ return count
211
+ end
212
+
213
+
214
+ # packs all the boxes
215
+ def pack_boxes
216
+
217
+ # pack the SELECT box
218
+ select_align = Gtk::Alignment.new 0,0,0,0
219
+ @select_box.pack_start(@select_img_btn, :expand => false, :fill => true, :padding => 0)
220
+ @select_box.pack_start(select_align, :expand => true, :fill => true, :padding => 0)
221
+ @select_box.pack_start(@img_name_time_box, :expand => true, :fill => true, :padding => 0)
222
+
223
+ # pack the FORWARD/BACK box
224
+ @for_back_box.pack_start(@forward_btn, :expand => true, :fill => true, :padding => 0)
225
+ @for_back_box.pack_start(@backward_btn, :expand => true, :fill => true, :padding => 0)
226
+
227
+ # pack the new time box
228
+ move_time_label = Gtk::Label.new("move the creation time ")
229
+ new_hour_label = Gtk::Label.new("h ")
230
+ new_min_label = Gtk::Label.new("min ")
231
+ new_sec_label = Gtk::Label.new("sec ")
232
+ @new_time_box.pack_start(move_time_label, :expand => true, :fill => true, :padding => 0)
233
+ @new_time_box.pack_start(@new_hour_ent, :expand => true, :fill => true, :padding => 0)
234
+ @new_time_box.pack_start(new_hour_label, :expand => true, :fill => true, :padding => 3)
235
+ @new_time_box.pack_start(@new_min_ent, :expand => true, :fill => true, :padding => 0)
236
+ @new_time_box.pack_start(new_min_label, :expand => true, :fill => true, :padding => 3)
237
+ @new_time_box.pack_start(@new_sec_ent, :expand => true, :fill => true, :padding => 0)
238
+ @new_time_box.pack_start(new_sec_label, :expand => true, :fill => true, :padding => 3)
239
+ @new_time_box.pack_start(@for_back_box, :expand => true, :fill => true, :padding => 10)
240
+
241
+ # pack the meta_change_box
242
+ @meta_data_box.pack_start(@select_box, :expand => true, :fill => true, :padding => 0)
243
+ @meta_data_box.pack_start(@new_time_box, :expand => true, :fill => true, :padding => 20)
244
+ @meta_data_box.pack_start(@apply_all_btn, :expand => true, :fill => true, :padding => 0)
245
+ @meta_data_box.pack_start(@success_label, :expand => true, :fill => true, :padding => 5)
246
+
247
+ # pack the @apply_cancel_box
248
+ apply_align = Gtk::Alignment.new 0, 0, 0, 0
249
+ @apply_cancel_box.pack_start(apply_align, :expand => true, :fill => true, :padding => 0)
250
+ @apply_cancel_box.pack_start(@quit_btn, :expand => false, :fill => true, :padding => 5)
251
+ @apply_cancel_box.pack_start(@apply_btn, :expand => false, :fill => true, :padding => 5)
252
+
253
+ # pack the main box
254
+ @main_box.pack_start(@meta_data_box, :expand => false, :fill => true, :padding => 0)
255
+ @main_box.pack_start(@apply_cancel_box, :expand => false, :fill => true, :padding => 0)
256
+ end
257
+
258
+ # main method - shows the window with all the buttons and abilities to select images and change meta data
259
+ def run
260
+
261
+ @window.add(@main_box)
262
+
263
+ # prepare meta data for the chosen image - file name and original creation time
264
+ img_name_label, img_name_text = prepare_orig_meta("file name", @img_name_box)
265
+ img_time_label, img_time_text = prepare_orig_meta("creation date & time", @img_time_box)
266
+
267
+ @img_name_time_box.pack_start(@img_name_box, :expand => true, :fill => true, :padding => 0)
268
+ @img_name_time_box.pack_start(@img_time_box, :expand=> true, :fill => true, :padding => 0)
269
+
270
+ # execute the action for the SELECT button
271
+ selected_file = ""
272
+ @select_img_btn.signal_connect("clicked") {
273
+ selected_file, img_name_text, img_time_text = run_select_action(img_name_text, img_time_text)
274
+ }
275
+
276
+ # define new time - process the input for the new hour, minutes and seconds
277
+ @new_hour_ent.signal_connect("key_release_event") {
278
+ @new_hour_val, @hour_set = process_time_unit(@new_hour_ent)
279
+ }
280
+
281
+ @new_min_ent.signal_connect("key_release_event") {
282
+ @new_min_val, @min_set = process_time_unit(@new_min_ent)
283
+ }
284
+
285
+ @new_sec_ent.signal_connect("key_release_event") {
286
+ @new_sec_val, @sec_set = process_time_unit(@new_sec_ent)
287
+ }
288
+
289
+ # execute the action when the the ACCEPT button is clicked
290
+ @apply_btn.signal_connect("clicked") {
291
+ run_accept_action(img_time_text, selected_file)
292
+ }
293
+
294
+ # define the action for the QUIT button
295
+ @quit_btn.signal_connect("clicked") do |w|
296
+ Gtk.main_quit
297
+ false
298
+ end
299
+
300
+ # pack all the boxes
301
+ pack_boxes
302
+
303
+ # show everything
304
+ @window.show_all
305
+
306
+ # run the program
307
+ Gtk.main
308
+ end
309
+
310
+ end
311
+ end
@@ -0,0 +1,40 @@
1
+ require 'gtk3'
2
+ require 'fileutils'
3
+ require 'fastimage'
4
+ require 'mini_exiftool'
5
+ require 'sangoro/user_interface'
6
+
7
+ module Sangoro
8
+ describe UserInterface do
9
+
10
+ before do
11
+ @interface = UserInterface.new
12
+ end
13
+
14
+ it "checks the title of the window" do
15
+ expect(@interface.window.title).to eq "sangoro"
16
+ end
17
+
18
+ it "checks that the following buttons are deactivated at the beginning" do
19
+ expect(@interface.apply_all_btn.sensitive?).to eq false
20
+ expect(@interface.forward_btn.sensitive?).to eq false
21
+ expect(@interface.backward_btn.sensitive?).to eq false
22
+ expect(@interface.apply_btn.sensitive?).to eq false
23
+ expect(@interface.new_hour_ent.sensitive?).to eq false
24
+ expect(@interface.new_min_ent.sensitive?).to eq false
25
+ expect(@interface.new_sec_ent.sensitive?).to eq false
26
+ end
27
+
28
+ it "checks the label and the placeholder for the file name" do
29
+ @img_name_label, @img_name_text = @interface.prepare_orig_meta("file name", @interface.img_name_box)
30
+ expect(@img_name_label.label).to eq "file name: "
31
+ expect(@img_name_text.label).to eq " --"
32
+ end
33
+
34
+ it "checks the label and the placeholder for the original creation time stamp" do
35
+ @img_time_label, @img_time_text = @interface.prepare_orig_meta("creation date & time", @interface.img_time_box)
36
+ expect(@img_time_label.label).to eq "creation date & time: "
37
+ expect(@img_time_text.label).to eq " --"
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sangoro
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - BluePeony
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-03-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.8.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.8.0
33
+ description: "# Sangoro\nA Ruby program to change the exif creation time stamp of
34
+ JPEGs or PNGs.<br>\nDemo: https://www.youtube.com/watch?v=yAoQi_NcPWY\n\n# Installation\nFollowing
35
+ needs to be installed prior to running the script:\n- ruby (v >= 2.3.3)\n\nGems:\n-
36
+ gtk3 (```gem install gtk3```)\n- fileutils (```gem install fileutils```)\n- fastimage
37
+ (```gem install fastimage```)\n- mini_exiftool (```gem install mini_exiftool```)\n\nOn
38
+ Mac you might need the exiftool installed. I recommend installing it using the Brew
39
+ package manager:\n```brew install exiftool```\n\nAfter the you installed ruby and
40
+ the gems, install the gem:\n```git install sangoro```\nNow you can just run ```sangoro```
41
+ in your command line.\n\n# Usage\n1. Select a JPEG/PNG file by clicking on \"Select
42
+ image\"\n2. You will see the file name and the creation date & time on the right
43
+ side, if available.\n3. Now you can specify by how many hours, minutes and/or seconds
44
+ you want the time stamp to move. You also need to choose whether to move the timestamp
45
+ forward or back.\n4. If you want to apply this change to all images in the folder,
46
+ check the box below.\n5. Click \"Apply\". \n6. You are done. The creation timestamp
47
+ of the selected image(s) was adjusted as specified.\n"
48
+ email: blue.peony2314@gmail.com
49
+ executables:
50
+ - sangoro
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - LICENSE
55
+ - README.md
56
+ - bin/sangoro
57
+ - lib/sangoro/user_interface.rb
58
+ - spec/sangoro/user_interface_spec.rb
59
+ homepage: https://github.com/BluePeony/sangoro
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '1.9'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.4.8
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Command line tool to change the EXIF creation time stamp of JPEGs and PNGs
82
+ test_files:
83
+ - spec/sangoro/user_interface_spec.rb