#! /usr/bin/env python
#
# This shows the web service interaction needed for a typical CD ripper.
#
# Usage:
#	python ripper.py
#
# $Id: ripper.py 201 2006-03-27 14:43:13Z matt $
#
import sys, os, logging
import musicbrainz2.disc as mbdisc
import musicbrainz2.webservice as mbws
from musicbrainz2.utils import extractUuid as uuid
from optparse import OptionParser
from subprocess import Popen,PIPE
from time import sleep

##
## The script "oggit", which needs to be installed on the remote host,
## ensures that aruments are not mangled by the shell -- ssh does not
## pass its command arguments in a sane manner. :-(
## It looks like this:
#
# #!/bin/sh
# 
# read a
# test -d "$a" || exit 1
# cd "$a"
# exec xargs -0x oggenc
#

# Debian special. Invent your own.
if "DISPLAY" in os.environ:
	browser = "x-www-browser"
else:
	browser = "www-browser"

parser = OptionParser(conflict_handler="resolve",usage="Usage: %prog [args] [path [dpath]]")
parser.add_option("-h","--help","-?", action="help",
                    help="print this help text")
parser.add_option("--cdid", dest="cdcd", action="store_true",
                    help="also read the CDCD id (needs non-standard cdcd)")
parser.add_option("-d", "--dev", dest="dev", action="store",
                    help="CD-Device (Default: %default)")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
                    help="output debug messages")
parser.add_option("-r", "--remote",
					dest="remote", action="store",
                    help="run 'oggit' on a remote host")
parser.add_option("-q", "--qual","--quality",
					dest="qual", action="store", type="float",
                    help="Quality (Default %default, 0-10, more => better")
parser.add_option("-u", "--url", dest="url", action="store_true",
                    help="only open the CD's Musicbrainz URL")
parser.add_option("-n", "--no-rip", dest="no_rip", action="store_true",
                    help="no ripping: encode / write status file only")
parser.add_option("-i", "--ignore", dest="ignore", action="store_true",
                    help="ignore existing encoded files")
parser.add_option("-l", "--local", dest="local", action="store",
                    help="don't do Internet lookups; arg=disc_name")

parser.set_defaults(qual=5.0, dev="/dev/cdrom")

(opts, args) = parser.parse_args()
if len(args) > 2:
	parser.print_usage(file=os.stderr)
	sys.exit(1)
elif args:
	path=args[0]
	if len(args) > 1:
		dpath=args[1]
	else:
		dpath=path
else:
	path=None
	dpath=None

# Activate logging.
#
logging.basicConfig()
logger = logging.getLogger()
if opts.verbose:
	logger.setLevel(logging.DEBUG)
else:
	logger.setLevel(logging.WARNING)


# Setup a Query object.
#
service = mbws.WebService()
query = mbws.Query(service)

Popen(["eject","-t",opts.dev]).wait()

# Read the disc in the drive
#
try:
	disc = mbdisc.readDisc(deviceName=opts.dev)
except mbdisc.DiscError, e:
	print "Error:", e
	sys.exit(1)


# Query for all discs matching the given DiscID.
#
cdid = None
if opts.cdcd:
	cdid=Popen(["cdcd","-d",opts.dev,"id"], stdout=PIPE).stdout.read().rstrip()

if not opts.url and not opts.local:
	try:
		filter = mbws.ReleaseFilter(discId=disc.getId())
		results = query.getReleases(filter)
	except mbws.WebServiceError, e:
		print "Error:", e
		sys.exit(2)

# No disc matching this DiscID has been found.
#
	if len(results) == 0:
		print "Disc is not yet in the MusicBrainz database."

if opts.cdcd:
	print "The cd ID is",cdid
print "The mb ID is",disc.getId()

if not opts.local and (opts.url or not results):
	b = Popen([browser,mbdisc.getSubmissionUrl(disc)])
	Popen(["eject",opts.dev])
	if "DISPLAY" not in os.environ:
		b.wait()
	sys.exit(2)


if not opts.local:
# Display the returned results to the user.
#
	print 'Matching releases:'

	for result in results:
		release = result.release
		print 'Artist  :', release.artist.name
		print 'Title   :', release.title
		print


	# Select one of the returned releases. We just pick the first one.
	#
	if len(results) > 1:
		print "Welchen? ",
		import readline
		r=sys.stdin.readline()
		try:
			release = results[int(r.rstrip())].release
		except ValueError:
			os.execlp(browser, browser,mbdisc.getSubmissionUrl(disc))
	
	else:
		release = results[0].release


	# The returned release object only contains title and artist, but no tracks.
	# Query the web service once again to get all data we need.
	#
	try:
		inc = mbws.ReleaseIncludes(artist=True, tracks=True, releaseEvents=True)
		release = query.getReleaseById(release.getId(), inc)
	except mbws.WebServiceError, e:
		print "Error:", e
		sys.exit(2)


	# Now display the returned data.
	#
	isSingleArtist = release.isSingleArtistRelease()

	print "%s - %s" % (release.artist.getUniqueName(), release.title)

# All data has been retrieved, now actually rip the CD :-)
#
if path is None:
	path = "/daten/Sound/unsortiert"

if opts.local:
	ran = opts.local
	tit = ""
	path += "/%s" % (ran,)
else:
	ran = release.artist.name.replace("/","-")
	tit = release.title.replace("/","-")
	path += "/%s/%s" % (ran,tit)
if dpath is None:
	dpath = path
else:
	if opts.local:
		dpath += "/%s" % (ran,)
	else:
		dpath += "/%s/%s" % (ran,tit)

if opts.no_rip:
	Popen(["eject",opts.dev]).wait()

if path and not os.path.exists(path):
	os.makedirs(path)
if dpath != path and not os.path.exists(dpath):
	os.makedirs(dpath)
os.chdir(path)

cddb = os.path.join(dpath,".cddb")
f=open(cddb,"w")
print >>f, """\
cddb %s
cdid %s
qual %f""" % (cdid or "?", disc.getId(), opts.qual)
if not opts.local:
	print >>f, """\
uuid %s""" % (uuid(release.getId()),)
f.close()

if opts.local:
	tr = range(1,disc.lastTrackNum+1)
else:
	tr = release.tracks
i = 1
for t in tr:
	if not opts.local:
		if isSingleArtist:
			title = t.title
		else:
			title = t.artist.name + ' - ' +  t.title
	
		(minutes, seconds) = t.getDurationSplit()
		print " %2d. %s (%d:%02d)" % (i, title, minutes, seconds)
	if not opts.no_rip:
		try:
			os.remove("track%02d.cdda.wav" % (i,))
		except OSError:
			pass
	i+=1

if not opts.no_rip:
	rip = Popen(["cdparanoia","-q","-d",opts.dev,"-B"])
else:
	rip = None

i = 0
for t in tr:
	i += 1
	filename = "%02d" % (i,)
	if not opts.local:
		filename += " "
		if isSingleArtist:
			filename += t.title
		else:
			filename += t.artist.name + ': ' +  t.title
	filename = filename.replace("/","-")
	filename += ".ogg"

	if rip and i == len(tr):
		rip.wait()
		rip = None
		Popen(["eject",opts.dev]).wait()

	if os.path.exists(filename) and not opts.ignore:
		continue
	
	if rip: # always =None at the end of the disc -- see above
		while not os.path.exists("track%02d.cdda.wav" % (i+1,)):
			sleep(1)

	srcf = "track%02d.cdda.wav" % (i,);
	if not os.path.exists(srcf):
		continue

	if opts.remote:
		args=[]
		enc=Popen(["ssh","-T","smurf","oggit"],stdin=PIPE)
		print >>enc.stdin,dpath

		if(path != dpath):
			Popen(["mv", srcf, os.path.join(dpath,srcf)]).wait()
	else:
		args=["oggenc"]
		if(path != dpath):
			filename = os.path.join(dpath,filename)

	args.extend(["-q",str(opts.qual), "-o",filename])
	if not opts.local:
		args.extend(["-c","TITLE="+t.title])
		if isSingleArtist:
			args.extend(["-c","ARTIST="+release.artist.name])
		else:
			args.extend(["-c","ARTIST="+t.artist.name])
	args.extend(["-c","TRACKNUMBER="+str(i)])
	args.extend(["-c","TRACKTOTAL="+str(len(tr))])
	if not opts.local:
		args.extend(["-c","ALBUM="+release.title])
		args.extend(["-c","MUSICBRAINZ_SORTNAME="+release.artist.sortName])
		args.extend(["-c","MUSICBRAINZ_ALBUMID="+uuid(release.getId())])
		args.extend(["-c","MUSICBRAINZ_ALBUMARTISTID="+uuid(release.artist.getId())])
		if not isSingleArtist:
			args.extend(["-c","MUSICBRAINZ_ARTISTID="+uuid(t.artist.getId())])
		args.extend(["-c","MUSICBRAINZ_TRACKID="+uuid(t.getId())])
	args.append("track%02d.cdda.wav" % (i,))

	if opts.remote:
		print >>enc.stdin,"\0".join(args).encode("utf-8"),
		enc.stdin.close()
	else:
		enc=Popen(args)

	while enc.poll() is None:
		if rip and rip.poll() is not None:
			rip.wait()
			rip = None
			Popen(["eject",opts.dev]).wait()
		else:
			sleep(1)
	enc.wait()
	if path != dpath and opts.remote:
		os.remove(os.path.join(dpath,srcf))
	else:
		os.remove(srcf)
	
if not opts.no_rip:
	try: os.remove("track00.cdda.wav")
	except OSError: pass

if opts.local:
	print "done:",(cdid or "?"),uuid(release.getId())

# EOF
