Merge pull request #432 from ABCbum/develop

Allow whipper's mblookup command to look up information based on Release MBID
This commit is contained in:
JoeLametta
2019-12-19 10:01:10 +01:00
committed by GitHub
3 changed files with 116 additions and 43 deletions

View File

@@ -1,5 +1,7 @@
from whipper.command.basecommand import BaseCommand
from whipper.common.mbngs import musicbrainz
from whipper.common.mbngs import musicbrainz, getReleaseMetadata
import re
class MBLookup(BaseCommand):
@@ -12,35 +14,58 @@ Example disc id: KnpGsLhvH.lPrNc1PBL21lb9Bg4-"""
def add_arguments(self):
self.parser.add_argument(
'mbdiscid', action='store', help="MB disc id to look up"
'mbid', action='store', help="MB disc id or release id to look up"
)
def _printMetadata(self, md):
"""
Print out metadata received in a sensible way.
:param md: MusicBrainz's metadata about the disc
:type md: `DiscMetadata`
"""
print(' Artist: %s' % md.artist.encode('utf-8'))
print(' Title: %s' % md.title.encode('utf-8'))
print(' Type: %s' % str(md.releaseType).encode('utf-8'))
print(' URL: %s' % md.url)
print(' Tracks: %d' % len(md.tracks))
if md.catalogNumber:
print(' Cat no: %s' % md.catalogNumber)
if md.barcode:
print(' Barcode: %s' % md.barcode)
for j, track in enumerate(md.tracks):
print(' Track %2d: %s - %s' % (
j + 1, track.artist.encode('utf-8'),
track.title.encode('utf-8')
))
def do(self):
try:
discId = str(self.options.mbdiscid)
mbid = str(self.options.mbid.strip())
except IndexError:
print('Please specify a MusicBrainz disc id.')
print('Please specify a MusicBrainz disc id or release id.')
return 3
metadatas = musicbrainz(discId)
releaseIdMatch = re.match(
r'^[\dA-Fa-f]{8}-(?:[\dA-Fa-f]{4}-){3}[\dA-Fa-f]{12}$',
mbid
)
discIdMatch = re.match(
r'^[\dA-Za-z._]{27}-$',
mbid
)
print('%d releases' % len(metadatas))
for i, md in enumerate(metadatas):
print('- Release %d:' % (i + 1, ))
print(' Artist: %s' % md.artist.encode('utf-8'))
print(' Title: %s' % md.title.encode('utf-8'))
print(' Type: %s' % str(md.releaseType).encode('utf-8')) # noqa: E501
print(' URL: %s' % md.url)
print(' Tracks: %d' % len(md.tracks))
if md.catalogNumber:
print(' Cat no: %s' % md.catalogNumber)
if md.barcode:
print(' Barcode: %s' % md.barcode)
for j, track in enumerate(md.tracks):
print(' Track %2d: %s - %s' % (
j + 1, track.artist.encode('utf-8'),
track.title.encode('utf-8')
))
# see https://musicbrainz.org/doc/MusicBrainz_Identifier
if releaseIdMatch:
md = getReleaseMetadata(releaseIdMatch.group(0))
if md:
self._printMetadata(md)
elif discIdMatch:
metadatas = musicbrainz(discIdMatch.group(0))
print('%d releases' % len(metadatas))
for i, md in enumerate(metadatas):
print('- Release %d:' % (i + 1, ))
self._printMetadata(md)
return None

View File

@@ -24,9 +24,13 @@ Handles communication with the MusicBrainz server using NGS.
from urllib.error import HTTPError
import whipper
import json
import musicbrainzngs
import logging
logger = logging.getLogger(__name__)
musicbrainzngs.set_useragent("whipper", whipper.__version__,
"https://github.com/whipper-team/whipper")
VA_ID = "89ad4ac3-39f7-470e-963a-56509c546377" # Various Artists
@@ -161,7 +165,7 @@ def _getWorks(recording):
return works
def _getMetadata(release, discid, country=None):
def _getMetadata(release, discid=None, country=None):
"""
:type release: dict
:param release: a release dict as returned in the value for key release
@@ -220,7 +224,7 @@ def _getMetadata(release, discid, country=None):
# only show discs from medium-list->disc-list with matching discid
for medium in release['medium-list']:
for disc in medium['disc-list']:
if disc['id'] == discid:
if discid is None or disc['id'] == discid:
title = release['title']
discMD.releaseTitle = title
if 'disambiguation' in release:
@@ -271,6 +275,40 @@ def _getMetadata(release, discid, country=None):
return discMD
def getReleaseMetadata(release_id, discid=None, country=None, record=False):
"""
Return a DiscMetadata object based on MusicBrainz Release ID and Disc ID.
If the disc id is not specified, it will match with any disc that is on
the release disc-list. Otherwise only returns metadata of one disc in
release disc-list.
:param release_id: MusicBrainz Release ID
:type release_id: str
:param discid: MusicBrainz Disc ID
:type discid: str or None
:param country: the country the release was issued in
:type country: str or None
:param record: whether to record to disc as a JSON serialization
:type record: bool
:returns: a DiscMetadata object based on MusicBrainz Release ID & Disc ID
:rtype: `DiscMetadata`
"""
# to get titles of recordings, we need to query the release with
# artist-credits
res = musicbrainzngs.get_release_by_id(
release_id, includes=["artists", "artist-credits",
"recordings", "discids",
"labels", "recording-level-rels",
"work-rels", "release-groups"])
_record(record, 'release', release_id, res)
releaseDetail = res['release']
formatted = json.dumps(releaseDetail, sort_keys=False, indent=4)
logger.debug('release %s', formatted)
return _getMetadata(releaseDetail, discid, country)
# see http://bugs.musicbrainz.org/browser/python-musicbrainz2/trunk/examples/
# ripper.py
@@ -287,11 +325,8 @@ def musicbrainz(discid, country=None, record=False):
:rtype: list of :any:`DiscMetadata`
"""
logger.debug('looking up results for discid %r', discid)
import musicbrainzngs
logging.getLogger("musicbrainzngs").setLevel(logging.WARNING)
musicbrainzngs.set_useragent("whipper", whipper.__version__,
"https://github.com/whipper-team/whipper")
ret = []
try:
@@ -314,26 +349,12 @@ def musicbrainz(discid, country=None, record=False):
# Display the returned results to the user.
import json
for release in result['disc']['release-list']:
formatted = json.dumps(release, sort_keys=False, indent=4)
logger.debug('result %s: artist %r, title %r', formatted,
release['artist-credit-phrase'], release['title'])
# to get titles of recordings, we need to query the release with
# artist-credits
res = musicbrainzngs.get_release_by_id(
release['id'], includes=["artists", "artist-credits",
"recordings", "discids", "labels",
"recording-level-rels", "work-rels",
"release-groups"])
_record(record, 'release', release['id'], res)
releaseDetail = res['release']
formatted = json.dumps(releaseDetail, sort_keys=False, indent=4)
logger.debug('release %s', formatted)
md = _getMetadata(releaseDetail, discid, country)
md = getReleaseMetadata(release['id'], discid, country, record)
if md:
logger.debug('duration %r', md.duration)
ret.append(md)

View File

@@ -4,8 +4,10 @@
import os
import pickle
import unittest
import json
from whipper.command import mblookup
from whipper.common.mbngs import _getMetadata
class MBLookupTestCase(unittest.TestCase):
@@ -19,6 +21,22 @@ class MBLookupTestCase(unittest.TestCase):
with open(path, "rb") as p:
return pickle.load(p)
@staticmethod
def _mock_getReleaseMetadata(release_id):
"""
Mock function for whipper.common.mbngs.getReleaseMetadata.
:param release_id: MusicBrainz Release ID
:type release_id: str
:returns: a DiscMetadata object based on the given release_id
:rtype: `DiscMetadata`
"""
filename = 'whipper.release.{}.json'.format(release_id)
path = os.path.join(os.path.dirname(__file__), filename)
with open(path, "rb") as handle:
response = json.loads(handle.read().decode('utf-8'))
return _getMetadata(response['release'])
def testMissingReleaseType(self):
"""Test that lookup for release without a type set doesn't fail."""
# Using: Gustafsson, Österberg & Cowle - What's Up? 8 (disc 4)
@@ -28,3 +46,12 @@ class MBLookupTestCase(unittest.TestCase):
# https://musicbrainz.org/cdtoc/xu338_M8WukSRi0J.KTlDoflB8Y-
lookup = mblookup.MBLookup([discid], 'whipper mblookup', None)
lookup.do()
def testGetDataFromReleaseId(self):
"""Test that lookup for a release with a specified id."""
# Using: The KLF - Space & Chill Out
# https://musicbrainz.org/release/c56ff16e-1d81-47de-926f-ba22891bd2bd
mblookup.getReleaseMetadata = self._mock_getReleaseMetadata
releaseid = 'c56ff16e-1d81-47de-926f-ba22891bd2bd'
lookup = mblookup.MBLookup([releaseid], 'whipper mblookup', None)
lookup.do()