* morituri/result/logger.py:

Fix for toctable->table
	* morituri/common/program.py:
	* morituri/rip/cd.py:
	  Move lots of functionality to program module.
This commit is contained in:
Thomas Vander Stichele
2009-06-07 15:22:55 +00:00
parent ce1bc21a39
commit a460abc475
4 changed files with 257 additions and 238 deletions

View File

@@ -1,3 +1,11 @@
2009-06-07 Thomas Vander Stichele <thomas at apestaart dot org>
* morituri/result/logger.py:
Fix for toctable->table
* morituri/common/program.py:
* morituri/rip/cd.py:
Move lots of functionality to program module.
2009-06-07 Thomas Vander Stichele <thomas at apestaart dot org> 2009-06-07 Thomas Vander Stichele <thomas at apestaart dot org>
* morituri/result/result.py: * morituri/result/result.py:

View File

@@ -24,12 +24,151 @@
Common functionality and class for all programs using morituri. Common functionality and class for all programs using morituri.
""" """
from morituri.common import common
from morituri.result import result
from morituri.program import cdrdao
import os import os
from morituri.common import common, log
from morituri.result import result
from morituri.program import cdrdao, cdparanoia
import gst
class MusicBrainzException(Exception):
def __init__(self, exc):
self.args = (exc, )
self.exception = exc
class TrackMetadata(object):
artist = None
title = None
class DiscMetadata(object):
"""
@param release: earliest release date, in YYYY-MM-DD
@type release: unicode
"""
artist = None
title = None
various = False
tracks = None
release = None
def __init__(self):
self.tracks = []
def filterForPath(text):
return "-".join(text.split("/"))
def getMetadata(release):
metadata = DiscMetadata()
isSingleArtist = release.isSingleArtistRelease()
metadata.various = not isSingleArtist
metadata.title = release.title
# getUniqueName gets disambiguating names like Muse (UK rock band)
metadata.artist = release.artist.name
metadata.release = release.getEarliestReleaseDate()
for t in release.tracks:
track = TrackMetadata()
if isSingleArtist:
track.artist = metadata.artist
track.title = t.title
else:
track.artist = t.artist.name
track.title = t.title
metadata.tracks.append(track)
return metadata
def musicbrainz(discid):
#import musicbrainz2.disc as mbdisc
import musicbrainz2.webservice as mbws
# Setup a Query object.
service = mbws.WebService()
query = mbws.Query(service)
# Query for all discs matching the given DiscID.
# FIXME: let mbws.WebServiceError go through for now
try:
rfilter = mbws.ReleaseFilter(discId=discid)
results = query.getReleases(rfilter)
except mbws.WebServiceError, e:
raise MusicBrainzException(e)
# No disc matching this DiscID has been found.
if len(results) == 0:
return None
# Display the returned results to the user.
ret = []
for result in results:
release = result.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:
raise MusicBrainzException(e)
ret.append(getMetadata(release))
return ret
def getPath(outdir, template, metadata, mbdiscid, i):
"""
Based on the template, get a complete path for the given track,
minus extension.
Also works for the disc name, using disc variables for the template.
@param outdir: the directory where to write the files
@type outdir: str
@param template: the template for writing the file
@type template: str
@param metadata:
@type metadata: L{DiscMetadata}
@param i: track number (0 for HTOA)
@type i: int
"""
# returns without extension
v = {}
v['t'] = '%02d' % i
# default values
v['A'] = 'Unknown Artist'
v['d'] = mbdiscid
v['a'] = v['A']
v['n'] = 'Unknown Track %d' % i
if metadata:
v['A'] = filterForPath(metadata.artist)
v['d'] = filterForPath(metadata.title)
if i > 0:
try:
v['a'] = filterForPath(metadata.tracks[i - 1].artist)
v['n'] = filterForPath(metadata.tracks[i - 1].title)
except IndexError, e:
print 'ERROR: no track %d found, %r' % (i, e)
raise
else:
# htoa defaults to disc's artist
v['a'] = filterForPath(metadata.artist)
v['n'] = filterForPath('Hidden Track One Audio')
import re
template = re.sub(r'%(\w)', r'%(\1)s', template)
return os.path.join(outdir, template % v)
class Program(object): class Program(object):
""" """
@@ -79,6 +218,76 @@ class Program(object):
return itable return itable
def getTagList(self, metadata, i):
"""
Based on the metadata, get a gst.TagList for the given track.
@param metadata:
@type metadata: L{DiscMetadata}
@param i: track number (0 for HTOA)
@type i: int
@rtype: L{gst.TagList}
"""
artist = u'Unknown Artist'
disc = u'Unknown Disc'
title = u'Unknown Track'
if metadata:
artist = metadata.artist
disc = metadata.title
if i > 0:
try:
artist = metadata.tracks[i - 1].artist
title = metadata.tracks[i - 1].title
except IndexError, e:
print 'ERROR: no track %d found, %r' % (i, e)
raise
else:
# htoa defaults to disc's artist
title = 'Hidden Track One Audio'
ret = gst.TagList()
# gst-python 0.10.15.1 does not handle unicode -> utf8 string conversion
# see http://bugzilla.gnome.org/show_bug.cgi?id=584445
ret[gst.TAG_ARTIST] = artist.encode('utf-8')
ret[gst.TAG_TITLE] = title.encode('utf-8')
ret[gst.TAG_ALBUM] = disc.encode('utf-8')
# gst-python 0.10.15.1 does not handle tags that are UINT
# see gst-python commit 26fa6dd184a8d6d103eaddf5f12bd7e5144413fb
# FIXME: no way to compare against 'master' version after 0.10.15
if gst.pygst_version >= (0, 10, 15):
ret[gst.TAG_TRACK_NUMBER] = i
if metadata:
# works, but not sure we want this
# if gst.pygst_version >= (0, 10, 15):
# ret[gst.TAG_TRACK_COUNT] = len(metadata.tracks)
# hack to get a GstDate which we cannot instantiate directly in
# 0.10.15.1
# FIXME: The dates are strings and must have the format 'YYYY',
# 'YYYY-MM' or 'YYYY-MM-DD'.
# GstDate expects a full date, so default to Jan and 1st if MM and DD
# are missing
date = metadata.release
if date:
log.debug('metadata',
'Converting release date %r to structure', date)
if len(date) == 4:
date += '-01'
if len(date) == 7:
date += '-01'
s = gst.structure_from_string('hi,date=(GstDate)%s' %
str(date))
ret[gst.TAG_DATE] = s['date']
# FIXME: gst.TAG_ISRC
return ret
def writeCue(self, discName): def writeCue(self, discName):
assert self.result.table.canCue() assert self.result.table.canCue()
@@ -95,7 +304,7 @@ class Program(object):
def writeLog(self, discName, logger): def writeLog(self, discName, logger):
logPath = '%s.log' % discName logPath = '%s.log' % discName
handle = open(logPath, 'w') handle = open(logPath, 'w')
handle.write(logger.log(res).encode('utf-8')) handle.write(logger.log(self.result).encode('utf-8'))
handle.close() handle.close()
self.logPath = logPath self.logPath = logPath

View File

@@ -64,7 +64,7 @@ class MorituriLogger(object):
" Track | Start | Length") " Track | Start | Length")
lines.append( lines.append(
" ------------------------------------------------") " ------------------------------------------------")
table = ripResult.toctable table = ripResult.table
for t in table.tracks: for t in table.tracks:

View File

@@ -21,231 +21,17 @@
# along with morituri. If not, see <http://www.gnu.org/licenses/>. # along with morituri. If not, see <http://www.gnu.org/licenses/>.
import os import os
import sys
import math import math
import gobject import gobject
gobject.threads_init() gobject.threads_init()
import gst
from morituri.common import logcommand, task, checksum, common, accurip, log from morituri.common import logcommand, task, checksum, common, accurip, log
from morituri.common import drive, encode, program from morituri.common import drive, encode, program
from morituri.result import result from morituri.result import result
from morituri.image import image, cue, table from morituri.image import image, cue, table
from morituri.program import cdrdao, cdparanoia from morituri.program import cdrdao, cdparanoia
class TrackMetadata(object):
artist = None
title = None
class DiscMetadata(object):
"""
@param release: earliest release date, in YYYY-MM-DD
@type release: unicode
"""
artist = None
title = None
various = False
tracks = None
release = None
def __init__(self):
self.tracks = []
def filterForPath(text):
return "-".join(text.split("/"))
def musicbrainz(discid):
metadata = DiscMetadata()
#import musicbrainz2.disc as mbdisc
import musicbrainz2.webservice as mbws
# Setup a Query object.
service = mbws.WebService()
query = mbws.Query(service)
# Query for all discs matching the given DiscID.
try:
rfilter = mbws.ReleaseFilter(discId=discid)
results = query.getReleases(rfilter)
except mbws.WebServiceError, e:
print "Error:", e
return
# No disc matching this DiscID has been found.
if len(results) == 0:
print "Disc is not yet in the MusicBrainz database."
print "Consider adding it."
return
# 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.
selectedRelease = 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(selectedRelease.getId(), inc)
except mbws.WebServiceError, e:
print "Error:", e
sys.exit(2)
# convert to our objects
isSingleArtist = release.isSingleArtistRelease()
metadata.various = not isSingleArtist
metadata.title = release.title
# getUniqueName gets disambiguating names like Muse (UK rock band)
metadata.artist = release.artist.name
metadata.release = release.getEarliestReleaseDate()
print "%s - %s" % (release.artist.name, release.title)
for t in release.tracks:
track = TrackMetadata()
if isSingleArtist:
track.artist = metadata.artist
track.title = t.title
else:
track.artist = t.artist.name
track.title = t.title
metadata.tracks.append(track)
return metadata
def getPath(outdir, template, metadata, mbdiscid, i):
"""
Based on the template, get a complete path for the given track,
minus extension.
Also works for the disc name, using disc variables for the template.
@param outdir: the directory where to write the files
@type outdir: str
@param template: the template for writing the file
@type template: str
@param metadata:
@type metadata: L{DiscMetadata}
@param i: track number (0 for HTOA)
@type i: int
"""
# returns without extension
v = {}
v['t'] = '%02d' % i
# default values
v['A'] = 'Unknown Artist'
v['d'] = mbdiscid
v['a'] = v['A']
v['n'] = 'Unknown Track %d' % i
if metadata:
v['A'] = filterForPath(metadata.artist)
v['d'] = filterForPath(metadata.title)
if i > 0:
try:
v['a'] = filterForPath(metadata.tracks[i - 1].artist)
v['n'] = filterForPath(metadata.tracks[i - 1].title)
except IndexError, e:
print 'ERROR: no track %d found, %r' % (i, e)
raise
else:
# htoa defaults to disc's artist
v['a'] = filterForPath(metadata.artist)
v['n'] = filterForPath('Hidden Track One Audio')
import re
template = re.sub(r'%(\w)', r'%(\1)s', template)
return os.path.join(outdir, template % v)
def getTagList(metadata, i):
"""
Based on the metadata, get a gst.TagList for the given track.
@param metadata:
@type metadata: L{DiscMetadata}
@param i: track number (0 for HTOA)
@type i: int
@rtype: L{gst.TagList}
"""
artist = u'Unknown Artist'
disc = u'Unknown Disc'
title = u'Unknown Track'
if metadata:
artist = metadata.artist
disc = metadata.title
if i > 0:
try:
artist = metadata.tracks[i - 1].artist
title = metadata.tracks[i - 1].title
except IndexError, e:
print 'ERROR: no track %d found, %r' % (i, e)
raise
else:
# htoa defaults to disc's artist
title = 'Hidden Track One Audio'
ret = gst.TagList()
# gst-python 0.10.15.1 does not handle unicode -> utf8 string conversion
# see http://bugzilla.gnome.org/show_bug.cgi?id=584445
ret[gst.TAG_ARTIST] = artist.encode('utf-8')
ret[gst.TAG_TITLE] = title.encode('utf-8')
ret[gst.TAG_ALBUM] = disc.encode('utf-8')
# gst-python 0.10.15.1 does not handle tags that are UINT
# see gst-python commit 26fa6dd184a8d6d103eaddf5f12bd7e5144413fb
# FIXME: no way to compare against 'master' version after 0.10.15
if gst.pygst_version >= (0, 10, 15):
ret[gst.TAG_TRACK_NUMBER] = i
if metadata:
# works, but not sure we want this
# if gst.pygst_version >= (0, 10, 15):
# ret[gst.TAG_TRACK_COUNT] = len(metadata.tracks)
# hack to get a GstDate which we cannot instantiate directly in
# 0.10.15.1
# FIXME: The dates are strings and must have the format 'YYYY',
# 'YYYY-MM' or 'YYYY-MM-DD'.
# GstDate expects a full date, so default to Jan and 1st if MM and DD
# are missing
date = metadata.release
if date:
log.debug('metadata',
'Converting release date %r to structure', date)
if len(date) == 4:
date += '-01'
if len(date) == 7:
date += '-01'
s = gst.structure_from_string('hi,date=(GstDate)%s' %
str(date))
ret[gst.TAG_DATE] = s['date']
# FIXME: gst.TAG_ISRC
return ret
class Rip(logcommand.LogCommand): class Rip(logcommand.LogCommand):
summary = "rip CD" summary = "rip CD"
@@ -311,10 +97,27 @@ class Rip(logcommand.LogCommand):
mbdiscid = ittoc.getMusicBrainzDiscId() mbdiscid = ittoc.getMusicBrainzDiscId()
print "MusicBrainz disc id", mbdiscid print "MusicBrainz disc id", mbdiscid
metadata = musicbrainz(mbdiscid) # look up disc on musicbrainz
if not metadata: try:
metadatas = program.musicbrainz(mbdiscid)
except program.MusicBrainzException, e:
print "Error:", e
return
metadata = None
if metadatas:
print 'Matching releases:'
for metadata in metadatas:
print 'Artist :', metadata.artist
print 'Title :', metadata.title
# Select one of the returned releases. We just pick the first one.
metadata = metadatas[0]
else:
print 'Submit this disc to MusicBrainz at:' print 'Submit this disc to MusicBrainz at:'
print ittoc.getMusicBrainzSubmitURL() print ittoc.getMusicBrainzSubmitURL()
print
# now, read the complete index table, which is slower # now, read the complete index table, which is slower
itable = prog.getTable(runner, ittoc.getCDDBDiscId(), device) itable = prog.getTable(runner, ittoc.getCDDBDiscId(), device)
@@ -334,18 +137,17 @@ class Rip(logcommand.LogCommand):
extension = profile.extension extension = profile.extension
# result # result
res = prog.result prog.result.offset = int(self.options.offset)
res.offset = int(self.options.offset) prog.result.artist = metadata and metadata.artist or 'Unknown Artist'
res.artist = metadata and metadata.artist or 'Unknown Artist' prog.result.title = metadata and metadata.title or 'Unknown Title'
res.title = metadata and metadata.title or 'Unknown Title'
# cdio is optional for now # cdio is optional for now
try: try:
import cdio import cdio
_, res.vendor, res.model, __ = cdio.Device(device).get_hwinfo() _, prog.result.vendor, prog.result.model, __ = cdio.Device(device).get_hwinfo()
except ImportError: except ImportError:
print 'WARNING: pycdio not installed, cannot identify drive' print 'WARNING: pycdio not installed, cannot identify drive'
res.vendor = 'Unknown' prog.result.vendor = 'Unknown'
res.model = 'Unknown' prog.result.model = 'Unknown'
# check for hidden track one audio # check for hidden track one audio
@@ -364,8 +166,8 @@ class Rip(logcommand.LogCommand):
start, stop) start, stop)
# rip it # rip it
htoapath = getPath(outdir, self.options.track_template, metadata, htoapath = program.getPath(outdir, self.options.track_template,
mbdiscid, 0) + '.' + extension metadata, mbdiscid, 0) + '.' + extension
dirname = os.path.dirname(htoapath) dirname = os.path.dirname(htoapath)
if not os.path.exists(dirname): if not os.path.exists(dirname):
os.makedirs(dirname) os.makedirs(dirname)
@@ -378,7 +180,7 @@ class Rip(logcommand.LogCommand):
offset=int(self.options.offset), offset=int(self.options.offset),
device=self.parentCommand.options.device, device=self.parentCommand.options.device,
profile=profile, profile=profile,
taglist=getTagList(metadata, 0)) taglist=prog.getTagList(metadata, 0))
function(runner, t) function(runner, t)
if t.checksum is not None: if t.checksum is not None:
@@ -402,9 +204,9 @@ class Rip(logcommand.LogCommand):
continue continue
trackResult = result.TrackResult() trackResult = result.TrackResult()
res.tracks.append(trackResult) prog.result.tracks.append(trackResult)
path = getPath(outdir, self.options.track_template, metadata, path = program.getPath(outdir, self.options.track_template, metadata,
mbdiscid, i + 1) + '.' + extension mbdiscid, i + 1) + '.' + extension
trackResult.number = i + 1 trackResult.number = i + 1
trackResult.filename = path trackResult.filename = path
@@ -423,7 +225,7 @@ class Rip(logcommand.LogCommand):
offset=int(self.options.offset), offset=int(self.options.offset),
device=self.parentCommand.options.device, device=self.parentCommand.options.device,
profile=profile, profile=profile,
taglist=getTagList(metadata, i + 1)) taglist=prog.getTagList(metadata, i + 1))
t.description = 'Reading Track %d' % (i + 1) t.description = 'Reading Track %d' % (i + 1)
function(runner, t) function(runner, t)
if t.checksum: if t.checksum:
@@ -445,7 +247,7 @@ class Rip(logcommand.LogCommand):
### write disc files ### write disc files
discName = getPath(outdir, self.options.disc_template, metadata, discName = program.getPath(outdir, self.options.disc_template, metadata,
mbdiscid, 0) mbdiscid, 0)
dirname = os.path.dirname(discName) dirname = os.path.dirname(discName)
if not os.path.exists(dirname): if not os.path.exists(dirname):
@@ -467,7 +269,7 @@ class Rip(logcommand.LogCommand):
if not track.audio: if not track.audio:
continue continue
path = getPath(outdir, self.options.track_template, metadata, path = program.getPath(outdir, self.options.track_template, metadata,
mbdiscid, i + 1) + '.' + extension mbdiscid, i + 1) + '.' + extension
u = u'#EXTINF:%d,%s\n' % ( u = u'#EXTINF:%d,%s\n' % (
itable.getTrackLength(i + 1) / common.FRAMES_PER_SECOND, itable.getTrackLength(i + 1) / common.FRAMES_PER_SECOND,
@@ -507,7 +309,7 @@ class Rip(logcommand.LogCommand):
# loop over tracks # loop over tracks
for i, csum in enumerate(cuetask.checksums): for i, csum in enumerate(cuetask.checksums):
trackResult = res.tracks[i] trackResult = prog.result.tracks[i]
trackResult.accuripCRC = csum trackResult.accuripCRC = csum
status = 'rip NOT accurate' status = 'rip NOT accurate'