Projects

Ticket #465: find-photos.rb

File find-photos.rb, 4.8 KB (added by roberto@…, 2 years ago)

find-photos.rb

Line 
1#! /opt/local/bin/ruby
2# Encoding: UTF-8
3#
4# Author::      Ollivier Robert
5# Copyright::   Â© 2007-2008 by Ollivier Robert
6# Description:: Identify in which format an image exist
7#
8# Long description:
9#
10# From a given tree root, find all images in DNG/PEF/JPG formats,
11# displays partial path from the root and sizes for all.
12#
13# BUGS
14#
15# XXX highly specific to my own setup (the -new hack) and Pentax-specific
16#
17# An ImagePool is made of ImageBuckets.
18# An ImageBucket is made of potentially several formats and several versions
19# per format
20
21# VCS ID
22MYNAME = File.basename($0)
23ID = "$Id: find-photos.rb,v 17656f563b53 2008/10/03 14:04:50 roberto $"
24
25# Standard packages
26#
27require "optparse"
28
29$verbose = false
30
31# === message
32#
33def message(msg)
34  if $verbose then
35    puts(msg)
36  end
37end # -- message
38
39# == class Image
40#
41# Contain partial path (below the root of the ImagePool) path
42# different types may be in different directories so store all partial
43# paths
44#
45class ImageBucket
46  include Enumerable
47  attr_reader :base_name, :raw, :dng, :jpg, :pef, :nef, :cr2, :crw
48  attr_reader :types
49 
50  # === initialize
51  #
52  def initialize(base_name)
53    @base_name = base_name
54    @types = Hash.new
55    @dng = 0
56    @jpg = 0
57    @pef = 0
58    @raw = 0
59    @nef = 0
60    @cr2 = 0
61    @crw = 0
62  end # -- initialize
63
64  # === add
65  #
66  def add(base_dir, type)
67    tmp = @types[type] || Array.new
68    tmp << "#{base_dir}/#{@base_name}.#{type}"
69    @types[type] = tmp
70    case type
71    when /DNG/
72      @dng += 1
73      @raw += 1
74    when /dng/
75      @dng += 1
76      @raw += 1
77    when /PEF/i
78      @pef += 1
79      @raw += 1
80    when /NEF/i
81      @nef += 1
82      @raw += 1
83    when /CR2/i
84      @nef += 1
85      @raw += 1
86    when /CRW/i
87      @nef += 1
88      @raw += 1
89    when /JPEG/i
90      @jpg += 1
91    when /JPG/i
92      @jpg += 1
93    end
94    message("Adding #{type} to #{base_name}")
95  end # -- add
96
97  # === each
98  #
99  def each
100    @types.each do |type, base_path|
101      yield(type, base_path)
102    end
103  end # -- each
104
105end # -- ImageBucket
106
107# == class ImagePool
108#
109class ImagePool
110  include Enumerable
111  attr_reader :root, :size, :pool, :total, :stats
112
113  # === initialize
114  #
115  def initialize(root, now = true, type = nil)
116    @root = root
117    @pool = Hash.new
118    @size = 0
119    @total = 0
120    @stats = Hash.new(0)
121    if now then
122      read(type)
123    end
124  end # -- initialize
125
126  # === read
127  #
128  # General format: base_dir/base_name.base_type
129  #
130  # For every base_name, base_dir/base_type can change
131  #
132  def read(type = nil)
133    Dir.chdir(@root) do
134      Dir.glob("**/*.{JPG,jpg,DNG,dng,PEF,pef,NEF,CR2,CRW}").each do |full_name|
135
136        fname = File.basename(full_name)
137        fname =~%r{(.*)\.(\w+)$}
138        base_name = $1
139        base_type = $2
140        base_dir = File.dirname(full_name)
141
142        message("(#{base_dir})(#{base_name}).(#{base_type})")
143
144        if @pool[base_name].nil? then
145          img = ImageBucket.new(base_name)
146          img.add(base_dir, base_type)
147          @pool[base_name] = img
148          @size += 1
149        else
150          @pool[base_name].add(base_dir, base_type)
151        end
152        @total += 1
153        @stats[base_type] += 1
154      end # -- Dir.glob
155    end # -- Dir.chdir
156  end # -- read
157
158  # === add
159  #
160  def add(img)
161    @pool[img.base_name].add(img)
162  end # -- add
163
164  # === <<
165  #
166  def <<(img)
167    self.add(img)
168  end # -- <<
169
170  # === each
171  #
172  def each
173    @pool.each do |base_name, img|
174      yield(base_name, img)
175    end
176  end # -- each
177
178  # === keys
179  #
180  def keys
181    return @pool.keys
182  end # -- keys
183
184  # === []
185  #
186  def [](base_name)
187    return @pool[base_name]
188  end # -- []
189
190end # -- ImagePool
191
192# == main
193#
194def main(argv)
195  usage = <<-"EOTEXT"
196Usage: #{MYNAME} [-hv] DIR
197  EOTEXT
198 
199  banner = <<-"EOTEXT"
200#{MYNAME}
201Revision #{ID}
202
203#{usage}
204  EOTEXT
205
206  # Process options
207  #
208  argv.options do |opts|
209    opts.banner = banner
210    opts.on("-v", "--verbose", "Be more verbose.") do
211      $verbose = true
212    end
213    opts.on("-h", "--help", "Display this.") do
214      puts banner
215      return 255
216    end
217    opts.parse!
218  end
219
220  argv.options = nil
221  root_dir = argv.shift
222
223  if root_dir.nil? then
224    $stderr.puts("You must specify a directory to look into!")
225    return 1
226  end
227
228  # Down to work
229  #
230  ts = Time.new
231  list = ImagePool.new(root_dir)
232  te = Time.new - ts
233  puts "Root is #{root_dir}"
234  puts "#{list.size} photos with #{list.total} variants found in #{te}s"
235  list.keys.sort.each do |base_name|
236    puts "#{base_name}"
237    img = list[base_name]
238    img.each do |type, partial_path|
239      partial_path.each do |path|
240        full_path = "#{list.root}/#{path}"
241        size = File.size(full_path)
242        puts #{type}: #{path} (#{size})"
243      end
244    end
245  end
246 
247  # Some stats
248  #
249  list.stats.each do |type, nb|
250    puts "#{nb} #{type} photos"
251  end
252  return 0
253end # -- main
254
255if $0 == __FILE__ then
256  exit(main(ARGV) || 1)
257end