Skip to content

Instantly share code, notes, and snippets.

@tcaddy
Created August 20, 2020 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tcaddy/05efe3cf35a9898f8903bcee414e8cf0 to your computer and use it in GitHub Desktop.
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.
#!/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
@tcaddy
Copy link
Author

tcaddy commented Aug 20, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment