5 # A.k.a `Best web-album Of the world, Or your money back, Humerus'.
7 # The acronyn sucks, however this is a tribute to Dragon Ball by
8 # Akira Toriyama, where the last enemy beaten by heroes of Dragon
9 # Ball is named "Boo". But there was already a free software project
10 # called Boo, so this one will be it "Booh". Or whatever.
13 # Copyright (c) 2004 Guillaume Cottenceau <gc3 at bluewin.ch>
15 # This software may be freely redistributed under the terms of the GNU
16 # public license version 2.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 bindtextdomain("booh")
29 require 'booh/config.rb'
30 require 'booh/version.rb'
34 $CURRENT_CHARSET = `locale charmap`.chomp
35 $convert = 'convert -interlace line +profile "*"'
36 $convert_enhance = '-contrast -enhance -normalize'
39 return Iconv::iconv("UTF-8", $CURRENT_CHARSET, string).to_s
43 return Iconv::iconv($CURRENT_CHARSET, "UTF-8", string).to_s
46 def make_dest_filename(orig_filename)
47 #- we remove non alphanumeric characters but need to do that
48 #- cleverly to not end up with two similar dest filenames. we won't
49 #- urlencode because urldecode might happen in the browser.
50 return orig_filename.unpack("C*").collect { |v| v.chr =~ /[a-zA-Z\-_0-9\.\/]/ ? v.chr : sprintf("%2X", v) }.to_s
53 def msg(verbose_level, msg)
54 if verbose_level <= $verbose_level
56 warn _("\t***ERROR***: %s\n") % msg
57 elsif verbose_level == 1
58 warn _("\tWarning: %s\n") % msg
65 def msg_(verbose_level, msg)
66 if verbose_level <= $verbose_level
68 warn _("\t***ERROR***: %s") % msg
69 elsif verbose_level == 1
70 warn _("\tWarning: %s") % msg
82 def select_theme(name)
84 msg 3, _("Selecting theme `%s'") % $theme
85 themedir = "#{$FPATH}/themes/#{$theme}"
86 if !File.directory?(themedir)
87 die _("Theme was not found (tried %s directory).") % themedir
89 require "#{themedir}/metadata/parameters.rb"
90 $default_size = $images_size.detect { |sizeobj| sizeobj['default'] }
94 if entry =~ /\.(jpg|jpeg|jpe|gif|bmp|png)$/i
96 elsif entry =~ /\.(mov|avi|mpg|mpeg|mpe|wmv|asx)$/i
97 #- might consider using file magic later..
109 #- parallelizable sys
119 if $pids.length == $mproc
120 finished = Process.wait2
121 $pids.delete(finished[0])
122 $pids = $pids.find_all { |pid| Process.waitpid(pid, Process::WNOHANG) == nil }
129 #- grab the results of a command but don't sleep forever on a runaway process
130 def subproc_runaway_aware(command)
135 rescue Timeout::Error
136 msg 1, _("forgetting runaway process (transcode sucks again?)")
137 #- todo should slay transcode but dunno how to do that
142 def get_image_size(fullpath)
143 if `identify '#{fullpath}'` =~ / JPEG (\d+)x(\d+) /
144 return { :x => $1, :y => $2 }
150 #- commify from http://pleac.sourceforge.net/ (pleac rulz)
152 n.to_s =~ /([^\.]*)(\..*)?/
153 int, dec = $1.reverse, $2 ? $2 : ""
154 while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2' + _(",") + '\3')
159 def guess_rotate(filename)
160 orientation = `exif '#{filename}'`.detect { |line| line =~ /^Orientation/ }
161 if orientation =~ /right - top/
163 elsif orientation =~ /left - bottom/
168 size = get_image_size(filename)
169 #- remove rotate if image is obviously already in portrait (situation can come from gthumb)
170 if size && size[:x] < size[:y]
177 def rotate_pixbuf(pixbuf, angle)
178 return pixbuf.rotate(angle == 90 ? Gdk::Pixbuf::ROTATE_CLOCKWISE :
179 angle == 180 ? Gdk::Pixbuf::ROTATE_UPSIDEDOWN :
180 angle == 270 ? Gdk::Pixbuf::ROTATE_COUNTERCLOCKWISE :
181 Gdk::Pixbuf::ROTATE_NONE)
184 def gen_thumbnails_element(orig, xmldir, allow_background, dests)
185 gen_thumbnails(orig, xmldir, allow_background, dests, xmldir.elements["[@filename='#{utf8(File.basename(orig))}']"], '')
188 def gen_thumbnails_subdir(orig, xmldir, allow_background, dests, type)
189 #- type can be `subdirs' or `thumbnails'
190 gen_thumbnails(orig, xmldir, allow_background, dests, xmldir, type + '-')
193 def gen_thumbnails(orig, xmldir, allow_background, dests, felem, attributes_prefix)
194 if !dests.detect { |dest| !File.exists?(dest['filename']) }
200 if entry2type(orig) == 'image'
202 rotate = felem.attributes["#{attributes_prefix}rotate"]
204 felem.add_attribute("#{attributes_prefix}rotate", rotate = guess_rotate(orig).to_i)
206 convert_options += "-rotate #{rotate} "
207 if felem.attributes["#{attributes_prefix}enhance"]
208 convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
212 if !File.exists?(dest['filename'])
213 cmd = "#{$convert} #{convert_options}-size #{dest['size']} -resize #{dest['size']} '#{orig}' '#{dest['filename']}'"
223 elsif entry2type(orig) == 'video'
224 dest_dir = make_dest_filename(File.dirname(dests[0]['filename']))
226 #- frame-offset is an attribute that allows to specify which frame to use for the thumbnail
227 frame_offset = felem.attributes["#{attributes_prefix}frame-offset"]
229 felem.add_attribute("#{attributes_prefix}frame-offset", frame_offset = "0")
231 frame_offset = frame_offset.to_i
232 if rotate = felem.attributes["#{attributes_prefix}rotate"]
233 convert_options += "-rotate #{rotate} "
235 if felem.attributes["#{attributes_prefix}enhance"]
236 convert_options += ($config['convert-enhance'] || $convert_enhance) + " "
240 if !File.exists?("#{dest_dir}/screenshot.jpg000000.jpg")
241 transcode_options = ''
243 if felem.attributes["#{attributes_prefix}color-swap"]
244 transcode_options += '-k '
247 cmd = "transcode -a 0 -c #{frame_offset}-#{frame_offset+1} -i '#{orig}' -y jpg -o '#{dest_dir}/screenshot.jpg' #{transcode_options} 2>&1"
249 results = subproc_runaway_aware(cmd)
250 if results =~ /skipping frames/ && results =~ /encoded 0 frames/
251 msg 0, _("specified frame-offset too large? max frame was: %s. that may also be another probleme. try another value.") %
252 results.scan(/skipping frames \[000000-(\d+)\]/)[-1]
254 elsif results =~ /V: import format.*unknown/ || !File.exists?("#{dest_dir}/screenshot.jpg000000.jpg")
255 msg 2, _("* could not extract first image of video %s with transcode, will try first converting with mencoder") % orig
256 cmd = "mencoder '#{orig}' -nosound -ovc lavc -lavcopts vcodec=mjpeg -o '#{dest_dir}/foo.avi' -frames #{frame_offset+1} -fps 25 >/dev/null 2>/dev/null"
259 if File.exists?("#{dest_dir}/foo.avi")
260 cmd = "transcode -a 0 -c #{frame_offset}-#{frame_offset+1} -i '#{dest_dir}/foo.avi' -y jpg -o '#{dest_dir}/screenshot.jpg' #{transcode_options} 2>&1"
262 results = subproc_runaway_aware(cmd)
263 system("rm -f '#{dest_dir}/foo.avi'")
264 if results =~ /skipping frames/ && results =~ /encoded 0 frames/
265 msg 0, _("specified frame-offset too large? max frame was: %s. that may also be another probleme. try another value.") %
266 results.scan(/skipping frames \[000000-(\d+)\]/)[-1]
268 elsif results =~ /V: import format.*unknown/ || !File.exists?("#{dest_dir}/screenshot.jpg000000.jpg")
269 msg 0, _("could not extract first image of video %s encoded by mencoder") % "#{dest_dir}/foo.avi"
273 msg 0, _("could not make mencoder to encode %s to mpeg4") % orig
279 if !File.exists?(dest['filename'])
280 sys("#{$convert} #{convert_options}-size #{dest['size']} -resize #{dest['size']} #{dest_dir}/screenshot.jpg000000.jpg '#{dest['filename']}'")
287 def invornil(obj, methodname)
291 return obj.method(methodname).call
295 def find_subalbum_info_type(xmldir)
296 #- first look for subdirs info; if not, means there is no subdir
297 if xmldir.attributes['subdirs-caption']
304 def find_subalbum_caption_info(xmldir)
305 type = find_subalbum_info_type(xmldir)
306 return from_utf8(xmldir.attributes["#{type}-captionfile"]), xmldir.attributes["#{type}-caption"]