#!/usr/bin/env ruby # frozen_string_literal: true require 'pathname' require 'open3' # Renames music files in a directory according to the file's tags. # Expects two arguments: # - Path to the zip file with music files. # - Music directory to extract songs into. class Song attr_reader :title, :track, :extension def initialize(extension) @extension = extension end def title=(title) @title = title.strip end def track=(track) @track = track.strip.split('/').first.rjust(2, '0') end def to_s @track + ' - ' + @title + @extension end end def find_unnamed_directory(parent_path) parent_path.children.filter { |child| child.basename.to_s.start_with? '_' }.first end def extract_and_rename_archive(album_archive, music_directory) artist_name, album_name = album_archive.basename('.zip').to_s.split(' - ') system 'unzip', '-d', music_directory.to_path, album_archive.to_path, exception: true artist_path = music_directory + artist_name album_path = artist_path + album_name source_artist_path = find_unnamed_directory music_directory if artist_path.exist? find_unnamed_directory(source_artist_path).rename album_path source_artist_path.unlink else source_artist_path.rename artist_path find_unnamed_directory(artist_path).rename album_path end album_path end def probe_song(song_path) song = Song.new song_path.extname Open3.popen3 'ffprobe', song_path.to_s do |_stdin, _stdout, stderr, _wait_pid| while (line = stderr.gets) key, value = line.split ':' next if value.nil? case key.strip.downcase when 'title' song.title = value if song.title.nil? when 'track' song.track = value if song.track.nil? end end end song end album_archive = Pathname.new ARGV[0] music_directory = Pathname.new ARGV[1] metadata = {} album_path = extract_and_rename_archive album_archive, music_directory Dir.each_child album_path do |filename| song_path = album_path + filename metadata[song_path] = probe_song(song_path).to_s end metadata.each_pair do |from, to| File.rename(from, album_path + to) end