From 6ab37720df55f9adee2348f23d012e797deee9b3 Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Wed, 19 Oct 2011 18:00:11 +0000 Subject: [PATCH] * 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. --- ChangeLog | 10 ++ morituri/common/Makefile.am | 1 + morituri/common/musicbrainz.py | 187 +++++++++++++++++++++++ morituri/common/program.py | 162 +------------------- morituri/test/Makefile.am | 1 + morituri/test/test_common_musicbrainz.py | 54 +++++++ 6 files changed, 257 insertions(+), 158 deletions(-) create mode 100644 morituri/common/musicbrainz.py create mode 100644 morituri/test/test_common_musicbrainz.py diff --git a/ChangeLog b/ChangeLog index bb7cc7a..221aeb5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2011-10-19 Thomas Vander Stichele + + * 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 * TODO: diff --git a/morituri/common/Makefile.am b/morituri/common/Makefile.am index ad29478..5e9b978 100644 --- a/morituri/common/Makefile.am +++ b/morituri/common/Makefile.am @@ -12,5 +12,6 @@ morituri_PYTHON = \ gstreamer.py \ log.py \ logcommand.py \ + musicbrainz.py \ program.py \ renamer.py diff --git a/morituri/common/musicbrainz.py b/morituri/common/musicbrainz.py new file mode 100644 index 0000000..e16ad58 --- /dev/null +++ b/morituri/common/musicbrainz.py @@ -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 . + +""" +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 diff --git a/morituri/common/program.py b/morituri/common/program.py index ed2b6f5..fb05cf7 100644 --- a/morituri/common/program.py +++ b/morituri/common/program.py @@ -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 diff --git a/morituri/test/Makefile.am b/morituri/test/Makefile.am index 2acd495..4b3e0bb 100644 --- a/morituri/test/Makefile.am +++ b/morituri/test/Makefile.am @@ -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 \ diff --git a/morituri/test/test_common_musicbrainz.py b/morituri/test/test_common_musicbrainz.py new file mode 100644 index 0000000..73a0735 --- /dev/null +++ b/morituri/test/test_common_musicbrainz.py @@ -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)