--- /dev/null
+#!/usr/bin/ruby -w
+
+#
+# move_to_hierarchy.rb
+#
+# Moves all the files in a directory with the given file extension into a
+# hierarchy. The maximum number of entries at each level is fixed.
+#
+# This is useful for arranging mp3s in a way that makes them easy to play on
+# devices where large directories are impractical.
+#
+# Colin Mccabe
+#
+
+require 'fileutils'
+require 'optparse'
+require 'ostruct'
+
+class MyOptions
+ DEFAULT_FILES_PER_LEVEL = 10
+
+ def self.parse(args)
+ opts = OpenStruct.new
+ opts.dry_run = false
+ opts.files_per_level = DEFAULT_FILES_PER_LEVEL
+ opts.extension = nil
+ $fu_args = { :verbose => true }
+ opts.preserve_names = false
+
+ # Fill in $opts values
+ parser = OptionParser.new do |myparser|
+ myparser.banner = "Usage: #{ File.basename($0) } [opts]"
+ myparser.separator("Specific options:")
+ myparser.on("--files-per-level NFILES", "-L",
+ "Set the number of files per directory to use (default " +
+ DEFAULT_FILES_PER_LEVEL.to_s() + ").") do |d|
+ opts.files_per_level = d.to_i
+ end
+ myparser.on("--dry-run", "-d",
+ "Show what would be done, without doing it.") do |d|
+ $fu_args = { :verbose => true, :noop => true }
+ opts.dry_run = true
+ end
+ myparser.on("--file-extension EXTENSION", "-e",
+ "The file extension for the files to rename.") do |e|
+ opts.extension = e
+ end
+ end
+
+ parser.parse!(args)
+ raise "invalid number of files per level: #{opts.files_per_level}" unless
+ opts.files_per_level > 1
+ raise "must give an extension" unless opts.extension != nil
+ return opts
+ end
+end
+
+def idx_to_path(idx)
+ num_digits = Math.log10($opts.files_per_level + 1).ceil()
+ path = "."
+ (0..idx.length - 2).each do |i|
+ v = idx[i]
+ path = path + "/" + (("%0" + num_digits.to_s + "d") % v)
+ end
+ return path
+end
+
+def idx_increment(idx)
+ i = idx.length - 1
+ while (idx[i] > $opts.files_per_level)
+ idx[i] = 1
+ i = i - 1
+ idx[i] = idx[i] + 1
+ end
+end
+
+# MAIN
+begin
+ $opts = MyOptions.parse(ARGV)
+rescue Exception => msg
+ $stderr.print("#{msg}.\nType --help to see usage information.\n")
+ exit 1
+end
+
+# get number of files
+num_files = 0
+Dir.glob("*.#{$opts.extension}").sort.each do |f|
+ num_files = num_files + 1
+end
+if (num_files == 0):
+ raise "No files found in the current working directory with extension " +
+ $opts.extension
+end
+num_levels = Math.log(num_files) / Math.log($opts.files_per_level)
+num_levels = num_levels.ceil()
+if ($fu_args[:verbose]) then
+ print "Number of files: " + num_files.to_s() + ". Number of levels: " +
+ num_levels.to_s() + "\n"
+end
+
+idx = Array.new
+(1..num_levels).each { |n| idx.push(1) }
+
+files_in_dir = 0
+Dir.glob("*.#{$opts.extension}").sort.each do |f|
+ path = idx_to_path(idx)
+ FileUtils.mkdir_p(path, $fu_args)
+ FileUtils.mv(f, path + "/" + f, $fu_args)
+ idx[idx.length - 1] = idx[idx.length - 1] + 1
+ if (idx[idx.length - 1] > $opts.files_per_level)
+ idx_increment(idx)
+ end
+end
+
+exit 0