commit 4a4848953ddf8e73901a8a595adc02d320946d65
Author: lostd <lostd@2f30.org>
Date: Fri, 6 Jul 2012 15:13:35 +0000
Time to start versioning this thingy
Diffstat:
A | bin/musicfix | | | 185 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 185 insertions(+), 0 deletions(-)
diff --git a/bin/musicfix b/bin/musicfix
@@ -0,0 +1,185 @@
+#!/usr/bin/env ruby
+
+# Musicfix is a music file renamer and tagger with consistency concerns.
+# It has a release-centered logic and gets data from www.discogs.com.
+# First configure the target music directory (mdir variable).
+# Next browse the site and locate your album. Change to the album's
+# directory. Ensure the files are ordered and provide the release id
+# to the program. Enjoy!
+
+# Dependencies:
+# System packages: taglib
+# Ruby gems: discogs-wrapper stringex taglib-ruby
+
+require 'rubygems'
+require 'fileutils'
+require 'open-uri'
+require 'discogs'
+require 'stringex'
+require 'taglib'
+
+# Configuration
+mdir = '/var/music/external'
+mytrack = '"#{mdir}/#{fba}-#{my}-#{fb}-#{fv}/#{d}#{fn}-#{fa}-#{ft}.#{x}"'
+myimage = '"#{mdir}/#{fba}-#{my}-#{fb}-#{fv}/#{zz}-#{fba}-#{fb}_cover.jpg"'
+
+# Naming conventions
+# The 'f' prefix means "normalized for filename"
+#
+# Use everywhere:
+# [f]b: album name
+# [f]ba: album artist
+# y: release year
+# my: master release year
+# g: genre style
+# [f]v: release format
+#
+# Only for mytrack:
+# [f]a: track artist
+# [f]t: track title
+# [f]n: track number, may have letters (vinyls)
+# d: disc number
+# tn: track number counter
+# x: file extension in lowercase
+#
+# Only for myimage:
+# zz: zeros that match d + n width
+
+# Concatenate artist list using '&'
+# Convert "Sound, The (2)" to "The Sound"
+def mkartist al
+ nl = al.collect {|a| a.name}
+ nl.each {|n| n.gsub! /\s\(\d+\)$/, ''}
+ nl.each {|n| n.gsub! /(.*), The$/, 'The \1'}
+ nl.join ' & '
+end
+
+# Convert "3" to (nil, "03")
+# Convert "A3" to (nil, "A3")
+# Convert "2.3" to ("2", "03")
+# Convert "2.03" to ("2", "03")
+# Convert "CD2-3" to ("2", "03")
+def mkdiscnum pos
+ np = pos.gsub /[-.]/, '#'
+ d, n = np.split '#'
+ if not n
+ n = d
+ d = nil
+ else
+ d = d.gsub /\D/, ''
+ end
+ n = n.rjust(2, '0')
+ return d, n
+end
+
+# Convert to lowercase ASCII without punctuation
+def mkname n
+ n = n.gsub '12"', '12 inch'
+ n = n.gsub "12''", "12 inch"
+ n = n.gsub "12'", "12 inch"
+ n = n.gsub '7"', '7 inch'
+ n = n.gsub "7''", "7 inch"
+ n = n.gsub "7'", "7 inch"
+ n.to_url.gsub '-', '_'
+end
+
+# Get cover artwork
+def getimgurl rel
+ img = rel.images.select {|i| i.type == 'primary'}.first ||
+ rel.images.first ||
+ (return nil)
+ img.uri.gsub 'api.discogs.com', 's.dsimg.com'
+end
+
+# Parse command line
+relid = ARGV[0] || (puts "Usage: musicfix release_id"; exit)
+
+# Construct file list
+fl = Dir['*'].select {|f| File.extname(f).match /mp3|ogg|mpc|flac|wv/i}.sort
+puts 'Files to process:'
+puts fl
+
+# Some test releases:
+# Sound, The (2) – From The Lions Mouth: 377432
+# Various – Return Of The Banshee: 127565
+# Harold Budd & Brian Eno – The Pearl: 699395
+# Dead Souls Rising – Clepsydre 1993-1999: 1212954
+# Metro Decay – Υπέρβαση: 1766242
+# Dispossessed, The (2) – Sister Mary: 1965566
+
+# Get release data
+puts "Geting release data..."
+discogs = Discogs::Wrapper.new("musicfix")
+r = discogs.get_release(relid)
+mr = if r.master_id then discogs.get_master_release(r.master_id) end
+# Tracklist can contain dummy header tracks, strip them
+tl = r.tracklist.select {|t| t.position}
+
+# Sanity checks
+if tl.length != fl.length then
+ puts "Found #{tl.length} tracks for #{fl.length} music files."
+ exit if fl.length > tl.length
+ exit if fl.length == 0
+ puts "Use only the first #{fl.length} entries? [y/n] "
+ res = STDIN.readline.strip
+ exit unless res == 'y'
+end
+
+# Gather release-wide data
+ba = mkartist r.artists
+b = r.title
+y = r.released
+my = if mr then mr.year end || r.released
+# Year can be full-date so keep only the year part
+y = y.slice(0..3)
+my = my.slice(0..3)
+g = if r.styles then r.styles.first end ||
+ if r.genres then r.genres.first end
+fba = mkname ba
+fb = mkname b
+v = r.formats.first.name
+fv = mkname v
+
+# Loop over the music files and
+# 1. Copy them over with proper names
+# 2. Fix the tags on the new files
+tn = 0
+fl.each do |ofname|
+ tn = tn.next
+ s = tl[tn - 1]
+ al = (r.artists.first.name == "Various") ? s.artists : r.artists
+ a = mkartist al
+ d, n = mkdiscnum s.position
+ t = s.title
+ fa = mkname a
+ ft = mkname t
+ fn = mkname n
+ x = File.extname(ofname).delete('.').downcase
+ #nfname = "#{mdir}/#{fba}-#{my}-#{fb}-#{fv}/#{d}#{fn}-#{fa}-#{ft}.#{x}"
+ nfname = eval mytrack
+ puts "Copy track to #{nfname}"
+ FileUtils.makedirs(File.dirname nfname)
+ FileUtils.copy(ofname, nfname)
+ TagLib::FileRef.open(nfname) do |f|
+ f.tag.artist = a
+ f.tag.album = b
+ f.tag.title = t
+ f.tag.track = tn
+ f.tag.year = y.to_i
+ f.tag.genre = g
+ f.save
+ end
+end
+
+# Also save the first image of the artwork
+zz = '0' * (mkdiscnum tl.last.position).join.length
+#imgname = "#{mdir}/#{fba}-#{my}-#{fb}-#{fv}/#{zz}-#{fba}-#{fb}_cover.jpg"
+imgname = eval myimage
+imgurl = getimgurl r
+if imgurl then
+ puts "Save cover to #{imgname}"
+ img = open(imgurl, {"User-Agent" => "Mozilla"}).read
+ File.open(imgname, 'wb').write img
+end
+
+# vim:set ts=4 sw=4 et: