Skip to content

Instantly share code, notes, and snippets.

@keithrbennett
Last active October 11, 2019 01:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save keithrbennett/4d9953e66ea35e2c52abae52650ebb1b to your computer and use it in GitHub Desktop.
Save keithrbennett/4d9953e66ea35e2c52abae52650ebb1b to your computer and use it in GitHub Desktop.
Helps separate your audio and video files into saves and deletes using Ruby and MPlayer.
#!/usr/bin/env ruby
# organize-av-files - Organizes files playable by mplayer
# into 'saves', 'deletes', and 'undecideds' subdirectories
# of the current working directory.
#
# Be careful, if you specify files to process in multiple directories,
# they will all be moved to the same subdirectories, so they will no
# longer be organized by directory, and if there are multiple files
# of the same name, some may be lost if overwritten.
#
# stored at:
# https://gist.github.com/keithrbennett/4d9953e66ea35e2c52abae52650ebb1b
require 'date'
require 'fileutils'
require 'set'
LOG_FILESPEC = 'organize-av-files.log'
def create_dirs
%w{deletes saves undecideds}.each { |dir| FileUtils.mkdir_p(dir) }
end
def check_presence_of_mplayer
if `which mplayer`.chomp.size == 0
raise "mplayer not detected. "
"Please install it (with apt, brew, yum, etc.)"
end
end
# Takes all ARGV elements, expands any wildcards,
# converts to normalized (absolute) form,
# and eliminates duplicates.
def files_to_process
# Dir[] does not understand ~, need to process it ourselves.
# This does *not* handle the `~username` form.
replace_tilde_if_needed = ->(filespec) do
filespec.start_with?('~/') \
? File.join(ENV['HOME'], filespec[2, -1]) \
: filespec
end
# When Dir[] gets a directory it returns no files.
# Need to add '/*' to it.
add_star_to_dirspec_if_needed = ->(filespec) do
File.directory?(filespec) \
? File.join(filespec, '*') \
: filespec
end
# Default to all nonhidden files in current directory
# but not its subdirectories.
ARGV[0] ||= '*'
all_filespecs = ARGV.each_with_object(Set.new) do |filemask, all_filespecs|
filemask = replace_tilde_if_needed.(filemask)
filemask = add_star_to_dirspec_if_needed.(filemask)
Dir[filemask] \
.map { |f| File.absolute_path(f) } \
.select { |f| File.file?(f) } \
.each do |filespec|
all_filespecs << filespec
end
end
all_filespecs.sort
end
def greeting
puts <<~GREETING
organize-av-files
Enables the vetting of audio and video files.
For each file, plays it with mplayer, and prompts for what you would like to do
with that file, moving the file to one of the following subdirectories:
* deletes
* saves
* undecideds
This software uses mplayer to play audio files. Use cursor keys to move forwards/backwards in time.
Press 'q' or 'ESC' to abort playback and specify disposition of that file.
Run `man mplayer` for more on mplayer.
Assumes all files specified are playable by mplayer.
Creates subdirectories in the current directory: deletes, saves, undecideds.
Logs to file '#{LOG_FILESPEC}'
GREETING
end
def play_file(filespec)
# If you have mplayer problems, remove the redirection ("2> /dev/null")
# to see any errors.
`mplayer #{filespec} 2> /dev/null`
end
def disposition_prompt(filespec)
"\n\n#{filespec}:\ns = save, d = delete, u = undecided, q = quit: "
end
def get_disposition_from_user
loop do
response = $stdin.gets.chomp.downcase
if response == 'q'
exit
elsif %w(s d u).include?(response)
return {
's' => 'saves',
'd' => 'deletes',
'u' => 'undecideds'
}[response]
else
print "s = save, d = delete, u = undecided, q = quit: "
end
end
end
def log(filespec, destination_subdir)
dest_abbrev = destination_subdir[0].upcase # 'S' for saves, etc.
log_message = "#{dest_abbrev} #{Time.now} #{filespec}"
`echo #{log_message} >> #{LOG_FILESPEC}`
end
def main
check_presence_of_mplayer
create_dirs
puts greeting
files_to_process.each do |filespec|
play_file(filespec)
print disposition_prompt(filespec)
destination_subdir = get_disposition_from_user
`mv #{filespec} #{destination_subdir}`
log(filespec, destination_subdir)
end
end
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment