Skip to content

Instantly share code, notes, and snippets.

@don-code
Created January 3, 2021 17:46
Show Gist options
  • Save don-code/9dac1bd06ac797876fa9fa6cf49be821 to your computer and use it in GitHub Desktop.
Save don-code/9dac1bd06ac797876fa9fa6cf49be821 to your computer and use it in GitHub Desktop.
Sync FLAC music library to dumb MP3 players.
#!/usr/bin/env ruby
# This script syncs music to an MP3 player that only plays MP3s, according to
# the following rules:
#
# 1. Always copy files using a scheme that makes it easy to navigate based on folders:
# $ARTIST/$ALBUM/$TRACK.mp3
# 2. If the destination track already exists as an MP3, do nothing.
# 3. If the source track is an MP3, copy it without any processing.
# 4. If the source track is a FLAC, transcode it to MP3 and copy the MP3.
#
# Adjust MUSIC_SOURCE and MUSIC_TARGET to meet your needs.
#
# The "MP3 player" this script was intended for is actually a car head unit with
# a rudimentary folder browser.
require 'fileutils'
MUSIC_SOURCE = '/home/don/Music'
MUSIC_TARGET = '/run/media/don/USB DISK'
def sync_directory(artist, album = nil)
puts "Artist: #{artist} | Album: #{album}"
source_dir = build_path(MUSIC_SOURCE, '/', artist, album)
target_dir = build_path(MUSIC_TARGET, ' - ', artist, album)
songs_to_sync = Dir.children(source_dir).select do |child|
is_music = false
%w(flac mp3 m4a mp4 wma).each do |type|
if child.end_with?(type)
is_music = true
break
end
end
is_music
end
Dir.mkdir(target_dir) if !Dir.exist?(target_dir) && songs_to_sync.count > 0
songs_to_sync.each do |song_to_sync|
full_path = "#{source_dir}/#{song_to_sync}"
sync_file(full_path, target_dir)
end
end
def sync_file(source_file, target_dir)
if source_file.end_with?('mp3') # pass through
return if File.exist?("#{target_dir}/#{File.basename(source_file)}")
puts "Copying MP3: #{source_file}"
FileUtils.cp(source_file, target_dir)
else # transcode
dest_file = "#{target_dir}/#{File.basename(source_file).gsub(File.extname(source_file), '.mp3')}"
return if File.exist?(dest_file)
puts "Transcoding: #{source_file}"
`ffmpeg -i "#{source_file}" -b:a 320k "#{dest_file}"`
end
end
def build_path(location, join, artist, album = nil)
path = "#{location}/#{artist}"
path += "#{join}#{album}" if album
path
end
def subdirectories(path)
Dir.children(path).select { |x| File.directory?("#{path}/#{x}") }
end
artists = subdirectories(MUSIC_SOURCE)
artists.each do |artist|
sync_directory(artist)
albums = subdirectories("#{MUSIC_SOURCE}/#{artist}")
albums.each do |album|
sync_directory(artist, album)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment