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