Last active
February 20, 2018 02:21
-
-
Save alexblackie/8e3688b245991c0d574df4d97f695b51 to your computer and use it in GitHub Desktop.
This is how I manage my music library. SHA256 hashes as filenames, transcoded to MP3@320K, is what gets distributed to my phones, laptops, etc. FLAC/MP3 masters get left on the NAS.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# vim: ts=4 sw=4 noexpandtab | |
require "digest" | |
require "fileutils" | |
class Transcoder | |
CONCURRENCY = 8.freeze | |
DEFAULT_OUTPUT_DIR = File.join(File.dirname(__FILE__), "..", "mp3s").freeze | |
MANIFEST_FILE = ".transcode.manifest".freeze | |
MANIFEST_DELIMITER = "||".freeze | |
# @param output [String] path to the directory to store output MP3s | |
def initialize(target: DEFAULT_OUTPUT_DIR) | |
@flacs = Dir["**/*.flac"] | |
@mp3s = Dir["**/*.mp3"] | |
@existing = Dir["#{ target }/*.mp3"] | |
@target = target | |
@manifest = populate_manifest() | |
FileUtils.mkdir_p(@target) | |
end | |
def transcode | |
@flacs.each do |f| | |
next if @manifest.has_key?(f) | |
target = dest(f) | |
puts "Transcoding #{ f} -> #{ target }" | |
unless File.exist?(target) | |
`ffmpeg -loglevel panic -i "#{ f }" -threads #{ CONCURRENCY } -acodec libmp3lame -b:a 320k '#{ dest(f) }'` | |
end | |
@manifest[f] = target | |
end | |
@mp3s.each do |f| | |
next if @manifest.has_key?(f) | |
target = dest(f) | |
puts "Copying #{ f } -> #{ target }" | |
unless File.exist?(target) | |
FileUtils.cp(f, target) | |
end | |
@manifest[f] = target | |
end | |
write_manifest() | |
end | |
private | |
def dest(file) | |
"#{ @target }/#{ hashify(file) }.mp3" | |
end | |
def hashify(file) | |
Digest::SHA256.hexdigest(File.read(file)).upcase | |
end | |
def write_manifest | |
content = @manifest.reduce(""){|acc, (orig,dest)| acc + orig + MANIFEST_DELIMITER + dest + "\n"} | |
File.write(MANIFEST_FILE, content) | |
end | |
def populate_manifest | |
FileUtils.touch(MANIFEST_FILE) unless File.exist?(MANIFEST_FILE) | |
File. | |
read(MANIFEST_FILE). | |
lines. | |
reduce({}) do |acc, l| | |
line = l.split(MANIFEST_DELIMITER) | |
acc[line.first] = line.last | |
acc | |
end | |
end | |
end | |
Transcoder.new.transcode |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment