bconv 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/bconv +194 -0
- data/data/bconv.glade +215 -0
- data/lib/bconv.rb +116 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0a2ee9a53ba762929e67f01c9250b8f5d41ff9ea
|
4
|
+
data.tar.gz: 848942f749535612beb4c00a0c21f800b40e0881
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cd24ae77b608b5466535fadf0e39be5ca0fc89bb151bede6e8bfb749ae85c2ae2db8f68f1b629bfd064882bfa092351fba7b8adb2026fc99938626dd6358987b
|
7
|
+
data.tar.gz: 9f64cc426f59bd05940d5ac5ea1340e0e782b8b5158d2249bf59dab4c229f5377b341512770d3807d5b1c5c7444b3d191d507c2b152b1772af1f818cd12d0a16
|
data/bin/bconv
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
#!/usr/bin/env ruby19
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
#############################################################################
|
5
|
+
# Yet Another ImageMagic Frontend #
|
6
|
+
# (C) 2010-2011, Vasiliy Yeremeyev <vayerx@gmail.com> #
|
7
|
+
# #
|
8
|
+
# This program is free software: you can redistribute it and/or modify #
|
9
|
+
# it under the terms of the GNU General Public License as published by #
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
11
|
+
# (at your option) any later version. #
|
12
|
+
# #
|
13
|
+
# This program is distributed in the hope that it will be useful, #
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
16
|
+
# GNU General Public License for more details. #
|
17
|
+
# #
|
18
|
+
# You should have received a copy of the GNU General Public License #
|
19
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
20
|
+
#############################################################################
|
21
|
+
|
22
|
+
# TODO humanist config interface (parseconfig -> /dev/null)
|
23
|
+
# TODO thinking of 'libglade2 -> raw gtk2'
|
24
|
+
# TODO humanist errors notification
|
25
|
+
|
26
|
+
|
27
|
+
require 'bconv'
|
28
|
+
require 'parseconfig'
|
29
|
+
require 'libglade2'
|
30
|
+
|
31
|
+
class ConverterGUI
|
32
|
+
MIN_RATIO = 65
|
33
|
+
MAX_RATIO = 100
|
34
|
+
|
35
|
+
def initialize(file_or_data)
|
36
|
+
@semaphore = Mutex.new
|
37
|
+
@counting_thread = nil
|
38
|
+
@source_files = []
|
39
|
+
|
40
|
+
@config_file = RUBY_PLATFORM =~ /linux|bsd|darwin|cygwin/i ? File.expand_path('~/.bconv.conf') : 'bconv.ini'
|
41
|
+
@config = ParseConfig.new(@config_file) rescue nil
|
42
|
+
|
43
|
+
@converter = Converter.new
|
44
|
+
@builder = Gtk::Builder.new
|
45
|
+
@builder << file_or_data
|
46
|
+
@builder.connect_signals { |name| method(name) }
|
47
|
+
|
48
|
+
%w[src_edit dest_edit size_edit ratio_scale run_button src_button dest_button
|
49
|
+
textview progress_bar status_bar main_window].each do |control|
|
50
|
+
instance_variable_set('@' + control, @builder[ control ])
|
51
|
+
end
|
52
|
+
|
53
|
+
@src_edit.text = @config['source'] rescue ''
|
54
|
+
@dest_edit.text = @config['destination'] rescue ''
|
55
|
+
@size_edit.text = @config['size'] rescue '1600x1200'
|
56
|
+
|
57
|
+
@ratio_scale.set_range(MIN_RATIO, MAX_RATIO)
|
58
|
+
ratio = @config['ratio'].to_i rescue 95
|
59
|
+
@ratio_scale.value = (ratio < MIN_RATIO ? MIN_RATIO : (ratio > MAX_RATIO ? MAX_RATIO : ratio))
|
60
|
+
|
61
|
+
@main_window.show
|
62
|
+
|
63
|
+
preload_dir(@src_edit.text) unless @src_edit.text.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
def run
|
67
|
+
Gtk.main
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_src_button_clicked
|
71
|
+
dialog = Gtk::FileChooserDialog.new("Выбери исходную папку",
|
72
|
+
@main_window,
|
73
|
+
Gtk::FileChooser::ACTION_SELECT_FOLDER,
|
74
|
+
nil,
|
75
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
76
|
+
[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
|
77
|
+
|
78
|
+
dialog.current_folder = @src_edit.text
|
79
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
80
|
+
preload_dir(@src_edit.text = dialog.filename)
|
81
|
+
end
|
82
|
+
dialog.destroy
|
83
|
+
end
|
84
|
+
|
85
|
+
def preload_dir(dir)
|
86
|
+
@converter.preload_dir(dir) do |amount|
|
87
|
+
@status_bar.push(@status_bar.get_context_id(:init.to_s), "Найдено #{amount} файл(ов)") rescue nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def on_dest_button_clicked
|
92
|
+
dialog = Gtk::FileChooserDialog.new("Выбери папку для результата",
|
93
|
+
@main_window,
|
94
|
+
Gtk::FileChooser::ACTION_SELECT_FOLDER,
|
95
|
+
nil,
|
96
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
97
|
+
[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
|
98
|
+
|
99
|
+
dialog.current_folder = @dest_edit.text
|
100
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
101
|
+
@dest_edit.text = dialog.filename
|
102
|
+
end
|
103
|
+
dialog.destroy
|
104
|
+
end
|
105
|
+
|
106
|
+
# Save configuration
|
107
|
+
def save_config
|
108
|
+
File.open(@config_file, 'w') do |f|
|
109
|
+
f << 'source=' << @src_edit.text << "\n"
|
110
|
+
f << 'destination=' << @dest_edit.text << "\n"
|
111
|
+
f << 'size=' << @size_edit.text << "\n"
|
112
|
+
f << 'ratio=' << @ratio_scale.value.to_i << "\n"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Execute main job
|
117
|
+
def on_run_button_clicked
|
118
|
+
save_config
|
119
|
+
|
120
|
+
# check format of image size
|
121
|
+
img_size = @size_edit.text.gsub(/\s/, '')
|
122
|
+
begin
|
123
|
+
if img_size.index('%') || img_size.index('@')
|
124
|
+
raise img_size unless img_size.gsub!(/^(\d*)([%@])/, '\1\2')
|
125
|
+
else
|
126
|
+
raise img_size unless img_size.gsub!(/^(\d*)\w?(\d*)(\D?)$/, '\1x\2\3')
|
127
|
+
end
|
128
|
+
rescue
|
129
|
+
@textview.buffer.text = "Некорретный размер изображения '#{@size_edit.text}'"
|
130
|
+
return
|
131
|
+
end
|
132
|
+
|
133
|
+
ratio = @ratio_scale.value.to_i
|
134
|
+
if (ratio < 1 || ratio > 100)
|
135
|
+
@textview.buffer.text = "Недопустимый диапазон сжатия '#{ratio}'"
|
136
|
+
return
|
137
|
+
end
|
138
|
+
|
139
|
+
return unless check_directories(@src_edit.text, @dest_edit.text)
|
140
|
+
|
141
|
+
@textview.buffer.text = ""
|
142
|
+
@progress_bar.fraction = 0
|
143
|
+
|
144
|
+
lockUI
|
145
|
+
@converter.process(@src_edit.text, @dest_edit.text, img_size, ratio) { |file, step, done|
|
146
|
+
pulse_progress(file, step, done)
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
# Exit main loop
|
151
|
+
def on_main_window_destroy
|
152
|
+
@converter.stop
|
153
|
+
Gtk.main_quit
|
154
|
+
end
|
155
|
+
|
156
|
+
def lockUI
|
157
|
+
[ @run_button, @src_button, @dest_button ].each { |button| button.hide }
|
158
|
+
@progress_bar.show
|
159
|
+
end
|
160
|
+
|
161
|
+
def unlockUI
|
162
|
+
[ @run_button, @src_button, @dest_button ].each { |button| button.show }
|
163
|
+
@progress_bar.hide
|
164
|
+
end
|
165
|
+
|
166
|
+
# Check source/destination directories
|
167
|
+
def check_directories(src_dir, dest_dir)
|
168
|
+
# check source
|
169
|
+
if !FileTest.directory?(src_dir)
|
170
|
+
@textview.buffer.text = "#{src_dir} должно быть директорией"
|
171
|
+
return false
|
172
|
+
end
|
173
|
+
|
174
|
+
# check destination
|
175
|
+
Dir.mkdir(dest_dir, 0775) unless FileTest.exists?(dest_dir)
|
176
|
+
if !FileTest.directory?(dest_dir)
|
177
|
+
@textview.buffer.text = "#{dest_dir} должно быть директорией"
|
178
|
+
return false
|
179
|
+
end
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
183
|
+
def pulse_progress(file, step, done)
|
184
|
+
@semaphore.synchronize do
|
185
|
+
@textview.buffer.text = file + "\n" + @textview.buffer.text if file
|
186
|
+
@progress_bar.fraction += step if step
|
187
|
+
unlockUI if done
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
Encoding.default_external = 'UTF-8'
|
193
|
+
gui = ConverterGUI.new(File.read(File.expand_path('data/bconv.glade', File.dirname(__FILE__)+'/..')))
|
194
|
+
gui.run
|
data/data/bconv.glade
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<interface>
|
3
|
+
<requires lib="gtk+" version="2.16"/>
|
4
|
+
<!-- interface-naming-policy project-wide -->
|
5
|
+
<object class="GtkWindow" id="main_window">
|
6
|
+
<property name="width_request">700</property>
|
7
|
+
<property name="title" translatable="yes">Пакетное сжатие</property>
|
8
|
+
<property name="window_position">center</property>
|
9
|
+
<signal name="destroy" handler="on_main_window_destroy"/>
|
10
|
+
<child>
|
11
|
+
<object class="GtkTable" id="table1">
|
12
|
+
<property name="visible">True</property>
|
13
|
+
<property name="n_rows">7</property>
|
14
|
+
<property name="n_columns">3</property>
|
15
|
+
<property name="column_spacing">2</property>
|
16
|
+
<property name="row_spacing">2</property>
|
17
|
+
<child>
|
18
|
+
<object class="GtkEntry" id="dest_edit">
|
19
|
+
<property name="visible">True</property>
|
20
|
+
<property name="can_focus">True</property>
|
21
|
+
<property name="tooltip_text" translatable="yes">Каталог, куда записывать пожатые фотографии</property>
|
22
|
+
<property name="invisible_char">●</property>
|
23
|
+
</object>
|
24
|
+
<packing>
|
25
|
+
<property name="left_attach">1</property>
|
26
|
+
<property name="right_attach">2</property>
|
27
|
+
<property name="top_attach">1</property>
|
28
|
+
<property name="bottom_attach">2</property>
|
29
|
+
<property name="y_options"></property>
|
30
|
+
</packing>
|
31
|
+
</child>
|
32
|
+
<child>
|
33
|
+
<object class="GtkLabel" id="label1">
|
34
|
+
<property name="visible">True</property>
|
35
|
+
<property name="xpad">3</property>
|
36
|
+
<property name="ypad">3</property>
|
37
|
+
<property name="label" translatable="yes">Исходный каталог</property>
|
38
|
+
<property name="justify">right</property>
|
39
|
+
<property name="single_line_mode">True</property>
|
40
|
+
</object>
|
41
|
+
<packing>
|
42
|
+
<property name="x_options">GTK_FILL</property>
|
43
|
+
<property name="y_options"></property>
|
44
|
+
</packing>
|
45
|
+
</child>
|
46
|
+
<child>
|
47
|
+
<object class="GtkLabel" id="label2">
|
48
|
+
<property name="visible">True</property>
|
49
|
+
<property name="xpad">3</property>
|
50
|
+
<property name="ypad">3</property>
|
51
|
+
<property name="label" translatable="yes">Результирующий каталог</property>
|
52
|
+
<property name="justify">right</property>
|
53
|
+
<property name="single_line_mode">True</property>
|
54
|
+
</object>
|
55
|
+
<packing>
|
56
|
+
<property name="top_attach">1</property>
|
57
|
+
<property name="bottom_attach">2</property>
|
58
|
+
<property name="x_options">GTK_FILL</property>
|
59
|
+
<property name="y_options"></property>
|
60
|
+
</packing>
|
61
|
+
</child>
|
62
|
+
<child>
|
63
|
+
<object class="GtkButton" id="dest_button">
|
64
|
+
<property name="label" translatable="yes">Выбрать...</property>
|
65
|
+
<property name="visible">True</property>
|
66
|
+
<property name="can_focus">True</property>
|
67
|
+
<property name="receives_default">True</property>
|
68
|
+
<signal name="clicked" handler="on_dest_button_clicked"/>
|
69
|
+
</object>
|
70
|
+
<packing>
|
71
|
+
<property name="left_attach">2</property>
|
72
|
+
<property name="right_attach">3</property>
|
73
|
+
<property name="top_attach">1</property>
|
74
|
+
<property name="bottom_attach">2</property>
|
75
|
+
<property name="x_options"></property>
|
76
|
+
<property name="y_options"></property>
|
77
|
+
</packing>
|
78
|
+
</child>
|
79
|
+
<child>
|
80
|
+
<object class="GtkButton" id="src_button">
|
81
|
+
<property name="label" translatable="yes">Выбрать...</property>
|
82
|
+
<property name="visible">True</property>
|
83
|
+
<property name="can_focus">True</property>
|
84
|
+
<property name="receives_default">True</property>
|
85
|
+
<signal name="clicked" handler="on_src_button_clicked"/>
|
86
|
+
</object>
|
87
|
+
<packing>
|
88
|
+
<property name="left_attach">2</property>
|
89
|
+
<property name="right_attach">3</property>
|
90
|
+
<property name="x_options"></property>
|
91
|
+
<property name="y_options"></property>
|
92
|
+
</packing>
|
93
|
+
</child>
|
94
|
+
<child>
|
95
|
+
<object class="GtkButton" id="run_button">
|
96
|
+
<property name="label" translatable="yes">Фигачить!</property>
|
97
|
+
<property name="visible">True</property>
|
98
|
+
<property name="can_focus">True</property>
|
99
|
+
<property name="receives_default">True</property>
|
100
|
+
<signal name="clicked" handler="on_run_button_clicked"/>
|
101
|
+
</object>
|
102
|
+
<packing>
|
103
|
+
<property name="right_attach">3</property>
|
104
|
+
<property name="top_attach">4</property>
|
105
|
+
<property name="bottom_attach">5</property>
|
106
|
+
<property name="x_options"></property>
|
107
|
+
<property name="y_options"></property>
|
108
|
+
</packing>
|
109
|
+
</child>
|
110
|
+
<child>
|
111
|
+
<object class="GtkLabel" id="label3">
|
112
|
+
<property name="visible">True</property>
|
113
|
+
<property name="xpad">3</property>
|
114
|
+
<property name="ypad">3</property>
|
115
|
+
<property name="label" translatable="yes">Сжатие</property>
|
116
|
+
<property name="justify">right</property>
|
117
|
+
<property name="single_line_mode">True</property>
|
118
|
+
</object>
|
119
|
+
<packing>
|
120
|
+
<property name="top_attach">2</property>
|
121
|
+
<property name="bottom_attach">3</property>
|
122
|
+
<property name="x_options">GTK_FILL</property>
|
123
|
+
<property name="y_options"></property>
|
124
|
+
</packing>
|
125
|
+
</child>
|
126
|
+
<child>
|
127
|
+
<object class="GtkHScale" id="ratio_scale">
|
128
|
+
<property name="visible">True</property>
|
129
|
+
<property name="can_focus">True</property>
|
130
|
+
<property name="fill_level">100</property>
|
131
|
+
<property name="digits">0</property>
|
132
|
+
</object>
|
133
|
+
<packing>
|
134
|
+
<property name="left_attach">2</property>
|
135
|
+
<property name="right_attach">3</property>
|
136
|
+
<property name="top_attach">2</property>
|
137
|
+
<property name="bottom_attach">3</property>
|
138
|
+
<property name="y_options"></property>
|
139
|
+
</packing>
|
140
|
+
</child>
|
141
|
+
<child>
|
142
|
+
<object class="GtkEntry" id="size_edit">
|
143
|
+
<property name="visible">True</property>
|
144
|
+
<property name="can_focus">True</property>
|
145
|
+
<property name="tooltip_text" translatable="yes">Максимальный размер фотографии: ШИРИНА x ВЫСОТА или ПРОЦЕНТ%</property>
|
146
|
+
<property name="invisible_char">●</property>
|
147
|
+
</object>
|
148
|
+
<packing>
|
149
|
+
<property name="left_attach">1</property>
|
150
|
+
<property name="right_attach">2</property>
|
151
|
+
<property name="top_attach">2</property>
|
152
|
+
<property name="bottom_attach">3</property>
|
153
|
+
<property name="y_options"></property>
|
154
|
+
</packing>
|
155
|
+
</child>
|
156
|
+
<child>
|
157
|
+
<object class="GtkProgressBar" id="progress_bar"/>
|
158
|
+
<packing>
|
159
|
+
<property name="right_attach">3</property>
|
160
|
+
<property name="top_attach">5</property>
|
161
|
+
<property name="bottom_attach">6</property>
|
162
|
+
<property name="y_options"></property>
|
163
|
+
</packing>
|
164
|
+
</child>
|
165
|
+
<child>
|
166
|
+
<object class="GtkStatusbar" id="status_bar">
|
167
|
+
<property name="visible">True</property>
|
168
|
+
<property name="spacing">2</property>
|
169
|
+
</object>
|
170
|
+
<packing>
|
171
|
+
<property name="right_attach">3</property>
|
172
|
+
<property name="top_attach">6</property>
|
173
|
+
<property name="bottom_attach">7</property>
|
174
|
+
<property name="y_options"></property>
|
175
|
+
</packing>
|
176
|
+
</child>
|
177
|
+
<child>
|
178
|
+
<object class="GtkEntry" id="src_edit">
|
179
|
+
<property name="visible">True</property>
|
180
|
+
<property name="can_focus">True</property>
|
181
|
+
<property name="tooltip_text" translatable="yes">Каталог с исходными фотографиями</property>
|
182
|
+
<property name="invisible_char">●</property>
|
183
|
+
</object>
|
184
|
+
<packing>
|
185
|
+
<property name="left_attach">1</property>
|
186
|
+
<property name="right_attach">2</property>
|
187
|
+
<property name="y_options"></property>
|
188
|
+
</packing>
|
189
|
+
</child>
|
190
|
+
<child>
|
191
|
+
<object class="GtkScrolledWindow" id="scrolledwindow1">
|
192
|
+
<property name="visible">True</property>
|
193
|
+
<property name="can_focus">True</property>
|
194
|
+
<property name="hscrollbar_policy">automatic</property>
|
195
|
+
<property name="vscrollbar_policy">automatic</property>
|
196
|
+
<child>
|
197
|
+
<object class="GtkTextView" id="textview">
|
198
|
+
<property name="visible">True</property>
|
199
|
+
<property name="can_focus">True</property>
|
200
|
+
<property name="editable">False</property>
|
201
|
+
<property name="cursor_visible">False</property>
|
202
|
+
<property name="accepts_tab">False</property>
|
203
|
+
</object>
|
204
|
+
</child>
|
205
|
+
</object>
|
206
|
+
<packing>
|
207
|
+
<property name="right_attach">3</property>
|
208
|
+
<property name="top_attach">3</property>
|
209
|
+
<property name="bottom_attach">4</property>
|
210
|
+
</packing>
|
211
|
+
</child>
|
212
|
+
</object>
|
213
|
+
</child>
|
214
|
+
</object>
|
215
|
+
</interface>
|
data/lib/bconv.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#############################################################################
|
3
|
+
# Yet Another ImageMagic Frontend #
|
4
|
+
# (C) 2010-2011, Vasiliy Yeremeyev <vayerx@gmail.com> #
|
5
|
+
# #
|
6
|
+
# This program is free software: you can redistribute it and/or modify #
|
7
|
+
# it under the terms of the GNU General Public License as published by #
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
9
|
+
# (at your option) any later version. #
|
10
|
+
# #
|
11
|
+
# This program is distributed in the hope that it will be useful, #
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
14
|
+
# GNU General Public License for more details. #
|
15
|
+
# #
|
16
|
+
# You should have received a copy of the GNU General Public License #
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
18
|
+
#############################################################################
|
19
|
+
|
20
|
+
# TODO use 'rmagic' interface instead of Kernel.system
|
21
|
+
|
22
|
+
|
23
|
+
require 'enumerator'
|
24
|
+
|
25
|
+
def number_of_processors
|
26
|
+
raise "Can't determine 'number_of_processors' for '#{RUBY_PLATFORM}'" unless RUBY_PLATFORM =~ /linux/
|
27
|
+
%x[cat /proc/cpuinfo | grep processor | wc -l].to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
class Converter
|
31
|
+
def initialize
|
32
|
+
@source_files = []
|
33
|
+
@launcher_thread = nil
|
34
|
+
@semaphore = Mutex.new
|
35
|
+
@jobs_slots = (number_of_processors * 1.25 + 1).to_i rescue 1
|
36
|
+
@jobs_slots = 1 if @jobs_slots < 1
|
37
|
+
end
|
38
|
+
|
39
|
+
# Asynchronously get directory listing
|
40
|
+
def preload_dir(dir, &block)
|
41
|
+
@counting_thread.terminate if @counting_thread
|
42
|
+
@counting_thread = Thread.new {
|
43
|
+
list_directory(dir)
|
44
|
+
block.call(@source_files.size) if block_given?
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Process directory
|
49
|
+
def process(src_dir, dest_dir, size, ratio, &progress)
|
50
|
+
@launcher_thread.join if @launcher_thread # supposed to be quick enough
|
51
|
+
@launcher_thread = Thread.new {
|
52
|
+
# get directory listing
|
53
|
+
if @counting_thread
|
54
|
+
@counting_thread.join
|
55
|
+
@counting_thread = nil
|
56
|
+
else
|
57
|
+
list_directory(src_dir)
|
58
|
+
end
|
59
|
+
|
60
|
+
unless @source_files.empty?
|
61
|
+
pulse_step = 1 / @source_files.size.to_f
|
62
|
+
@jobs=[]
|
63
|
+
@source_files.each_slice(((@source_files.size + @jobs_slots - 1) / @jobs_slots).to_i) do |files|
|
64
|
+
@jobs << Thread.new do
|
65
|
+
process_directory(src_dir, dest_dir, files, size, ratio, pulse_step, &progress)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
else
|
69
|
+
progress.call(nil, nil, true)
|
70
|
+
end
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
# Terminate all running threads
|
75
|
+
def stop
|
76
|
+
@launcher_thread.terminate if @launcher_thread
|
77
|
+
@jobs.each { |thread| thread.terminate } if @jobs
|
78
|
+
@launcher_thread, @jobs = nil, nil
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Convert files
|
84
|
+
def process_directory(src_dir, dest_dir, source_files, size, ratio, pulse_step, &progress)
|
85
|
+
# rename files if destination is a source directory
|
86
|
+
file_rename =
|
87
|
+
if src_dir == dest_dir
|
88
|
+
Proc.new { |file| file.gsub(/(.*)(\.jpe?g)$/i, "\\1_#{size}\\2") }
|
89
|
+
else
|
90
|
+
Proc.new { |file| file }
|
91
|
+
end
|
92
|
+
|
93
|
+
# process files
|
94
|
+
size_opt = size && size.length != 0 ? "-resize #{size}" : ""
|
95
|
+
source_files.each do |file|
|
96
|
+
src_file = "#{src_dir}/#{file}".gsub(/([\s'()"])/, '\\\\\1')
|
97
|
+
dest_file = "#{dest_dir}/#{file_rename[file]}".gsub(/([\s'()"])/, '\\\\\1')
|
98
|
+
if src_file != dest_file
|
99
|
+
progress.call(file, nil, false)
|
100
|
+
system("convert -quality #{ratio} #{size_opt} #{src_file} #{dest_file}")
|
101
|
+
else
|
102
|
+
progress.call('SKIPPING ' + file, nil, false)
|
103
|
+
end
|
104
|
+
progress.call(nil, pulse_step, false)
|
105
|
+
end
|
106
|
+
ensure
|
107
|
+
@semaphore.synchronize {
|
108
|
+
progress.call(nil, nil, true) if (@jobs_slots -= 1) == 0
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
# Find JPEG files in a directory
|
113
|
+
def list_directory(dir)
|
114
|
+
@source_files = Dir.entries(dir).find_all { |file| /.*jpe?g$/i =~ file }
|
115
|
+
end
|
116
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bconv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vasiliy Yeremeyev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: parseconfig
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.4
|
27
|
+
description: Yet another ImageMagick hardcore gui-frontend with multi-threading. Only
|
28
|
+
Russian language is supported.
|
29
|
+
email: vayerx@gmail.com
|
30
|
+
executables:
|
31
|
+
- bconv
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/bconv.rb
|
36
|
+
- data/bconv.glade
|
37
|
+
- bin/bconv
|
38
|
+
homepage: http://github.com/vayerx/bconv
|
39
|
+
licenses:
|
40
|
+
- GPL-3
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ~>
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 1.9.2
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements:
|
57
|
+
- libglade2, v2.6 or higher
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.0.14
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: ImageMagick frontend for fast jpeg compression.
|
63
|
+
test_files: []
|