Add move_to_hierarchy.rb
authorColin Patrick Mccabe <cmccabe@alumni.cmu.edu>
Mon, 30 Jul 2012 08:00:27 +0000 (01:00 -0700)
committerColin Patrick Mccabe <cmccabe@alumni.cmu.edu>
Mon, 30 Jul 2012 08:00:27 +0000 (01:00 -0700)
Signed-off-by: Colin McCabe <cmccabe@alumni.cmu.edu>

move_to_hierarchy.rb [new file with mode: 0755]

diff --git a/move_to_hierarchy.rb b/move_to_hierarchy.rb
new file mode 100755 (executable)
index 0000000..5ae3b51
--- /dev/null
@@ -0,0 +1,115 @@
+#!/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