bconv 0.0.6
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 +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: []
|