Created
August 20, 2020 20:57
-
-
Save tcaddy/05efe3cf35a9898f8903bcee414e8cf0 to your computer and use it in GitHub Desktop.
Put mp3s in folders based on id3 tags for artist and album. Create filenames based on disk #, track #, and title from id3 tags.
This file contains 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
#!/usr/bin/env ruby | |
begin | |
require 'fileutils' | |
require 'id3tag' | |
require 'pry' | |
class FolderifyMp3s | |
REGEX = /\(|\)|\/|\\/.freeze | |
def call | |
mp3s.each do |mp3| | |
process_mp3(file: mp3) | |
end | |
end | |
private | |
def initialize(folder:, dest:, test_mode: true) | |
@folder = folder | |
@dest = dest | |
@test_mode = test_mode | |
end | |
def mp3s | |
Dir.entries(@folder).select do |filename| | |
filename =~ /\.mp3\s*$/i | |
end | |
end | |
def process_mp3(file:) | |
tag = ID3Tag.read(File.open("#{@folder}/#{file}", 'rb')) | |
begin | |
debug_mp3(file: file, tag: tag) if @test_mode | |
fn = "#{mp3_directory!(tag: tag)}/#{filename(tag: tag)}" | |
raise "File already exists!!! #{fn}" if File.exist?(fn) | |
FileUtils.mv( | |
"#{@folder}/#{file}", | |
fn, | |
verbose: true, | |
noop: @test_mode | |
) | |
rescue RuntimeError => e | |
binding.pry | |
end | |
end | |
def debug_mp3(file:, tag:) | |
puts "\n---\n" | |
puts "path: #{file}" | |
puts "artist: #{tag.artist}" | |
puts "album: #{tag.album}" | |
puts "disk: #{disk(tag: tag)}" | |
puts "track: #{track(tag: tag)}" | |
puts "title: #{tag.title}" | |
end | |
def mp3_directory!(tag:) | |
unless Dir.exist?("#{@dest}/#{artist(tag: tag)}") | |
FileUtils.mkdir("#{@dest}/#{artist(tag: tag)}", noop: @test_mode) | |
end | |
unless Dir.exist?("#{@dest}/#{artist(tag: tag)}/#{album(tag: tag)}") | |
FileUtils.mkdir( | |
"#{@dest}/#{artist(tag: tag)}/#{album(tag: tag)}", noop: @test_mode | |
) | |
end | |
"#{@dest}/#{artist(tag: tag)}/#{album(tag: tag)}" | |
end | |
def artist(tag:) | |
if tag.artist.to_s.gsub(/^\s*$/, '') == '' | |
'Unknown Artist' | |
else | |
tag.artist.gsub(REGEX, '') | |
end | |
end | |
def album(tag:) | |
if tag.album.to_s.gsub(/^\s*$/, '') == '' | |
'Unknown Album' | |
else | |
tag.album.gsub(REGEX, '') | |
end | |
end | |
def filename(tag:) | |
[ | |
disk(tag: tag), track(tag: tag), tag.title | |
].compact.join(' - ').gsub(REGEX, '').chomp('.mp3') + '.mp3' | |
end | |
def disk(tag:) | |
disk_or_track(num: tag.get_frame(:TPOS).content.to_s.split('/')[0]) | |
rescue RuntimeError | |
nil | |
end | |
def track(tag:) | |
disk_or_track(num: tag.track_nr.to_s.split('/')[0]) | |
end | |
def disk_or_track(num:) | |
return nil if num.to_i.zero? | |
n | |
end | |
end | |
if File.expand_path(__FILE__) == File.expand_path($PROGRAM_NAME) | |
(1..5).each do |i| | |
FolderifyMp3s.new( | |
folder: "/mnt/media/plex/google-takeout/50gb/#{i}/Takeout/Google Play Music/Tracks", | |
dest: '/mnt/media/plex/google-takeout/50gb/organized_music', | |
test_mode: false | |
).call | |
end | |
end | |
rescue LoadError => e | |
puts "LoadError: #{e.message}" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
With Google Play Music being killed off, I used the Takeout process to get my music. All the decompressed archives put the music in one folder.
I made the move to Plex (https://www.plex.tv/), which put everything in an "Unknown Artist" category b/c the music was in one directory. This script worked pretty well to put things in an organized folder structure.
Things like movie soundtracks or albums where the "Artist" id3 tag was not consistent are still kinda a mess, but the bulk of my albums are looking good in the Plex environment.
Might follow-up to this gist with another script to try to make sense out of the soundtracks / albums with inconsistent artists.