sangoro 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +17 -12
- data/lib/sangoro/actions.rb +192 -0
- data/lib/sangoro/basic_elements.rb +74 -0
- data/lib/sangoro/user_interface.rb +27 -259
- data/spec/sangoro/user_interface_spec.rb +15 -13
- metadata +77 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4971ce60b11ef459842179239e0c9e3529dc622d7e824c395aa448aef7858e87
|
4
|
+
data.tar.gz: b2e91a73d8426516f8ba5933bea3708701067f3e32a49a0d1d19cedaaea07e29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e307807a9b27004450067118a8d38618707a7560fd8d3db936113c2bf200404e5acdec5cdc6175bcc9d321bb4e19a980b56835760d453a50c63c3a7d984e368
|
7
|
+
data.tar.gz: cd6897497b58b01ee33a855ca48ac78e86656a98f60c3e75c4e76fffa6069f77e036e7e6f21f258cca2044e8b4b1451d7c9f208c536e7acdd9b1c0616387de15
|
data/README.md
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
# Sangoro
|
2
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
3
|
|
5
|
-
# Installation
|
6
|
-
Following needs to be installed prior to running the script:
|
7
|
-
- ruby (v >= 2.3.3)
|
8
4
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
-
|
13
|
-
|
5
|
+
# Installation
|
6
|
+
To use the Sangoro tool you require:
|
7
|
+
<ul>
|
8
|
+
<li> <a href="https://www.ruby-lang.org/en/downloads/"><code>ruby</code></a> (v>=2.3.3)
|
9
|
+
</ul>
|
10
|
+
as well as the following Ruby gems:
|
11
|
+
<ul>
|
12
|
+
<li><code>fastimage</code>
|
13
|
+
<li><code>fileutils</code>
|
14
|
+
<li><code>gtk3</code>
|
15
|
+
<li><code>mini_exiftool</code></li>
|
16
|
+
</ul>
|
14
17
|
|
15
18
|
On Mac you might need the exiftool installed. I recommend installing it using the Brew package manager:
|
16
19
|
```brew install exiftool```
|
17
20
|
|
18
|
-
|
19
|
-
```git install sangoro```
|
21
|
+
Get the Sangoro tool by typing ```gem install sangoro``` in your command line. This will install the sangoro gem as well as the gems mentined above.
|
20
22
|
Now you can just run ```sangoro``` in your command line.
|
21
23
|
|
22
24
|
# Usage
|
@@ -25,4 +27,7 @@ Now you can just run ```sangoro``` in your command line.
|
|
25
27
|
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
28
|
4. If you want to apply this change to all images in the folder, check the box below.
|
27
29
|
5. Click "Apply".
|
28
|
-
6. You are done. The creation timestamp of the selected image(s) was adjusted as specified.
|
30
|
+
6. You are done. The exif creation timestamp of the selected image(s) was adjusted as specified.
|
31
|
+
|
32
|
+
# Remarks
|
33
|
+
If you have any remarks, bugs, questions etc. please tell me, I'd be happy to help.
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Sangoro
|
2
|
+
module Actions
|
3
|
+
|
4
|
+
# toggles the sensitivity between active and inactive
|
5
|
+
def self.toggle_sensitivity(button_set, img_parameters, sensitive_mode)
|
6
|
+
button_set[:apply_all_btn].sensitive = sensitive_mode
|
7
|
+
button_set[:forward_btn].sensitive = sensitive_mode
|
8
|
+
button_set[:backward_btn].sensitive = sensitive_mode
|
9
|
+
button_set[:apply_btn].sensitive = sensitive_mode
|
10
|
+
img_parameters[:new_hour_ent].sensitive = sensitive_mode
|
11
|
+
img_parameters[:new_min_ent].sensitive = sensitive_mode
|
12
|
+
img_parameters[:new_sec_ent].sensitive = sensitive_mode
|
13
|
+
|
14
|
+
return [button_set, img_parameters]
|
15
|
+
end
|
16
|
+
|
17
|
+
# prepare the box for the original meta data of an image
|
18
|
+
def self.prepare_orig_meta(label_text, box)
|
19
|
+
label = Gtk::Label.new("#{label_text}: ")
|
20
|
+
text = Gtk::Label.new(" --")
|
21
|
+
box.pack_start(label, :expand => false, :fill => false, :padding => 0)
|
22
|
+
box.pack_start(text, :expand => false, :fill => false, :padding => 10)
|
23
|
+
return [label, text]
|
24
|
+
end
|
25
|
+
|
26
|
+
# define the actions for the SELECT button
|
27
|
+
def self.run_select_action(window, img_parameters, button_set, img_name_text, img_time_text)
|
28
|
+
img_parameters[:hour_set] = false
|
29
|
+
img_parameters[:min_set] = false
|
30
|
+
img_parameters[:sec_set] = false
|
31
|
+
img_parameters[:new_hour_ent].set_text("")
|
32
|
+
img_parameters[:new_min_ent].set_text("")
|
33
|
+
img_parameters[:new_sec_ent].set_text("")
|
34
|
+
button_set[:apply_all_btn].set_active(false)
|
35
|
+
button_set[:forward_btn].set_active(true)
|
36
|
+
img_parameters[:success_label].set_markup("<span font_desc='0'>Creation time successfully changed!</span>")
|
37
|
+
dialog = Gtk::FileChooserDialog.new(:title => "select image", :parent => window, :action => Gtk::FileChooserAction::OPEN,
|
38
|
+
:buttons => [[Gtk::Stock::OPEN, Gtk::ResponseType::ACCEPT], [Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL]])
|
39
|
+
|
40
|
+
# process the selected image
|
41
|
+
if dialog.run == Gtk::ResponseType::ACCEPT
|
42
|
+
selected_file = dialog.filename
|
43
|
+
img_parameters[:photo] = MiniExiftool.new(selected_file)
|
44
|
+
img_name_text.set_text(" #{File.basename(selected_file)}")
|
45
|
+
if img_parameters[:photo].datetimeoriginal
|
46
|
+
img_time_text.set_text(" #{img_parameters[:photo].datetimeoriginal.to_s}")
|
47
|
+
button_set, img_parameters = toggle_sensitivity(button_set, img_parameters, true)
|
48
|
+
else
|
49
|
+
img_time_text.set_text(" No creation time available")
|
50
|
+
button_set, img_parameters = toggle_sensitivity(button_set, img_parameters, false)
|
51
|
+
img_parameters[: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>")
|
52
|
+
end
|
53
|
+
|
54
|
+
dialog.destroy
|
55
|
+
|
56
|
+
return [selected_file, img_name_text, img_time_text]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# process the time shift entered by the user
|
61
|
+
def self.process_time_unit(time_ent)
|
62
|
+
new_time = []
|
63
|
+
single_digits = ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09"]
|
64
|
+
if (time_ent.text.to_i.to_s == time_ent.text) || single_digits.include?(time_ent.text)
|
65
|
+
new_time << time_ent.text.to_i
|
66
|
+
new_time << true
|
67
|
+
else
|
68
|
+
time_ent.set_text("")
|
69
|
+
end
|
70
|
+
return new_time
|
71
|
+
end
|
72
|
+
|
73
|
+
# define the actions for the ACCEPT button
|
74
|
+
def self.run_accept_action(img_parameters, button_set, img_time_text, selected_file)
|
75
|
+
count = 0
|
76
|
+
time_diff = 0
|
77
|
+
image_wording = "image"
|
78
|
+
if img_parameters[:hour_set]
|
79
|
+
time_diff += img_parameters[:new_hour_val]*3600
|
80
|
+
end
|
81
|
+
if img_parameters[:min_set]
|
82
|
+
time_diff += img_parameters[:new_min_val]*60
|
83
|
+
end
|
84
|
+
if img_parameters[:sec_set]
|
85
|
+
time_diff += img_parameters[:new_sec_val]
|
86
|
+
end
|
87
|
+
|
88
|
+
if button_set[:backward_btn].active?
|
89
|
+
time_diff *= -1
|
90
|
+
end
|
91
|
+
|
92
|
+
# apply the new time for the selected image only
|
93
|
+
if !button_set[:apply_all_btn].active?
|
94
|
+
count = change_time_single(img_parameters, time_diff, count, img_time_text)
|
95
|
+
# apply the new time for all images in the folder
|
96
|
+
else
|
97
|
+
count = change_time_all(selected_file, time_diff, count, img_time_text)
|
98
|
+
image_wording = "images" if count > 1
|
99
|
+
end
|
100
|
+
|
101
|
+
img_parameters[:success_label].set_markup("<span font_desc='13' color='green'>Creation time successfully changed! #{count} #{image_wording} affected.</span>")
|
102
|
+
end
|
103
|
+
|
104
|
+
#change the creation time for a single image
|
105
|
+
def self.change_time_single(img_parameters, time_difference, count, img_time_text)
|
106
|
+
if img_parameters[:photo].datetimeoriginal
|
107
|
+
img_parameters[:photo].datetimeoriginal += time_difference
|
108
|
+
img_time_text.set_markup("<span color='green'> #{img_parameters[:photo].datetimeoriginal.to_s} </span>")
|
109
|
+
count += 1
|
110
|
+
end
|
111
|
+
if img_parameters[:photo].createdate
|
112
|
+
img_parameters[:photo].createdate += time_difference
|
113
|
+
end
|
114
|
+
img_parameters[:photo].save
|
115
|
+
return count
|
116
|
+
end
|
117
|
+
|
118
|
+
# change the creation time for all images in the folder
|
119
|
+
def self.change_time_all(selected_file, time_difference, count, img_time_text)
|
120
|
+
dir_path = File.dirname(selected_file)
|
121
|
+
all_images = []
|
122
|
+
all_entries = Dir.entries(dir_path)
|
123
|
+
all_entries.each do |el|
|
124
|
+
el_down = el.downcase
|
125
|
+
if el_down.include?(".jpg") || el_down.include?(".jpeg")
|
126
|
+
all_images << el
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
all_images.each do |im|
|
131
|
+
miniexif_img = MiniExiftool.new("#{dir_path}/#{im}")
|
132
|
+
if miniexif_img.datetimeoriginal
|
133
|
+
miniexif_img.datetimeoriginal += time_difference
|
134
|
+
count += 1
|
135
|
+
end
|
136
|
+
if miniexif_img.createdate
|
137
|
+
miniexif_img.createdate += time_difference
|
138
|
+
end
|
139
|
+
miniexif_img.save
|
140
|
+
if im.downcase == File.basename(selected_file).downcase
|
141
|
+
img_time_text.set_markup("<span color='green'> #{miniexif_img.datetimeoriginal.to_s} </span>")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
return count
|
145
|
+
end
|
146
|
+
|
147
|
+
# packs all the boxes
|
148
|
+
def self.pack_boxes(box_set, button_set, img_parameters)
|
149
|
+
|
150
|
+
# pack the SELECT box
|
151
|
+
select_align = Gtk::Alignment.new 0,0,0,0
|
152
|
+
box_set[:select_box].pack_start(button_set[:select_img_btn], :expand => false, :fill => true, :padding => 0)
|
153
|
+
box_set[:select_box].pack_start(select_align, :expand => true, :fill => true, :padding => 0)
|
154
|
+
box_set[:select_box].pack_start(box_set[:img_name_time_box], :expand => true, :fill => true, :padding => 0)
|
155
|
+
|
156
|
+
# pack the FORWARD/BACK box
|
157
|
+
box_set[:for_back_box].pack_start(button_set[:forward_btn], :expand => true, :fill => true, :padding => 0)
|
158
|
+
box_set[:for_back_box].pack_start(button_set[:backward_btn], :expand => true, :fill => true, :padding => 0)
|
159
|
+
|
160
|
+
# pack the new time box
|
161
|
+
move_time_label = Gtk::Label.new("move the creation time ")
|
162
|
+
new_hour_label = Gtk::Label.new("h ")
|
163
|
+
new_min_label = Gtk::Label.new("min ")
|
164
|
+
new_sec_label = Gtk::Label.new("sec ")
|
165
|
+
box_set[:new_time_box].pack_start(move_time_label, :expand => true, :fill => true, :padding => 0)
|
166
|
+
box_set[:new_time_box].pack_start(img_parameters[:new_hour_ent], :expand => true, :fill => true, :padding => 0)
|
167
|
+
box_set[:new_time_box].pack_start(new_hour_label, :expand => true, :fill => true, :padding => 3)
|
168
|
+
box_set[:new_time_box].pack_start(img_parameters[:new_min_ent], :expand => true, :fill => true, :padding => 0)
|
169
|
+
box_set[:new_time_box].pack_start(new_min_label, :expand => true, :fill => true, :padding => 3)
|
170
|
+
box_set[:new_time_box].pack_start(img_parameters[:new_sec_ent], :expand => true, :fill => true, :padding => 0)
|
171
|
+
box_set[:new_time_box].pack_start(new_sec_label, :expand => true, :fill => true, :padding => 3)
|
172
|
+
box_set[:new_time_box].pack_start(box_set[:for_back_box], :expand => true, :fill => true, :padding => 10)
|
173
|
+
|
174
|
+
# pack the meta_change_box
|
175
|
+
box_set[:meta_data_box].pack_start(box_set[:select_box], :expand => true, :fill => true, :padding => 0)
|
176
|
+
box_set[:meta_data_box].pack_start(box_set[:new_time_box], :expand => true, :fill => true, :padding => 20)
|
177
|
+
box_set[:meta_data_box].pack_start(button_set[:apply_all_btn], :expand => true, :fill => true, :padding => 0)
|
178
|
+
box_set[:meta_data_box].pack_start(img_parameters[:success_label], :expand => true, :fill => true, :padding => 5)
|
179
|
+
|
180
|
+
# pack the @apply_cancel_box
|
181
|
+
apply_align = Gtk::Alignment.new 0, 0, 0, 0
|
182
|
+
box_set[:apply_cancel_box].pack_start(apply_align, :expand => true, :fill => true, :padding => 0)
|
183
|
+
box_set[:apply_cancel_box].pack_start(button_set[:quit_btn], :expand => false, :fill => true, :padding => 5)
|
184
|
+
box_set[:apply_cancel_box].pack_start(button_set[:apply_btn], :expand => false, :fill => true, :padding => 5)
|
185
|
+
|
186
|
+
# pack the main box
|
187
|
+
box_set[:main_box].pack_start(box_set[:meta_data_box], :expand => false, :fill => true, :padding => 0)
|
188
|
+
box_set[:main_box].pack_start(box_set[:apply_cancel_box], :expand => false, :fill => true, :padding => 0)
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Sangoro
|
2
|
+
module BasicElements
|
3
|
+
|
4
|
+
# define the parameters of the image
|
5
|
+
def self.define_img_parameters
|
6
|
+
img_parameters = {
|
7
|
+
hour_set: false,
|
8
|
+
min_set: false,
|
9
|
+
sec_set: false,
|
10
|
+
new_hour_val: 0,
|
11
|
+
new_min_val: 0,
|
12
|
+
new_sec_val: 0,
|
13
|
+
new_hour_ent: Gtk::Entry.new,
|
14
|
+
new_min_ent: Gtk::Entry.new,
|
15
|
+
new_sec_ent: Gtk::Entry.new,
|
16
|
+
photo: MiniExiftool.new(),
|
17
|
+
success_label: Gtk::Label.new()
|
18
|
+
}
|
19
|
+
|
20
|
+
img_parameters[:new_hour_ent].set_max_length(2)
|
21
|
+
img_parameters[:new_hour_ent].set_width_chars(5)
|
22
|
+
img_parameters[:new_min_ent].set_max_length(2)
|
23
|
+
img_parameters[:new_min_ent].set_width_chars(5)
|
24
|
+
img_parameters[:new_sec_ent].set_max_length(2)
|
25
|
+
img_parameters[:new_sec_ent].set_width_chars(5)
|
26
|
+
|
27
|
+
return img_parameters
|
28
|
+
end
|
29
|
+
|
30
|
+
# define the window
|
31
|
+
def self.define_window
|
32
|
+
window = Gtk::Window.new
|
33
|
+
window.title = "sangoro"
|
34
|
+
window.border_width = 20
|
35
|
+
window.signal_connect("destroy") {
|
36
|
+
Gtk.main_quit
|
37
|
+
false
|
38
|
+
}
|
39
|
+
|
40
|
+
return window
|
41
|
+
end
|
42
|
+
|
43
|
+
# define the individual boxes
|
44
|
+
def self.define_boxes
|
45
|
+
|
46
|
+
return {
|
47
|
+
apply_cancel_box: Gtk::Box.new(:horizontal, 0),
|
48
|
+
for_back_box: Gtk::Box.new(:vertical, 0),
|
49
|
+
forward_back_box: Gtk::Box.new(:vertical, 0),
|
50
|
+
img_name_box: Gtk::Box.new(:horizontal, 0),
|
51
|
+
img_name_time_box: Gtk::Box.new(:vertical, 0),
|
52
|
+
img_time_box: Gtk::Box.new(:horizontal, 0),
|
53
|
+
main_box: Gtk::Box.new(:vertical, 0),
|
54
|
+
meta_data_box: Gtk::Box.new(:vertical, 0),
|
55
|
+
new_time_box: Gtk::Box.new(:horizontal, 0),
|
56
|
+
select_box: Gtk::Box.new(:horizontal, 0)
|
57
|
+
}
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
# define all necessary buttons
|
62
|
+
def self.define_buttons
|
63
|
+
apply_all_btn = Gtk::CheckButton.new("apply to all images in the folder accordingly")
|
64
|
+
forward_btn = Gtk::RadioButton.new(:label => "forward")
|
65
|
+
backward_btn = Gtk::RadioButton.new(:label => "back", :member => forward_btn)
|
66
|
+
select_img_btn = Gtk::Button.new(:label => "Select image", :use_underline => nil, :stock_id => nil)
|
67
|
+
apply_btn = Gtk::Button.new(:label => "Apply", :use_underline => nil, :stock_id => nil)
|
68
|
+
quit_btn = Gtk::Button.new(:label => "Quit", :use_underline => nil, :stock_id => nil)
|
69
|
+
|
70
|
+
return {apply_all_btn: apply_all_btn, forward_btn: forward_btn, backward_btn: backward_btn, select_img_btn: select_img_btn, apply_btn: apply_btn, quit_btn: quit_btn }
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -2,303 +2,71 @@ require 'gtk3'
|
|
2
2
|
require 'fileutils'
|
3
3
|
require 'fastimage'
|
4
4
|
require 'mini_exiftool'
|
5
|
+
require_relative 'basic_elements'
|
6
|
+
require_relative 'actions'
|
5
7
|
|
6
8
|
module Sangoro
|
7
9
|
|
8
10
|
class UserInterface
|
11
|
+
attr_reader :window, :button_set, :img_parameters, :box_set
|
9
12
|
|
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
13
|
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
14
|
|
30
|
-
#
|
31
|
-
|
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
|
15
|
+
# initialize parameters for the image
|
16
|
+
@img_parameters = BasicElements.define_img_parameters
|
212
17
|
|
18
|
+
# define the window, the boxes and the buttons
|
19
|
+
@window = BasicElements.define_window
|
20
|
+
@box_set = BasicElements.define_boxes
|
21
|
+
@button_set = BasicElements.define_buttons
|
22
|
+
@button_set, @img_parameters = Actions.toggle_sensitivity(@button_set, @img_parameters, false)
|
213
23
|
|
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
24
|
end
|
257
25
|
|
258
26
|
# main method - shows the window with all the buttons and abilities to select images and change meta data
|
259
27
|
def run
|
260
28
|
|
261
|
-
@window.add(@main_box)
|
29
|
+
@window.add(@box_set[:main_box])
|
262
30
|
|
263
31
|
# 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)
|
32
|
+
img_name_label, img_name_text = Actions.prepare_orig_meta("file name", @box_set[:img_name_box])
|
33
|
+
img_time_label, img_time_text = Actions.prepare_orig_meta("creation date & time", @box_set[:img_time_box])
|
266
34
|
|
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)
|
35
|
+
@box_set[:img_name_time_box].pack_start(@box_set[:img_name_box], :expand => true, :fill => true, :padding => 0)
|
36
|
+
@box_set[:img_name_time_box].pack_start(@box_set[:img_time_box], :expand=> true, :fill => true, :padding => 0)
|
269
37
|
|
270
38
|
# execute the action for the SELECT button
|
271
39
|
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)
|
40
|
+
@button_set[:select_img_btn].signal_connect("clicked") {
|
41
|
+
selected_file, img_name_text, img_time_text = Actions.run_select_action(window, @img_parameters, @button_set, img_name_text, img_time_text)
|
274
42
|
}
|
275
43
|
|
276
44
|
# 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)
|
45
|
+
@img_parameters[:new_hour_ent].signal_connect("key_release_event") {
|
46
|
+
@img_parameters[:new_hour_val], @img_parameters[:hour_set] = Actions.process_time_unit(@img_parameters[:new_hour_ent])
|
279
47
|
}
|
280
48
|
|
281
|
-
@new_min_ent.signal_connect("key_release_event") {
|
282
|
-
@new_min_val, @min_set = process_time_unit(@new_min_ent)
|
49
|
+
@img_parameters[:new_min_ent].signal_connect("key_release_event") {
|
50
|
+
@img_parameters[:new_min_val], @img_parameters[:min_set] = Actions.process_time_unit(@img_parameters[:new_min_ent])
|
283
51
|
}
|
284
52
|
|
285
|
-
@new_sec_ent.signal_connect("key_release_event") {
|
286
|
-
@new_sec_val, @sec_set = process_time_unit(@new_sec_ent)
|
53
|
+
@img_parameters[:new_sec_ent].signal_connect("key_release_event") {
|
54
|
+
@img_parameters[:new_sec_val], @img_parameters[:sec_set] = Actions.process_time_unit(@img_parameters[:new_sec_ent])
|
287
55
|
}
|
288
56
|
|
289
57
|
# 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)
|
58
|
+
@button_set[:apply_btn].signal_connect("clicked") {
|
59
|
+
Actions.run_accept_action(@img_parameters, @button_set, img_time_text, selected_file)
|
292
60
|
}
|
293
61
|
|
294
62
|
# define the action for the QUIT button
|
295
|
-
@quit_btn.signal_connect("clicked") do |w|
|
63
|
+
@button_set[:quit_btn].signal_connect("clicked") do |w|
|
296
64
|
Gtk.main_quit
|
297
65
|
false
|
298
66
|
end
|
299
67
|
|
300
68
|
# pack all the boxes
|
301
|
-
pack_boxes
|
69
|
+
Actions.pack_boxes(@box_set, @button_set, @img_parameters)
|
302
70
|
|
303
71
|
# show everything
|
304
72
|
@window.show_all
|
@@ -3,6 +3,8 @@ require 'fileutils'
|
|
3
3
|
require 'fastimage'
|
4
4
|
require 'mini_exiftool'
|
5
5
|
require 'sangoro/user_interface'
|
6
|
+
require 'sangoro/basic_elements'
|
7
|
+
require 'sangoro/actions'
|
6
8
|
|
7
9
|
module Sangoro
|
8
10
|
describe UserInterface do
|
@@ -16,25 +18,25 @@ module Sangoro
|
|
16
18
|
end
|
17
19
|
|
18
20
|
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
|
21
|
+
expect(@interface.button_set[:apply_all_btn].sensitive?).to eq false
|
22
|
+
expect(@interface.button_set[:forward_btn].sensitive?).to eq false
|
23
|
+
expect(@interface.button_set[:backward_btn].sensitive?).to eq false
|
24
|
+
expect(@interface.button_set[:apply_btn].sensitive?).to eq false
|
25
|
+
expect(@interface.img_parameters[:new_hour_ent].sensitive?).to eq false
|
26
|
+
expect(@interface.img_parameters[:new_min_ent].sensitive?).to eq false
|
27
|
+
expect(@interface.img_parameters[:new_sec_ent].sensitive?).to eq false
|
26
28
|
end
|
27
29
|
|
28
30
|
it "checks the label and the placeholder for the file name" do
|
29
|
-
|
30
|
-
expect(
|
31
|
-
expect(
|
31
|
+
img_name_label, img_name_text = Actions.prepare_orig_meta("file name", @interface.box_set[:img_name_box])
|
32
|
+
expect(img_name_label.label).to eq "file name: "
|
33
|
+
expect(img_name_text.label).to eq " --"
|
32
34
|
end
|
33
35
|
|
34
36
|
it "checks the label and the placeholder for the original creation time stamp" do
|
35
|
-
|
36
|
-
expect(
|
37
|
-
expect(
|
37
|
+
img_time_label, img_time_text = Actions.prepare_orig_meta("creation date & time", @interface.box_set[:img_time_box])
|
38
|
+
expect(img_time_label.label).to eq "creation date & time: "
|
39
|
+
expect(img_time_text.label).to eq " --"
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
metadata
CHANGED
@@ -1,15 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sangoro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- BluePeony
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gtk3
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fileutils
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: fastimage
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mini_exiftool
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
13
69
|
- !ruby/object:Gem::Dependency
|
14
70
|
name: rspec
|
15
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -31,20 +87,22 @@ dependencies:
|
|
31
87
|
- !ruby/object:Gem::Version
|
32
88
|
version: 2.8.0
|
33
89
|
description: "# Sangoro\nA Ruby program to change the exif creation time stamp of
|
34
|
-
JPEGs or PNGs.<br>\
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
Mac you might need the exiftool installed. I recommend installing it using
|
39
|
-
package manager:\n```brew install exiftool```\n\
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
you
|
45
|
-
|
46
|
-
|
47
|
-
|
90
|
+
JPEGs or PNGs.<br>\n\n\n# Installation\nTo use the Sangoro tool you require:\n<ul>\n
|
91
|
+
\ <li> <a href=\"https://www.ruby-lang.org/en/downloads/\"><code>ruby</code></a>
|
92
|
+
(v>=2.3.3)\n</ul>\nas well as the following Ruby gems: \n<ul>\n <li><code>fastimage</code>\n
|
93
|
+
\ <li><code>fileutils</code>\n <li><code>gtk3</code>\n <li><code>mini_exiftool</code></li>\n</ul>
|
94
|
+
\ \n\nOn Mac you might need the exiftool installed. I recommend installing it using
|
95
|
+
the Brew package manager:\n```brew install exiftool```\n\nGet the Sangoro tool by
|
96
|
+
typing ```gem install sangoro``` in your command line. This will install the sangoro
|
97
|
+
gem as well as the gems mentined above.\nNow you can just run ```sangoro``` in your
|
98
|
+
command line.\n\n# Usage\n1. Select a JPEG/PNG file by clicking on \"Select image\"\n2.
|
99
|
+
You will see the file name and the creation date & time on the right side, if available.\n3.
|
100
|
+
Now you can specify by how many hours, minutes and/or seconds you want the time
|
101
|
+
stamp to move. You also need to choose whether to move the timestamp forward or
|
102
|
+
back.\n4. If you want to apply this change to all images in the folder, check the
|
103
|
+
box below.\n5. Click \"Apply\". \n6. You are done. The exif creation timestamp of
|
104
|
+
the selected image(s) was adjusted as specified.\n\n# Remarks \nIf you have any
|
105
|
+
remarks, bugs, questions etc. please tell me, I'd be happy to help. "
|
48
106
|
email: blue.peony2314@gmail.com
|
49
107
|
executables:
|
50
108
|
- sangoro
|
@@ -54,6 +112,8 @@ files:
|
|
54
112
|
- LICENSE
|
55
113
|
- README.md
|
56
114
|
- bin/sangoro
|
115
|
+
- lib/sangoro/actions.rb
|
116
|
+
- lib/sangoro/basic_elements.rb
|
57
117
|
- lib/sangoro/user_interface.rb
|
58
118
|
- spec/sangoro/user_interface_spec.rb
|
59
119
|
homepage: https://github.com/BluePeony/sangoro
|
@@ -68,7 +128,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
128
|
requirements:
|
69
129
|
- - ">="
|
70
130
|
- !ruby/object:Gem::Version
|
71
|
-
version:
|
131
|
+
version: 2.3.3
|
72
132
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
133
|
requirements:
|
74
134
|
- - ">="
|