* morituri/common/Makefile.am:

* morituri/common/musicbrainz.py:
	* morituri/common/program.py:
	* morituri/test/Makefile.am:
	* morituri/test/test_common_musicbrainz.py (added):
	* morituri/common/musicbrainz.py (added):
	  Split off musicbrainz-specific code.
This commit is contained in:
Thomas Vander Stichele
2011-10-19 18:00:11 +00:00
parent ad71fa06db
commit 6ab37720df
6 changed files with 257 additions and 158 deletions

View File

@@ -1,3 +1,13 @@
2011-10-19 Thomas Vander Stichele <thomas at apestaart dot org>
* morituri/common/Makefile.am:
* morituri/common/musicbrainz.py:
* morituri/common/program.py:
* morituri/test/Makefile.am:
* morituri/test/test_common_musicbrainz.py (added):
* morituri/common/musicbrainz.py (added):
Split off musicbrainz-specific code.
2011-10-19 Thomas Vander Stichele <thomas at apestaart dot org>
* TODO:

View File

@@ -12,5 +12,6 @@ morituri_PYTHON = \
gstreamer.py \
log.py \
logcommand.py \
musicbrainz.py \
program.py \
renamer.py

View File

@@ -0,0 +1,187 @@
# -*- Mode: Python; test-case-name: morituri.test.test_common_musicbrainz -*-
# vi:si:et:sw=4:sts=4:ts=4
# Morituri - for those about to RIP
# Copyright (C) 2009, 2010, 2011 Thomas Vander Stichele
# This file is part of morituri.
#
# morituri is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# morituri is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with morituri. If not, see <http://www.gnu.org/licenses/>.
"""
Handles communication with the musicbrainz server.
"""
import urlparse
from morituri.common import log
class MusicBrainzException(Exception):
def __init__(self, exc):
self.args = (exc, )
self.exception = exc
class TrackMetadata(object):
artist = None
title = None
duration = None # in ms
mbid = None
sortName = None
mbidArtist = None
class DiscMetadata(object):
"""
@param release: earliest release date, in YYYY-MM-DD
@type release: unicode
"""
artist = None
sortName = None
title = None
various = False
tracks = None
release = None
mbid = None
mbidArtist = None
def __init__(self):
self.tracks = []
def _getMetadata(release):
"""
@type release: L{musicbrainz2.model.Release}
@rtype: L{DiscMetadata} or None
"""
log.debug('program', 'getMetadata for release id %r',
release.getId())
if not release.getId():
log.warning('program', 'No id for release %r', release)
return None
assert release.id, 'Release does not have an id'
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.sortName = release.artist.sortName
metadata.release = release.getEarliestReleaseDate()
metadata.mbid = urlparse.urlparse(release.id)[2].split("/")[-1]
metadata.mbidArtist = urlparse.urlparse(release.artist.id)[2].split("/")[-1]
metadata.url = release.getId()
tainted = False
duration = 0
for t in release.tracks:
track = TrackMetadata()
if isSingleArtist or t.artist == None:
track.artist = metadata.artist
track.sortName = metadata.sortName
track.mbidArtist = metadata.mbidArtist
else:
# various artists discs can have tracks with no artist
track.artist = t.artist and t.artist.name or release.artist.name
track.sortName = t.artist.sortName
track.mbidArtist = urlparse.urlparse(t.artist.id)[2].split("/")[-1]
track.title = t.title
track.mbid = urlparse.urlparse(t.id)[2].split("/")[-1]
track.duration = t.duration
if not track.duration:
log.warning('getMetadata',
'track %r (%r) does not have duration' % (
track.title, track.mbid))
tainted = True
else:
duration += t.duration
metadata.tracks.append(track)
if not tainted:
metadata.duration = duration
else:
metadata.duration = 0
return metadata
# see http://bugs.musicbrainz.org/browser/python-musicbrainz2/trunk/examples/ripper.py
def musicbrainz(discid):
"""
@rtype: list of L{DiscMetadata}
"""
log.debug('musicbrainz', 'looking up results for discid %r', discid)
#import musicbrainz2.disc as mbdisc
import musicbrainz2.webservice as mbws
results = []
# 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
log.debug('musicbrainz', 'found %d results for discid %r', len(results),
discid)
# Display the returned results to the user.
ret = []
for result in results:
release = result.release
log.debug('program', 'result %r: artist %r, title %r' % (
release, release.artist.getName(), release.title))
# 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, discs=True)
# Arid - Under the Cold Street Lights has getId() None
if release.getId():
release = query.getReleaseById(release.getId(), inc)
except mbws.WebServiceError, e:
raise MusicBrainzException(e)
md = _getMetadata(release)
if md:
log.debug('program', 'duration %r', md.duration)
ret.append(md)
return ret

View File

@@ -25,177 +25,23 @@ Common functionality and class for all programs using morituri.
"""
import os
import urlparse
import time
from morituri.common import common, log
from morituri.common import common, log, musicbrainz
from morituri.result import result
from morituri.program import cdrdao, cdparanoia
from morituri.image import image
class MusicBrainzException(Exception):
def __init__(self, exc):
self.args = (exc, )
self.exception = exc
class TrackMetadata(object):
artist = None
title = None
duration = None # in ms
mbid = None
sortName = None
mbidArtist = None
class DiscMetadata(object):
"""
@param release: earliest release date, in YYYY-MM-DD
@type release: unicode
"""
artist = None
sortName = None
title = None
various = False
tracks = None
release = None
mbid = None
mbidArtist = None
def __init__(self):
self.tracks = []
def filterForPath(text):
return "-".join(text.split("/"))
def getMetadata(release):
"""
@type release: L{musicbrainz2.model.Release}
@rtype: L{DiscMetadata} or None
"""
log.debug('program', 'getMetadata for release id %r',
release.getId())
if not release.getId():
log.warning('program', 'No id for release %r', release)
return None
assert release.id, 'Release does not have an id'
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.sortName = release.artist.sortName
metadata.release = release.getEarliestReleaseDate()
metadata.mbid = urlparse.urlparse(release.id)[2].split("/")[-1]
metadata.mbidArtist = urlparse.urlparse(release.artist.id)[2].split("/")[-1]
metadata.url = release.getId()
tainted = False
duration = 0
for t in release.tracks:
track = TrackMetadata()
if isSingleArtist or t.artist == None:
track.artist = metadata.artist
track.sortName = metadata.sortName
track.mbidArtist = metadata.mbidArtist
else:
# various artists discs can have tracks with no artist
track.artist = t.artist and t.artist.name or release.artist.name
track.sortName = t.artist.sortName
track.mbidArtist = urlparse.urlparse(t.artist.id)[2].split("/")[-1]
track.title = t.title
track.mbid = urlparse.urlparse(t.id)[2].split("/")[-1]
track.duration = t.duration
if not track.duration:
log.warning('getMetadata',
'track %r (%r) does not have duration' % (
track.title, track.mbid))
tainted = True
else:
duration += t.duration
metadata.tracks.append(track)
if not tainted:
metadata.duration = duration
else:
metadata.duration = 0
return metadata
# see http://bugs.musicbrainz.org/browser/python-musicbrainz2/trunk/examples/ripper.py
def musicbrainz(discid):
"""
@rtype: list of L{DiscMetadata}
"""
log.debug('musicbrainz', 'looking up results for discid %r', discid)
#import musicbrainz2.disc as mbdisc
import musicbrainz2.webservice as mbws
results = []
# 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
log.debug('musicbrainz', 'found %d results for discid %r', len(results),
discid)
# Display the returned results to the user.
ret = []
for result in results:
release = result.release
log.debug('program', 'result %r: artist %r, title %r' % (
release, release.artist.getName(), release.title))
# 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, discs=True)
# Arid - Under the Cold Street Lights has getId() None
if release.getId():
release = query.getReleaseById(release.getId(), inc)
except mbws.WebServiceError, e:
raise MusicBrainzException(e)
md = getMetadata(release)
if md:
log.debug('program', 'duration %r', md.duration)
ret.append(md)
return ret
class Program(log.Loggable):
"""
I maintain program state and functionality.
@ivar metadata:
@type metadata: L{DiscMetadata}
@type metadata: L{musicbrainz.DiscMetadata}
@ivar result: the rip's result
@type result: L{result.RipResult}
@type outdir: unicode
@@ -378,8 +224,8 @@ class Program(log.Loggable):
for _ in range(0, 4):
try:
metadatas = musicbrainz(mbdiscid)
except MusicBrainzException, e:
metadatas = musicbrainz.musicbrainz(mbdiscid)
except musicbrainz.MusicBrainzException, e:
print "Warning:", e
time.sleep(5)
continue

View File

@@ -5,6 +5,7 @@ EXTRA_DIST = \
common.py \
test_common_accurip.py \
test_common_checksum.py \
test_common_musicbrainz.py \
test_common_program.py \
test_common_renamer.py \
test_image_cue.py \

View File

@@ -0,0 +1,54 @@
# -*- Mode: Python; test-case-name: morituri.test.test_common_musicbrainz -*-
# vi:si:et:sw=4:sts=4:ts=4
import os
import unittest
from morituri.common import musicbrainz
class MetadataLengthTestCase(unittest.TestCase):
def testLamprey(self):
from musicbrainz2 import wsxml
path = os.path.join(os.path.dirname(__file__),
'release.c7d919f4-3ea0-4c4b-a230-b3605f069440.xml')
handle = open(path, "rb")
reader = wsxml.MbXmlParser()
wsMetadata = reader.parse(handle)
release = wsMetadata.getRelease()
metadata = musicbrainz._getMetadata(release)
self.assertEquals(metadata.duration, 2962889)
def testLadyhawke(self):
from musicbrainz2 import wsxml
path = os.path.join(os.path.dirname(__file__),
'release.93a6268c-ddf1-4898-bf93-fb862b1c5c5e.xml')
handle = open(path, "rb")
reader = wsxml.MbXmlParser()
wsMetadata = reader.parse(handle)
release = wsMetadata.getRelease()
metadata = musicbrainz._getMetadata(release)
self.failUnless(metadata)
# self.assertEquals(metadata.duration, 2609413)
def testDasCapital(self):
from musicbrainz2 import wsxml
path = os.path.join(os.path.dirname(__file__),
'release.08397059-86c1-463b-8ed0-cd596dbd174f.xml')
handle = open(path, "rb")
reader = wsxml.MbXmlParser()
wsMetadata = reader.parse(handle)
release = wsMetadata.getRelease()
metadata = musicbrainz._getMetadata(release)
# FIXME: 2 seconds longer than the duration according to table
self.assertEquals(metadata.duration, 2315730)