mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-02 09:49:36 +01:00
Merge branch 'ngs'
This commit is contained in:
390
headphones/mb.py
390
headphones/mb.py
@@ -3,16 +3,13 @@ from __future__ import with_statement
|
||||
import time
|
||||
import threading
|
||||
|
||||
import lib.musicbrainz2.webservice as ws
|
||||
import lib.musicbrainz2.model as m
|
||||
import lib.musicbrainz2.utils as u
|
||||
|
||||
from lib.musicbrainz2.webservice import WebServiceError
|
||||
|
||||
import headphones
|
||||
from headphones import logger, db
|
||||
from headphones.helpers import multikeysort, replace_all
|
||||
|
||||
import lib.musicbrainzngs as musicbrainzngs
|
||||
from lib.musicbrainzngs import WebServiceError
|
||||
|
||||
mb_lock = threading.Lock()
|
||||
|
||||
|
||||
@@ -46,8 +43,22 @@ def startmb(forcemb=False):
|
||||
mbport = 5000
|
||||
sleepytime = 0
|
||||
|
||||
service = ws.WebService(host=mbhost, port=mbport, username=mbuser, password=mbpass, mirror=headphones.MIRROR)
|
||||
q = ws.Query(service)
|
||||
musicbrainzngs.set_useragent("headphones","0.0","https://github.com/rembo10/headphones")
|
||||
musicbrainzngs.set_hostname(mbhost + ":" + str(mbport))
|
||||
if sleepytime == 0:
|
||||
musicbrainzngs.set_rate_limit(False)
|
||||
else:
|
||||
musicbrainzngs.set_rate_limit(True)
|
||||
|
||||
# Add headphones credentials
|
||||
if headphones.MIRROR == "headphones":
|
||||
if not mbuser and mbpass:
|
||||
logger.warn("No username or password set for VIP server")
|
||||
else:
|
||||
musicbrainzngs.hpauth(mbuser,mbpass)
|
||||
|
||||
#q = musicbrainzngs
|
||||
q = musicbrainzngs
|
||||
|
||||
logger.debug('Using the following server values:\nMBHost: %s ; MBPort: %i ; Sleep Interval: %i ' % (mbhost, mbport, sleepytime))
|
||||
|
||||
@@ -56,9 +67,7 @@ def startmb(forcemb=False):
|
||||
def findArtist(name, limit=1):
|
||||
|
||||
with mb_lock:
|
||||
|
||||
artistlist = []
|
||||
attempt = 0
|
||||
artistResults = None
|
||||
|
||||
chars = set('!?*')
|
||||
@@ -67,59 +76,50 @@ def findArtist(name, limit=1):
|
||||
|
||||
q, sleepytime = startmb(forcemb=True)
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
artistResults = q.getArtists(ws.ArtistFilter(query=name, limit=limit))
|
||||
break
|
||||
artistResults = musicbrainzngs.search_artists(query=name,limit=limit)['artist-list']
|
||||
except WebServiceError, e:
|
||||
logger.warn('Attempt to query MusicBrainz for %s failed (%s)' % (name, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(10)
|
||||
time.sleep(5)
|
||||
|
||||
time.sleep(sleepytime)
|
||||
|
||||
if not artistResults:
|
||||
return False
|
||||
|
||||
for result in artistResults:
|
||||
|
||||
if result.artist.name != result.artist.getUniqueName() and limit == 1:
|
||||
|
||||
if 'disambiguation' in result:
|
||||
uniquename = unicode(result['sort-name'] + " (" + result['disambiguation'] + ")")
|
||||
else:
|
||||
uniquename = unicode(result['sort-name'])
|
||||
if result['name'] != uniquename and limit == 1:
|
||||
logger.debug('Found an artist with a disambiguation: %s - doing an album based search' % name)
|
||||
artistdict = findArtistbyAlbum(name)
|
||||
|
||||
if not artistdict:
|
||||
logger.debug('Cannot determine the best match from an artist/album search. Using top match instead')
|
||||
artistlist.append({
|
||||
'name': result.artist.name,
|
||||
'uniquename': result.artist.getUniqueName(),
|
||||
'id': u.extractUuid(result.artist.id),
|
||||
'url': result.artist.id,
|
||||
'score': result.score
|
||||
'name': unicode(result['sort-name']),
|
||||
'uniquename': uniquename,
|
||||
'id': unicode(result['id']),
|
||||
'url': unicode("http://musicbrainz.org/artist/" + result['id']),#probably needs to be changed
|
||||
'score': int(result['ext:score'])
|
||||
})
|
||||
|
||||
else:
|
||||
artistlist.append(artistdict)
|
||||
|
||||
else:
|
||||
artistlist.append({
|
||||
'name': result.artist.name,
|
||||
'uniquename': result.artist.getUniqueName(),
|
||||
'id': u.extractUuid(result.artist.id),
|
||||
'url': result.artist.id,
|
||||
'score': result.score
|
||||
'name': unicode(result['sort-name']),
|
||||
'uniquename': uniquename,
|
||||
'id': unicode(result['id']),
|
||||
'url': unicode("http://musicbrainz.org/artist/" + result['id']),#probably needs to be changed
|
||||
'score': int(result['ext:score'])
|
||||
})
|
||||
|
||||
return artistlist
|
||||
|
||||
def findRelease(name, limit=1):
|
||||
|
||||
with mb_lock:
|
||||
|
||||
releaselist = []
|
||||
attempt = 0
|
||||
releaseResults = None
|
||||
releaselistngs = []
|
||||
releaseResultsngs = None
|
||||
|
||||
chars = set('!?')
|
||||
if any((c in chars) for c in name):
|
||||
@@ -127,81 +127,86 @@ def findRelease(name, limit=1):
|
||||
|
||||
q, sleepytime = startmb(forcemb=True)
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
releaseResults = q.getReleases(ws.ReleaseFilter(query=name, limit=limit))
|
||||
break
|
||||
except WebServiceError, e:
|
||||
releaseResultsngs = musicbrainzngs.search_releases(query=name,limit=limit)['release-list']
|
||||
except WebServiceError, e: #need to update exceptions
|
||||
logger.warn('Attempt to query MusicBrainz for "%s" failed: %s' % (name, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(10)
|
||||
time.sleep(5)
|
||||
|
||||
time.sleep(sleepytime)
|
||||
|
||||
if not releaseResults:
|
||||
if not releaseResultsngs:
|
||||
return False
|
||||
|
||||
for result in releaseResults:
|
||||
|
||||
releaselist.append({
|
||||
'uniquename': result.release.artist.name,
|
||||
'title': result.release.title,
|
||||
'id': u.extractUuid(result.release.artist.id),
|
||||
'albumid': u.extractUuid(result.release.id),
|
||||
'url': result.release.artist.id,
|
||||
'albumurl': result.release.id,
|
||||
'score': result.score
|
||||
for result in releaseResultsngs:
|
||||
releaselistngs.append({
|
||||
'uniquename': unicode(result['artist-credit'][0]['artist']['name']),
|
||||
'title': unicode(result['title']),
|
||||
'id': unicode(result['artist-credit'][0]['artist']['id']),
|
||||
'albumid': unicode(result['id']),
|
||||
'url': unicode("http://musicbrainz.org/artist/" + result['artist-credit'][0]['artist']['id']),#probably needs to be changed
|
||||
'albumurl': unicode("http://musicbrainz.org/release/" + result['id']),#probably needs to be changed
|
||||
'score': int(result['ext:score'])
|
||||
})
|
||||
|
||||
return releaselist
|
||||
return releaselistngs
|
||||
|
||||
def getArtist(artistid, extrasonly=False):
|
||||
|
||||
with mb_lock:
|
||||
|
||||
artist_dict = {}
|
||||
|
||||
#Get all official release groups
|
||||
inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, m.Release.TYPE_ALBUM), releaseGroups=True)
|
||||
artist = None
|
||||
attempt = 0
|
||||
|
||||
q, sleepytime = startmb()
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
artist = q.getArtistById(artistid, inc)
|
||||
break
|
||||
limit = 100
|
||||
artist = musicbrainzngs.get_artist_by_id(artistid)['artist']
|
||||
newRgs = None
|
||||
artist['release-group-list'] = []
|
||||
while newRgs == None or len(newRgs) >= limit:
|
||||
newRgs = musicbrainzngs.browse_release_groups(artistid,release_type="album",offset=len(artist['release-group-list']),limit=limit)['release-group-list']
|
||||
artist['release-group-list'] += newRgs
|
||||
except WebServiceError, e:
|
||||
logger.warn('Attempt to retrieve artist information from MusicBrainz failed for artistid: %s (%s)' % (artistid, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(5)
|
||||
except Exception,e:
|
||||
pass
|
||||
|
||||
if not artist:
|
||||
return False
|
||||
|
||||
time.sleep(sleepytime)
|
||||
|
||||
artist_dict['artist_name'] = artist.name
|
||||
artist_dict['artist_sortname'] = artist.sortName
|
||||
artist_dict['artist_uniquename'] = artist.getUniqueName()
|
||||
artist_dict['artist_type'] = u.extractFragment(artist.type)
|
||||
artist_dict['artist_begindate'] = artist.beginDate
|
||||
artist_dict['artist_enddate'] = artist.endDate
|
||||
if 'disambiguation' in artist:
|
||||
uniquename = unicode(artist['sort-name'] + " (" + artist['disambiguation'] + ")")
|
||||
else:
|
||||
uniquename = unicode(artist['sort-name'])
|
||||
artist_dict['artist_name'] = unicode(artist['name'])
|
||||
artist_dict['artist_sortname'] = unicode(artist['sort-name'])
|
||||
artist_dict['artist_uniquename'] = uniquename
|
||||
artist_dict['artist_type'] = unicode(artist['type'])
|
||||
|
||||
artist_dict['artist_begindate'] = None
|
||||
artist_dict['artist_enddate'] = None
|
||||
if 'life-span' in artist:
|
||||
if 'begin' in artist['life-span']:
|
||||
artist_dict['artist_begindate'] = unicode(artist['life-span']['begin'])
|
||||
if 'end' in artist['life-span']:
|
||||
artist_dict['artist_enddate'] = unicode(artist['life-span']['end'])
|
||||
|
||||
|
||||
|
||||
releasegroups = []
|
||||
|
||||
if not extrasonly:
|
||||
|
||||
for rg in artist.getReleaseGroups():
|
||||
|
||||
for rg in artist['release-group-list']:
|
||||
if rg['type'] != 'Album': #only add releases without a secondary type
|
||||
continue
|
||||
releasegroups.append({
|
||||
'title': rg.title,
|
||||
'id': u.extractUuid(rg.id),
|
||||
'url': rg.id,
|
||||
'type': u.getReleaseTypeName(rg.type)
|
||||
'title': unicode(rg['title']),
|
||||
'id': unicode(rg['id']),
|
||||
'url': u"http://musicbrainz.org/release-group/" + rg['id'],
|
||||
'type': unicode(rg['type'])
|
||||
})
|
||||
|
||||
# See if we need to grab extras
|
||||
@@ -213,33 +218,25 @@ def getArtist(artistid, extrasonly=False):
|
||||
includeExtras = False
|
||||
|
||||
if includeExtras or headphones.INCLUDE_EXTRAS:
|
||||
includes = [m.Release.TYPE_COMPILATION, m.Release.TYPE_REMIX, m.Release.TYPE_SINGLE, m.Release.TYPE_LIVE, m.Release.TYPE_EP, m.Release.TYPE_SOUNDTRACK]
|
||||
includes = ["single", "ep", "compilation", "soundtrack", "live", "remix"]
|
||||
for include in includes:
|
||||
inc = ws.ArtistIncludes(releases=(m.Release.TYPE_OFFICIAL, include), releaseGroups=True)
|
||||
|
||||
artist = None
|
||||
attempt = 0
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
artist = q.getArtistById(artistid, inc)
|
||||
break
|
||||
except WebServiceError, e:
|
||||
artist = musicbrainzngs.get_artist_by_id(artistid,includes=["releases","release-groups"],release_status=['official'],release_type=include)['artist']
|
||||
except WebServiceError, e:#update exceptions
|
||||
logger.warn('Attempt to retrieve artist information from MusicBrainz failed for artistid: %s (%s)' % (artistid, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(5)
|
||||
|
||||
if not artist:
|
||||
continue
|
||||
|
||||
for rg in artist.getReleaseGroups():
|
||||
|
||||
for rg in artist['release-group-list']:
|
||||
releasegroups.append({
|
||||
'title': rg.title,
|
||||
'id': u.extractUuid(rg.id),
|
||||
'url': rg.id,
|
||||
'type': u.getReleaseTypeName(rg.type)
|
||||
'title': unicode(rg['title']),
|
||||
'id': unicode(rg['id']),
|
||||
'url': u"http://musicbrainz.org/release-group/" + rg['id'],
|
||||
'type': unicode(rg['type'])
|
||||
})
|
||||
|
||||
artist_dict['releasegroups'] = releasegroups
|
||||
@@ -254,51 +251,37 @@ def getReleaseGroup(rgid):
|
||||
|
||||
releaselist = []
|
||||
|
||||
inc = ws.ReleaseGroupIncludes(releases=True, artist=True)
|
||||
releaseGroup = None
|
||||
attempt = 0
|
||||
|
||||
q, sleepytime = startmb()
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
releaseGroup = q.getReleaseGroupById(rgid, inc)
|
||||
break
|
||||
releaseGroup = musicbrainzngs.get_release_group_by_id(rgid,["artists","releases","media","discids",])['release-group']
|
||||
except WebServiceError, e:
|
||||
logger.warn('Attempt to retrieve information from MusicBrainz for release group "%s" failed (%s)' % (rgid, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(5)
|
||||
|
||||
if not releaseGroup:
|
||||
return False
|
||||
|
||||
|
||||
time.sleep(sleepytime)
|
||||
|
||||
# I think for now we have to make separate queries for each release, in order
|
||||
# to get more detailed release info (ASIN, track count, etc.)
|
||||
for release in releaseGroup.releases:
|
||||
|
||||
inc = ws.ReleaseIncludes(tracks=True, releaseEvents=True)
|
||||
for release in releaseGroup['release-list']:
|
||||
releaseResult = None
|
||||
attempt = 0
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
releaseResult = q.getReleaseById(release.id, inc)
|
||||
break
|
||||
releaseResult = musicbrainzngs.get_release_by_id(release['id'],["recordings","media"])['release']
|
||||
except WebServiceError, e:
|
||||
logger.warn('Attempt to retrieve release information for %s from MusicBrainz failed (%s)' % (releaseResult.title, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(5)
|
||||
|
||||
if not releaseResult:
|
||||
continue
|
||||
|
||||
# Release filter for non-official live albums
|
||||
types = releaseResult.getTypes()
|
||||
if any('Live' in type for type in types):
|
||||
if not any('Official' in type for type in types):
|
||||
if releaseGroup['type'] == 'live' and releaseResult['status'] != 'Official':
|
||||
logger.debug('%s is not an official live album. Skipping' % releaseResult.name)
|
||||
continue
|
||||
|
||||
@@ -313,68 +296,72 @@ def getReleaseGroup(rgid):
|
||||
'Digital Media': '0'
|
||||
}
|
||||
|
||||
country = {
|
||||
countries = {
|
||||
'US': '0',
|
||||
|
||||
'GB': '1',
|
||||
'JP': '2',
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
format = int(replace_all(u.extractFragment(releaseResult.releaseEvents[0].format), formats))
|
||||
format = int(formats[releaseResult['medium-list'][0]['format']])
|
||||
except:
|
||||
format = 3
|
||||
|
||||
try:
|
||||
country = int(replace_all(releaseResult.releaseEvents[0].country, country))
|
||||
country = int(countries[releaseResult['country']])
|
||||
except:
|
||||
country = 3
|
||||
totalTracks = 0
|
||||
tracks = []
|
||||
for medium in releaseResult['medium-list']:
|
||||
for track in medium['track-list']:
|
||||
tracks.append({
|
||||
'number': totalTracks + 1,
|
||||
'title': unicode(track['recording']['title']),
|
||||
'id': unicode(track['recording']['id']),
|
||||
'url': u"http://musicbrainz.org/track/" + track['recording']['id'],
|
||||
'duration': int(track['recording']['length'] if 'length' in track['recording'] else track['length'] if 'length' in track else 0)
|
||||
})
|
||||
totalTracks += 1
|
||||
|
||||
release_dict = {
|
||||
'hasasin': bool(releaseResult.asin),
|
||||
'asin': releaseResult.asin,
|
||||
'trackscount': len(releaseResult.getTracks()),
|
||||
'releaseid': u.extractUuid(releaseResult.id),
|
||||
'releasedate': releaseResult.getEarliestReleaseDate(),
|
||||
'hasasin': bool(releaseResult.get('asin')),
|
||||
'asin': unicode(releaseResult.get('asin')) if 'asin' in releaseResult else None,
|
||||
'trackscount': totalTracks,
|
||||
'releaseid': unicode(releaseResult.get('id')),
|
||||
'releasedate': unicode(releaseResult.get('date')),
|
||||
'format': format,
|
||||
'country': country
|
||||
}
|
||||
|
||||
tracks = []
|
||||
|
||||
i = 1
|
||||
for track in releaseResult.tracks:
|
||||
|
||||
tracks.append({
|
||||
'number': i,
|
||||
'title': track.title,
|
||||
'id': u.extractUuid(track.id),
|
||||
'url': track.id,
|
||||
'duration': track.duration
|
||||
})
|
||||
i += 1
|
||||
|
||||
release_dict['tracks'] = tracks
|
||||
|
||||
releaselist.append(release_dict)
|
||||
#necessary to make dates that miss the month and/or day show up after full dates
|
||||
def getSortableReleaseDate(releaseDate):
|
||||
if releaseDate.count('-') == 2:
|
||||
return releaseDate
|
||||
elif releaseDate.count('-') == 1:
|
||||
return releaseDate + '32'
|
||||
else:
|
||||
return releaseDate + '13-32'
|
||||
|
||||
releaselist.sort(key=lambda x:getSortableReleaseDate(x['releasedate']))
|
||||
|
||||
|
||||
average_tracks = sum(x['trackscount'] for x in releaselist) / float(len(releaselist))
|
||||
|
||||
for item in releaselist:
|
||||
item['trackscount_delta'] = abs(average_tracks - item['trackscount'])
|
||||
|
||||
a = multikeysort(releaselist, ['-hasasin', 'country', 'format', 'trackscount_delta'])
|
||||
|
||||
release_dict = {'releaseid' :a[0]['releaseid'],
|
||||
'releasedate' : releaselist[0]['releasedate'],
|
||||
'releasedate' : unicode(releaselist[0]['releasedate']),
|
||||
'trackcount' : a[0]['trackscount'],
|
||||
'tracks' : a[0]['tracks'],
|
||||
'asin' : a[0]['asin'],
|
||||
'releaselist' : releaselist,
|
||||
'artist_name' : releaseGroup.artist.name,
|
||||
'artist_id' : u.extractUuid(releaseGroup.artist.id),
|
||||
'title' : releaseGroup.title,
|
||||
'type' : u.extractFragment(releaseGroup.type)
|
||||
'artist_name' : unicode(releaseGroup['artist-credit'][0]['artist']['name']),
|
||||
'artist_id' : unicode(releaseGroup['artist-credit'][0]['artist']['id']),
|
||||
'title' : unicode(releaseGroup['title']),
|
||||
'type' : unicode(releaseGroup['type'])
|
||||
}
|
||||
|
||||
return release_dict
|
||||
@@ -386,21 +373,14 @@ def getRelease(releaseid):
|
||||
with mb_lock:
|
||||
|
||||
release = {}
|
||||
|
||||
inc = ws.ReleaseIncludes(tracks=True, releaseEvents=True, releaseGroup=True, artist=True)
|
||||
results = None
|
||||
attempt = 0
|
||||
|
||||
q, sleepytime = startmb()
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
results = q.getReleaseById(releaseid, inc)
|
||||
break
|
||||
results = musicbrainzngs.get_release_by_id(releaseid,["artists","release-groups","media","recordings"]).get('release')
|
||||
except WebServiceError, e:
|
||||
logger.warn('Attempt to retrieve information from MusicBrainz for release "%s" failed (%s)' % (releaseid, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(5)
|
||||
|
||||
if not results:
|
||||
@@ -408,36 +388,34 @@ def getRelease(releaseid):
|
||||
|
||||
time.sleep(sleepytime)
|
||||
|
||||
release['title'] = results.title
|
||||
release['id'] = u.extractUuid(results.id)
|
||||
release['asin'] = results.asin
|
||||
release['date'] = results.getEarliestReleaseDate()
|
||||
release['title'] = unicode(results['title'])
|
||||
release['id'] = unicode(results['id'])
|
||||
release['asin'] = unicode(results['asin']) if 'asin' in results else None
|
||||
release['date'] = unicode(results['date'])
|
||||
|
||||
|
||||
rg = results.getReleaseGroup()
|
||||
if rg:
|
||||
release['rgid'] = u.extractUuid(rg.id)
|
||||
release['rg_title'] = rg.title
|
||||
release['rg_type'] = u.extractFragment(rg.type)
|
||||
if 'release-group' in results:
|
||||
release['rgid'] = unicode(results['release-group']['id'])
|
||||
release['rg_title'] = unicode(results['release-group']['title'])
|
||||
release['rg_type'] = unicode(results['release-group']['type'])
|
||||
else:
|
||||
logger.warn("Release " + releaseid + "had no ReleaseGroup associated")
|
||||
#so we can start with a releaseID from anywhere and get the artist info
|
||||
#it looks like MB api v1 only returns 1 artist object - 2.0 returns more...
|
||||
release['artist_name'] = results.artist.name
|
||||
release['artist_id'] = u.extractUuid(results.artist.id)
|
||||
|
||||
release['artist_name'] = unicode(results['artist-credit'][0]['artist']['name'])
|
||||
release['artist_id'] = unicode(results['artist-credit'][0]['artist']['id'])
|
||||
|
||||
|
||||
totalTracks = 0
|
||||
tracks = []
|
||||
|
||||
i = 1
|
||||
for track in results.tracks:
|
||||
for medium in results['medium-list']:
|
||||
for track in medium['track-list']:
|
||||
tracks.append({
|
||||
'number': i,
|
||||
'title': track.title,
|
||||
'id': u.extractUuid(track.id),
|
||||
'url': track.id,
|
||||
'duration': track.duration
|
||||
'number': totalTracks + 1,
|
||||
'title': unicode(track['recording']['title']),
|
||||
'id': unicode(track['recording']['id']),
|
||||
'url': u"http://musicbrainz.org/track/" + track['recording']['id'],
|
||||
'duration': int(track['length']) if 'length' in track else 0
|
||||
})
|
||||
i += 1
|
||||
totalTracks += 1
|
||||
|
||||
release['tracks'] = tracks
|
||||
|
||||
@@ -459,21 +437,15 @@ def findArtistbyAlbum(name):
|
||||
|
||||
term = '"'+artist['AlbumTitle']+'" AND artist:"'+name+'"'
|
||||
|
||||
f = ws.ReleaseGroupFilter(query=term, limit=1)
|
||||
results = None
|
||||
attempt = 0
|
||||
|
||||
q, sleepytime = startmb(forcemb=True)
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
results = q.getReleaseGroups(f)
|
||||
break
|
||||
results = musicbrainzngs.search_release_groups(term).get('release-group-list')
|
||||
except WebServiceError, e:
|
||||
logger.warn('Attempt to query MusicBrainz for %s failed (%s)' % (name, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(10)
|
||||
time.sleep(5)
|
||||
|
||||
time.sleep(sleepytime)
|
||||
|
||||
@@ -481,39 +453,41 @@ def findArtistbyAlbum(name):
|
||||
return False
|
||||
|
||||
artist_dict = {}
|
||||
for releaseGroup in results:
|
||||
newArtist = releaseGroup['artist-credit'][0]['artist']
|
||||
if 'disambiguation' in newArtist:
|
||||
uniquename = unicode(newArtist['sort-name'] + " (" + newArtist['disambiguation'] + ")")
|
||||
else:
|
||||
uniquename = unicode(newArtist['sort-name'])
|
||||
artist_dict['name'] = unicode(newArtist['sort-name'])
|
||||
artist_dict['uniquename'] = uniquename
|
||||
artist_dict['id'] = unicode(newArtist['id'])
|
||||
artist_dict['url'] = u'http://musicbrainz.org/artist/' + newArtist['id']
|
||||
artist_dict['score'] = int(releaseGroup['ext:score'])
|
||||
|
||||
|
||||
for result in results:
|
||||
releaseGroup = result.releaseGroup
|
||||
artist_dict['name'] = releaseGroup.artist.name
|
||||
artist_dict['uniquename'] = releaseGroup.artist.getUniqueName()
|
||||
artist_dict['id'] = u.extractUuid(releaseGroup.artist.id)
|
||||
artist_dict['url'] = releaseGroup.artist.id
|
||||
artist_dict['score'] = result.score
|
||||
|
||||
return artist_dict
|
||||
|
||||
def findAlbumID(artist=None, album=None):
|
||||
|
||||
f = ws.ReleaseGroupFilter(title=album, artistName=artist, limit=1)
|
||||
results = None
|
||||
attempt = 0
|
||||
results_ngs = None
|
||||
|
||||
q, sleepytime = startmb(forcemb=True)
|
||||
|
||||
while attempt < 5:
|
||||
|
||||
try:
|
||||
results = q.getReleaseGroups(f)
|
||||
break
|
||||
term = '"'+album+'" AND artist:"'+artist+'"'
|
||||
results_ngs = musicbrainzngs.search_release_groups(term,1).get('release-group-list')
|
||||
except WebServiceError, e:
|
||||
logger.warn('Attempt to query MusicBrainz for %s - %s failed (%s)' % (artist, album, str(e)))
|
||||
attempt += 1
|
||||
time.sleep(10)
|
||||
time.sleep(5)
|
||||
|
||||
time.sleep(sleepytime)
|
||||
|
||||
if not results:
|
||||
if not results_ngs:
|
||||
return False
|
||||
|
||||
rgid = u.extractUuid(results[0].releaseGroup.id)
|
||||
return rgid
|
||||
if len(results_ngs) < 1:
|
||||
return False
|
||||
rgid_ngs = unicode(results_ngs[0]['id'])
|
||||
return rgid_ngs
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
"""A collection of classes for MusicBrainz.
|
||||
|
||||
To get started quickly, have a look at L{webservice.Query} and the examples
|
||||
there. The source distribution also contains example code you might find
|
||||
interesting.
|
||||
|
||||
This package contains the following modules:
|
||||
|
||||
1. L{model}: The MusicBrainz domain model, containing classes like
|
||||
L{Artist <model.Artist>}, L{Release <model.Release>}, or
|
||||
L{Track <model.Track>}
|
||||
|
||||
2. L{webservice}: An interface to the MusicBrainz XML web service.
|
||||
|
||||
3. L{wsxml}: A parser for the web service XML format (MMD).
|
||||
|
||||
4. L{disc}: Functions for creating and submitting DiscIDs.
|
||||
|
||||
5. L{utils}: Utilities for working with URIs and other commonly needed tools.
|
||||
|
||||
@author: Matthias Friedrich <matt@mafr.de>
|
||||
"""
|
||||
__revision__ = '$Id: __init__.py 12974 2011-05-01 08:43:54Z luks $'
|
||||
__version__ = '0.7.3'
|
||||
|
||||
# EOF
|
||||
@@ -1,10 +0,0 @@
|
||||
"""Support data for the musicbrainz2 package.
|
||||
|
||||
This package is I{not} part of the public API, it has been added to work
|
||||
around shortcomings in python and may thus be removed at any time.
|
||||
|
||||
Please use the L{musicbrainz2.utils} module instead.
|
||||
"""
|
||||
__revision__ = '$Id: __init__.py 7386 2006-04-30 11:12:55Z matt $'
|
||||
|
||||
# EOF
|
||||
@@ -1,253 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__revision__ = '$Id: countrynames.py 7386 2006-04-30 11:12:55Z matt $'
|
||||
|
||||
countryNames = {
|
||||
u'BD': u'Bangladesh',
|
||||
u'BE': u'Belgium',
|
||||
u'BF': u'Burkina Faso',
|
||||
u'BG': u'Bulgaria',
|
||||
u'BB': u'Barbados',
|
||||
u'WF': u'Wallis and Futuna Islands',
|
||||
u'BM': u'Bermuda',
|
||||
u'BN': u'Brunei Darussalam',
|
||||
u'BO': u'Bolivia',
|
||||
u'BH': u'Bahrain',
|
||||
u'BI': u'Burundi',
|
||||
u'BJ': u'Benin',
|
||||
u'BT': u'Bhutan',
|
||||
u'JM': u'Jamaica',
|
||||
u'BV': u'Bouvet Island',
|
||||
u'BW': u'Botswana',
|
||||
u'WS': u'Samoa',
|
||||
u'BR': u'Brazil',
|
||||
u'BS': u'Bahamas',
|
||||
u'BY': u'Belarus',
|
||||
u'BZ': u'Belize',
|
||||
u'RU': u'Russian Federation',
|
||||
u'RW': u'Rwanda',
|
||||
u'RE': u'Reunion',
|
||||
u'TM': u'Turkmenistan',
|
||||
u'TJ': u'Tajikistan',
|
||||
u'RO': u'Romania',
|
||||
u'TK': u'Tokelau',
|
||||
u'GW': u'Guinea-Bissau',
|
||||
u'GU': u'Guam',
|
||||
u'GT': u'Guatemala',
|
||||
u'GR': u'Greece',
|
||||
u'GQ': u'Equatorial Guinea',
|
||||
u'GP': u'Guadeloupe',
|
||||
u'JP': u'Japan',
|
||||
u'GY': u'Guyana',
|
||||
u'GF': u'French Guiana',
|
||||
u'GE': u'Georgia',
|
||||
u'GD': u'Grenada',
|
||||
u'GB': u'United Kingdom',
|
||||
u'GA': u'Gabon',
|
||||
u'SV': u'El Salvador',
|
||||
u'GN': u'Guinea',
|
||||
u'GM': u'Gambia',
|
||||
u'GL': u'Greenland',
|
||||
u'GI': u'Gibraltar',
|
||||
u'GH': u'Ghana',
|
||||
u'OM': u'Oman',
|
||||
u'TN': u'Tunisia',
|
||||
u'JO': u'Jordan',
|
||||
u'HT': u'Haiti',
|
||||
u'HU': u'Hungary',
|
||||
u'HK': u'Hong Kong',
|
||||
u'HN': u'Honduras',
|
||||
u'HM': u'Heard and Mc Donald Islands',
|
||||
u'VE': u'Venezuela',
|
||||
u'PR': u'Puerto Rico',
|
||||
u'PW': u'Palau',
|
||||
u'PT': u'Portugal',
|
||||
u'SJ': u'Svalbard and Jan Mayen Islands',
|
||||
u'PY': u'Paraguay',
|
||||
u'IQ': u'Iraq',
|
||||
u'PA': u'Panama',
|
||||
u'PF': u'French Polynesia',
|
||||
u'PG': u'Papua New Guinea',
|
||||
u'PE': u'Peru',
|
||||
u'PK': u'Pakistan',
|
||||
u'PH': u'Philippines',
|
||||
u'PN': u'Pitcairn',
|
||||
u'PL': u'Poland',
|
||||
u'PM': u'St. Pierre and Miquelon',
|
||||
u'ZM': u'Zambia',
|
||||
u'EH': u'Western Sahara',
|
||||
u'EE': u'Estonia',
|
||||
u'EG': u'Egypt',
|
||||
u'ZA': u'South Africa',
|
||||
u'EC': u'Ecuador',
|
||||
u'IT': u'Italy',
|
||||
u'VN': u'Viet Nam',
|
||||
u'SB': u'Solomon Islands',
|
||||
u'ET': u'Ethiopia',
|
||||
u'SO': u'Somalia',
|
||||
u'ZW': u'Zimbabwe',
|
||||
u'SA': u'Saudi Arabia',
|
||||
u'ES': u'Spain',
|
||||
u'ER': u'Eritrea',
|
||||
u'MD': u'Moldova, Republic of',
|
||||
u'MG': u'Madagascar',
|
||||
u'MA': u'Morocco',
|
||||
u'MC': u'Monaco',
|
||||
u'UZ': u'Uzbekistan',
|
||||
u'MM': u'Myanmar',
|
||||
u'ML': u'Mali',
|
||||
u'MO': u'Macau',
|
||||
u'MN': u'Mongolia',
|
||||
u'MH': u'Marshall Islands',
|
||||
u'MK': u'Macedonia, The Former Yugoslav Republic of',
|
||||
u'MU': u'Mauritius',
|
||||
u'MT': u'Malta',
|
||||
u'MW': u'Malawi',
|
||||
u'MV': u'Maldives',
|
||||
u'MQ': u'Martinique',
|
||||
u'MP': u'Northern Mariana Islands',
|
||||
u'MS': u'Montserrat',
|
||||
u'MR': u'Mauritania',
|
||||
u'UG': u'Uganda',
|
||||
u'MY': u'Malaysia',
|
||||
u'MX': u'Mexico',
|
||||
u'IL': u'Israel',
|
||||
u'FR': u'France',
|
||||
u'IO': u'British Indian Ocean Territory',
|
||||
u'SH': u'St. Helena',
|
||||
u'FI': u'Finland',
|
||||
u'FJ': u'Fiji',
|
||||
u'FK': u'Falkland Islands (Malvinas)',
|
||||
u'FM': u'Micronesia, Federated States of',
|
||||
u'FO': u'Faroe Islands',
|
||||
u'NI': u'Nicaragua',
|
||||
u'NL': u'Netherlands',
|
||||
u'NO': u'Norway',
|
||||
u'NA': u'Namibia',
|
||||
u'VU': u'Vanuatu',
|
||||
u'NC': u'New Caledonia',
|
||||
u'NE': u'Niger',
|
||||
u'NF': u'Norfolk Island',
|
||||
u'NG': u'Nigeria',
|
||||
u'NZ': u'New Zealand',
|
||||
u'ZR': u'Zaire',
|
||||
u'NP': u'Nepal',
|
||||
u'NR': u'Nauru',
|
||||
u'NU': u'Niue',
|
||||
u'CK': u'Cook Islands',
|
||||
u'CI': u'Cote d\'Ivoire',
|
||||
u'CH': u'Switzerland',
|
||||
u'CO': u'Colombia',
|
||||
u'CN': u'China',
|
||||
u'CM': u'Cameroon',
|
||||
u'CL': u'Chile',
|
||||
u'CC': u'Cocos (Keeling) Islands',
|
||||
u'CA': u'Canada',
|
||||
u'CG': u'Congo',
|
||||
u'CF': u'Central African Republic',
|
||||
u'CZ': u'Czech Republic',
|
||||
u'CY': u'Cyprus',
|
||||
u'CX': u'Christmas Island',
|
||||
u'CR': u'Costa Rica',
|
||||
u'CV': u'Cape Verde',
|
||||
u'CU': u'Cuba',
|
||||
u'SZ': u'Swaziland',
|
||||
u'SY': u'Syrian Arab Republic',
|
||||
u'KG': u'Kyrgyzstan',
|
||||
u'KE': u'Kenya',
|
||||
u'SR': u'Suriname',
|
||||
u'KI': u'Kiribati',
|
||||
u'KH': u'Cambodia',
|
||||
u'KN': u'Saint Kitts and Nevis',
|
||||
u'KM': u'Comoros',
|
||||
u'ST': u'Sao Tome and Principe',
|
||||
u'SI': u'Slovenia',
|
||||
u'KW': u'Kuwait',
|
||||
u'SN': u'Senegal',
|
||||
u'SM': u'San Marino',
|
||||
u'SL': u'Sierra Leone',
|
||||
u'SC': u'Seychelles',
|
||||
u'KZ': u'Kazakhstan',
|
||||
u'KY': u'Cayman Islands',
|
||||
u'SG': u'Singapore',
|
||||
u'SE': u'Sweden',
|
||||
u'SD': u'Sudan',
|
||||
u'DO': u'Dominican Republic',
|
||||
u'DM': u'Dominica',
|
||||
u'DJ': u'Djibouti',
|
||||
u'DK': u'Denmark',
|
||||
u'VG': u'Virgin Islands (British)',
|
||||
u'DE': u'Germany',
|
||||
u'YE': u'Yemen',
|
||||
u'DZ': u'Algeria',
|
||||
u'US': u'United States',
|
||||
u'UY': u'Uruguay',
|
||||
u'YT': u'Mayotte',
|
||||
u'UM': u'United States Minor Outlying Islands',
|
||||
u'LB': u'Lebanon',
|
||||
u'LC': u'Saint Lucia',
|
||||
u'LA': u'Lao People\'s Democratic Republic',
|
||||
u'TV': u'Tuvalu',
|
||||
u'TW': u'Taiwan',
|
||||
u'TT': u'Trinidad and Tobago',
|
||||
u'TR': u'Turkey',
|
||||
u'LK': u'Sri Lanka',
|
||||
u'LI': u'Liechtenstein',
|
||||
u'LV': u'Latvia',
|
||||
u'TO': u'Tonga',
|
||||
u'LT': u'Lithuania',
|
||||
u'LU': u'Luxembourg',
|
||||
u'LR': u'Liberia',
|
||||
u'LS': u'Lesotho',
|
||||
u'TH': u'Thailand',
|
||||
u'TF': u'French Southern Territories',
|
||||
u'TG': u'Togo',
|
||||
u'TD': u'Chad',
|
||||
u'TC': u'Turks and Caicos Islands',
|
||||
u'LY': u'Libyan Arab Jamahiriya',
|
||||
u'VA': u'Vatican City State (Holy See)',
|
||||
u'VC': u'Saint Vincent and The Grenadines',
|
||||
u'AE': u'United Arab Emirates',
|
||||
u'AD': u'Andorra',
|
||||
u'AG': u'Antigua and Barbuda',
|
||||
u'AF': u'Afghanistan',
|
||||
u'AI': u'Anguilla',
|
||||
u'VI': u'Virgin Islands (U.S.)',
|
||||
u'IS': u'Iceland',
|
||||
u'IR': u'Iran (Islamic Republic of)',
|
||||
u'AM': u'Armenia',
|
||||
u'AL': u'Albania',
|
||||
u'AO': u'Angola',
|
||||
u'AN': u'Netherlands Antilles',
|
||||
u'AQ': u'Antarctica',
|
||||
u'AS': u'American Samoa',
|
||||
u'AR': u'Argentina',
|
||||
u'AU': u'Australia',
|
||||
u'AT': u'Austria',
|
||||
u'AW': u'Aruba',
|
||||
u'IN': u'India',
|
||||
u'TZ': u'Tanzania, United Republic of',
|
||||
u'AZ': u'Azerbaijan',
|
||||
u'IE': u'Ireland',
|
||||
u'ID': u'Indonesia',
|
||||
u'UA': u'Ukraine',
|
||||
u'QA': u'Qatar',
|
||||
u'MZ': u'Mozambique',
|
||||
u'BA': u'Bosnia and Herzegovina',
|
||||
u'CD': u'Congo, The Democratic Republic of the',
|
||||
u'CS': u'Serbia and Montenegro',
|
||||
u'HR': u'Croatia',
|
||||
u'KP': u'Korea (North), Democratic People\'s Republic of',
|
||||
u'KR': u'Korea (South), Republic of',
|
||||
u'SK': u'Slovakia',
|
||||
u'SU': u'Soviet Union (historical, 1922-1991)',
|
||||
u'TL': u'East Timor',
|
||||
u'XC': u'Czechoslovakia (historical, 1918-1992)',
|
||||
u'XE': u'Europe',
|
||||
u'XG': u'East Germany (historical, 1949-1990)',
|
||||
u'XU': u'[Unknown Country]',
|
||||
u'XW': u'[Worldwide]',
|
||||
u'YU': u'Yugoslavia (historical, 1918-1992)',
|
||||
}
|
||||
|
||||
# EOF
|
||||
@@ -1,400 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__revision__ = '$Id: languagenames.py 8725 2006-12-17 22:39:07Z luks $'
|
||||
|
||||
languageNames = {
|
||||
u'ART': u'Artificial (Other)',
|
||||
u'ROH': u'Raeto-Romance',
|
||||
u'SCO': u'Scots',
|
||||
u'SCN': u'Sicilian',
|
||||
u'ROM': u'Romany',
|
||||
u'RON': u'Romanian',
|
||||
u'OSS': u'Ossetian; Ossetic',
|
||||
u'ALE': u'Aleut',
|
||||
u'MNI': u'Manipuri',
|
||||
u'NWC': u'Classical Newari; Old Newari; Classical Nepal Bhasa',
|
||||
u'OSA': u'Osage',
|
||||
u'MNC': u'Manchu',
|
||||
u'MWR': u'Marwari',
|
||||
u'VEN': u'Venda',
|
||||
u'MWL': u'Mirandese',
|
||||
u'FAS': u'Persian',
|
||||
u'FAT': u'Fanti',
|
||||
u'FAN': u'Fang',
|
||||
u'FAO': u'Faroese',
|
||||
u'DIN': u'Dinka',
|
||||
u'HYE': u'Armenian',
|
||||
u'DSB': u'Lower Sorbian',
|
||||
u'CAR': u'Carib',
|
||||
u'DIV': u'Divehi',
|
||||
u'TEL': u'Telugu',
|
||||
u'TEM': u'Timne',
|
||||
u'NBL': u'Ndebele, South; South Ndebele',
|
||||
u'TER': u'Tereno',
|
||||
u'TET': u'Tetum',
|
||||
u'SUN': u'Sundanese',
|
||||
u'KUT': u'Kutenai',
|
||||
u'SUK': u'Sukuma',
|
||||
u'KUR': u'Kurdish',
|
||||
u'KUM': u'Kumyk',
|
||||
u'SUS': u'Susu',
|
||||
u'NEW': u'Newari; Nepal Bhasa',
|
||||
u'KUA': u'Kuanyama; Kwanyama',
|
||||
u'MEN': u'Mende',
|
||||
u'LEZ': u'Lezghian',
|
||||
u'GLA': u'Gaelic; Scottish Gaelic',
|
||||
u'BOS': u'Bosnian',
|
||||
u'GLE': u'Irish',
|
||||
u'EKA': u'Ekajuk',
|
||||
u'GLG': u'Gallegan',
|
||||
u'AKA': u'Akan',
|
||||
u'BOD': u'Tibetan',
|
||||
u'GLV': u'Manx',
|
||||
u'JRB': u'Judeo-Arabic',
|
||||
u'VIE': u'Vietnamese',
|
||||
u'IPK': u'Inupiaq',
|
||||
u'UZB': u'Uzbek',
|
||||
u'BRE': u'Breton',
|
||||
u'BRA': u'Braj',
|
||||
u'AYM': u'Aymara',
|
||||
u'CHA': u'Chamorro',
|
||||
u'CHB': u'Chibcha',
|
||||
u'CHE': u'Chechen',
|
||||
u'CHG': u'Chagatai',
|
||||
u'CHK': u'Chuukese',
|
||||
u'CHM': u'Mari',
|
||||
u'CHN': u'Chinook jargon',
|
||||
u'CHO': u'Choctaw',
|
||||
u'CHP': u'Chipewyan',
|
||||
u'CHR': u'Cherokee',
|
||||
u'CHU': u'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic',
|
||||
u'CHV': u'Chuvash',
|
||||
u'CHY': u'Cheyenne',
|
||||
u'MSA': u'Malay',
|
||||
u'III': u'Sichuan Yi',
|
||||
u'ACE': u'Achinese',
|
||||
u'IBO': u'Igbo',
|
||||
u'IBA': u'Iban',
|
||||
u'XHO': u'Xhosa',
|
||||
u'DEU': u'German',
|
||||
u'CAT': u'Catalan; Valencian',
|
||||
u'DEL': u'Delaware',
|
||||
u'DEN': u'Slave (Athapascan)',
|
||||
u'CAD': u'Caddo',
|
||||
u'TAT': u'Tatar',
|
||||
u'RAJ': u'Rajasthani',
|
||||
u'SPA': u'Spanish; Castilian',
|
||||
u'TAM': u'Tamil',
|
||||
u'TAH': u'Tahitian',
|
||||
u'AFH': u'Afrihili',
|
||||
u'ENG': u'English',
|
||||
u'CSB': u'Kashubian',
|
||||
u'NYN': u'Nyankole',
|
||||
u'NYO': u'Nyoro',
|
||||
u'SID': u'Sidamo',
|
||||
u'NYA': u'Chichewa; Chewa; Nyanja',
|
||||
u'SIN': u'Sinhala; Sinhalese',
|
||||
u'AFR': u'Afrikaans',
|
||||
u'LAM': u'Lamba',
|
||||
u'SND': u'Sindhi',
|
||||
u'MAR': u'Marathi',
|
||||
u'LAH': u'Lahnda',
|
||||
u'NYM': u'Nyamwezi',
|
||||
u'SNA': u'Shona',
|
||||
u'LAD': u'Ladino',
|
||||
u'SNK': u'Soninke',
|
||||
u'MAD': u'Madurese',
|
||||
u'MAG': u'Magahi',
|
||||
u'MAI': u'Maithili',
|
||||
u'MAH': u'Marshallese',
|
||||
u'LAV': u'Latvian',
|
||||
u'MAL': u'Malayalam',
|
||||
u'MAN': u'Mandingo',
|
||||
u'ZND': u'Zande',
|
||||
u'ZEN': u'Zenaga',
|
||||
u'KBD': u'Kabardian',
|
||||
u'ITA': u'Italian',
|
||||
u'VAI': u'Vai',
|
||||
u'TSN': u'Tswana',
|
||||
u'TSO': u'Tsonga',
|
||||
u'TSI': u'Tsimshian',
|
||||
u'BYN': u'Blin; Bilin',
|
||||
u'FIJ': u'Fijian',
|
||||
u'FIN': u'Finnish',
|
||||
u'EUS': u'Basque',
|
||||
u'CEB': u'Cebuano',
|
||||
u'DAN': u'Danish',
|
||||
u'NOG': u'Nogai',
|
||||
u'NOB': u'Norwegian Bokmål; Bokmål, Norwegian',
|
||||
u'DAK': u'Dakota',
|
||||
u'CES': u'Czech',
|
||||
u'DAR': u'Dargwa',
|
||||
u'DAY': u'Dayak',
|
||||
u'NOR': u'Norwegian',
|
||||
u'KPE': u'Kpelle',
|
||||
u'GUJ': u'Gujarati',
|
||||
u'MDF': u'Moksha',
|
||||
u'MAS': u'Masai',
|
||||
u'LAO': u'Lao',
|
||||
u'MDR': u'Mandar',
|
||||
u'GON': u'Gondi',
|
||||
u'SMS': u'Skolt Sami',
|
||||
u'SMO': u'Samoan',
|
||||
u'SMN': u'Inari Sami',
|
||||
u'SMJ': u'Lule Sami',
|
||||
u'GOT': u'Gothic',
|
||||
u'SME': u'Northern Sami',
|
||||
u'BLA': u'Siksika',
|
||||
u'SMA': u'Southern Sami',
|
||||
u'GOR': u'Gorontalo',
|
||||
u'AST': u'Asturian; Bable',
|
||||
u'ORM': u'Oromo',
|
||||
u'QUE': u'Quechua',
|
||||
u'ORI': u'Oriya',
|
||||
u'CRH': u'Crimean Tatar; Crimean Turkish',
|
||||
u'ASM': u'Assamese',
|
||||
u'PUS': u'Pushto',
|
||||
u'DGR': u'Dogrib',
|
||||
u'LTZ': u'Luxembourgish; Letzeburgesch',
|
||||
u'NDO': u'Ndonga',
|
||||
u'GEZ': u'Geez',
|
||||
u'ISL': u'Icelandic',
|
||||
u'LAT': u'Latin',
|
||||
u'MAK': u'Makasar',
|
||||
u'ZAP': u'Zapotec',
|
||||
u'YID': u'Yiddish',
|
||||
u'KOK': u'Konkani',
|
||||
u'KOM': u'Komi',
|
||||
u'KON': u'Kongo',
|
||||
u'UKR': u'Ukrainian',
|
||||
u'TON': u'Tonga (Tonga Islands)',
|
||||
u'KOS': u'Kosraean',
|
||||
u'KOR': u'Korean',
|
||||
u'TOG': u'Tonga (Nyasa)',
|
||||
u'HUN': u'Hungarian',
|
||||
u'HUP': u'Hupa',
|
||||
u'CYM': u'Welsh',
|
||||
u'UDM': u'Udmurt',
|
||||
u'BEJ': u'Beja',
|
||||
u'BEN': u'Bengali',
|
||||
u'BEL': u'Belarusian',
|
||||
u'BEM': u'Bemba',
|
||||
u'AAR': u'Afar',
|
||||
u'NZI': u'Nzima',
|
||||
u'SAH': u'Yakut',
|
||||
u'SAN': u'Sanskrit',
|
||||
u'SAM': u'Samaritan Aramaic',
|
||||
u'SAG': u'Sango',
|
||||
u'SAD': u'Sandawe',
|
||||
u'RAR': u'Rarotongan',
|
||||
u'RAP': u'Rapanui',
|
||||
u'SAS': u'Sasak',
|
||||
u'SAT': u'Santali',
|
||||
u'MIN': u'Minangkabau',
|
||||
u'LIM': u'Limburgan; Limburger; Limburgish',
|
||||
u'LIN': u'Lingala',
|
||||
u'LIT': u'Lithuanian',
|
||||
u'EFI': u'Efik',
|
||||
u'BTK': u'Batak (Indonesia)',
|
||||
u'KAC': u'Kachin',
|
||||
u'KAB': u'Kabyle',
|
||||
u'KAA': u'Kara-Kalpak',
|
||||
u'KAN': u'Kannada',
|
||||
u'KAM': u'Kamba',
|
||||
u'KAL': u'Kalaallisut; Greenlandic',
|
||||
u'KAS': u'Kashmiri',
|
||||
u'KAR': u'Karen',
|
||||
u'KAU': u'Kanuri',
|
||||
u'KAT': u'Georgian',
|
||||
u'KAZ': u'Kazakh',
|
||||
u'TYV': u'Tuvinian',
|
||||
u'AWA': u'Awadhi',
|
||||
u'URD': u'Urdu',
|
||||
u'DOI': u'Dogri',
|
||||
u'TPI': u'Tok Pisin',
|
||||
u'MRI': u'Maori',
|
||||
u'ABK': u'Abkhazian',
|
||||
u'TKL': u'Tokelau',
|
||||
u'NLD': u'Dutch; Flemish',
|
||||
u'OJI': u'Ojibwa',
|
||||
u'OCI': u'Occitan (post 1500); Provençal',
|
||||
u'WOL': u'Wolof',
|
||||
u'JAV': u'Javanese',
|
||||
u'HRV': u'Croatian',
|
||||
u'DYU': u'Dyula',
|
||||
u'SSW': u'Swati',
|
||||
u'MUL': u'Multiple languages',
|
||||
u'HIL': u'Hiligaynon',
|
||||
u'HIM': u'Himachali',
|
||||
u'HIN': u'Hindi',
|
||||
u'BAS': u'Basa',
|
||||
u'GBA': u'Gbaya',
|
||||
u'WLN': u'Walloon',
|
||||
u'BAD': u'Banda',
|
||||
u'NEP': u'Nepali',
|
||||
u'CRE': u'Cree',
|
||||
u'BAN': u'Balinese',
|
||||
u'BAL': u'Baluchi',
|
||||
u'BAM': u'Bambara',
|
||||
u'BAK': u'Bashkir',
|
||||
u'SHN': u'Shan',
|
||||
u'ARP': u'Arapaho',
|
||||
u'ARW': u'Arawak',
|
||||
u'ARA': u'Arabic',
|
||||
u'ARC': u'Aramaic',
|
||||
u'ARG': u'Aragonese',
|
||||
u'SEL': u'Selkup',
|
||||
u'ARN': u'Araucanian',
|
||||
u'LUS': u'Lushai',
|
||||
u'MUS': u'Creek',
|
||||
u'LUA': u'Luba-Lulua',
|
||||
u'LUB': u'Luba-Katanga',
|
||||
u'LUG': u'Ganda',
|
||||
u'LUI': u'Luiseno',
|
||||
u'LUN': u'Lunda',
|
||||
u'LUO': u'Luo (Kenya and Tanzania)',
|
||||
u'IKU': u'Inuktitut',
|
||||
u'TUR': u'Turkish',
|
||||
u'TUK': u'Turkmen',
|
||||
u'TUM': u'Tumbuka',
|
||||
u'COP': u'Coptic',
|
||||
u'COS': u'Corsican',
|
||||
u'COR': u'Cornish',
|
||||
u'ILO': u'Iloko',
|
||||
u'GWI': u'Gwich´in',
|
||||
u'TLI': u'Tlingit',
|
||||
u'TLH': u'Klingon; tlhIngan-Hol',
|
||||
u'POR': u'Portuguese',
|
||||
u'PON': u'Pohnpeian',
|
||||
u'POL': u'Polish',
|
||||
u'TGK': u'Tajik',
|
||||
u'TGL': u'Tagalog',
|
||||
u'FRA': u'French',
|
||||
u'BHO': u'Bhojpuri',
|
||||
u'SWA': u'Swahili',
|
||||
u'DUA': u'Duala',
|
||||
u'SWE': u'Swedish',
|
||||
u'YAP': u'Yapese',
|
||||
u'TIV': u'Tiv',
|
||||
u'YAO': u'Yao',
|
||||
u'XAL': u'Kalmyk',
|
||||
u'FRY': u'Frisian',
|
||||
u'GAY': u'Gayo',
|
||||
u'OTA': u'Turkish, Ottoman (1500-1928)',
|
||||
u'HMN': u'Hmong',
|
||||
u'HMO': u'Hiri Motu',
|
||||
u'GAA': u'Ga',
|
||||
u'FUR': u'Friulian',
|
||||
u'MLG': u'Malagasy',
|
||||
u'SLV': u'Slovenian',
|
||||
u'FIL': u'Filipino; Pilipino',
|
||||
u'MLT': u'Maltese',
|
||||
u'SLK': u'Slovak',
|
||||
u'FUL': u'Fulah',
|
||||
u'JPN': u'Japanese',
|
||||
u'VOL': u'Volapük',
|
||||
u'VOT': u'Votic',
|
||||
u'IND': u'Indonesian',
|
||||
u'AVE': u'Avestan',
|
||||
u'JPR': u'Judeo-Persian',
|
||||
u'AVA': u'Avaric',
|
||||
u'PAP': u'Papiamento',
|
||||
u'EWO': u'Ewondo',
|
||||
u'PAU': u'Palauan',
|
||||
u'EWE': u'Ewe',
|
||||
u'PAG': u'Pangasinan',
|
||||
u'PAM': u'Pampanga',
|
||||
u'PAN': u'Panjabi; Punjabi',
|
||||
u'KIR': u'Kirghiz',
|
||||
u'NIA': u'Nias',
|
||||
u'KIK': u'Kikuyu; Gikuyu',
|
||||
u'SYR': u'Syriac',
|
||||
u'KIN': u'Kinyarwanda',
|
||||
u'NIU': u'Niuean',
|
||||
u'EPO': u'Esperanto',
|
||||
u'JBO': u'Lojban',
|
||||
u'MIC': u'Mi\'kmaq; Micmac',
|
||||
u'THA': u'Thai',
|
||||
u'HAI': u'Haida',
|
||||
u'ELL': u'Greek, Modern (1453-)',
|
||||
u'ADY': u'Adyghe; Adygei',
|
||||
u'ELX': u'Elamite',
|
||||
u'ADA': u'Adangme',
|
||||
u'GRB': u'Grebo',
|
||||
u'HAT': u'Haitian; Haitian Creole',
|
||||
u'HAU': u'Hausa',
|
||||
u'HAW': u'Hawaiian',
|
||||
u'BIN': u'Bini',
|
||||
u'AMH': u'Amharic',
|
||||
u'BIK': u'Bikol',
|
||||
u'BIH': u'Bihari',
|
||||
u'MOS': u'Mossi',
|
||||
u'MOH': u'Mohawk',
|
||||
u'MON': u'Mongolian',
|
||||
u'MOL': u'Moldavian',
|
||||
u'BIS': u'Bislama',
|
||||
u'TVL': u'Tuvalu',
|
||||
u'IJO': u'Ijo',
|
||||
u'EST': u'Estonian',
|
||||
u'KMB': u'Kimbundu',
|
||||
u'UMB': u'Umbundu',
|
||||
u'TMH': u'Tamashek',
|
||||
u'FON': u'Fon',
|
||||
u'HSB': u'Upper Sorbian',
|
||||
u'RUN': u'Rundi',
|
||||
u'RUS': u'Russian',
|
||||
u'PLI': u'Pali',
|
||||
u'SRD': u'Sardinian',
|
||||
u'ACH': u'Acoli',
|
||||
u'NDE': u'Ndebele, North; North Ndebele',
|
||||
u'DZO': u'Dzongkha',
|
||||
u'KRU': u'Kurukh',
|
||||
u'SRR': u'Serer',
|
||||
u'IDO': u'Ido',
|
||||
u'SRP': u'Serbian',
|
||||
u'KRO': u'Kru',
|
||||
u'KRC': u'Karachay-Balkar',
|
||||
u'NDS': u'Low German; Low Saxon; German, Low; Saxon, Low',
|
||||
u'ZUN': u'Zuni',
|
||||
u'ZUL': u'Zulu',
|
||||
u'TWI': u'Twi',
|
||||
u'NSO': u'Northern Sotho, Pedi; Sepedi',
|
||||
u'SOM': u'Somali',
|
||||
u'SON': u'Songhai',
|
||||
u'SOT': u'Sotho, Southern',
|
||||
u'MKD': u'Macedonian',
|
||||
u'HER': u'Herero',
|
||||
u'LOL': u'Mongo',
|
||||
u'HEB': u'Hebrew',
|
||||
u'LOZ': u'Lozi',
|
||||
u'GIL': u'Gilbertese',
|
||||
u'WAS': u'Washo',
|
||||
u'WAR': u'Waray',
|
||||
u'BUL': u'Bulgarian',
|
||||
u'WAL': u'Walamo',
|
||||
u'BUA': u'Buriat',
|
||||
u'BUG': u'Buginese',
|
||||
u'AZE': u'Azerbaijani',
|
||||
u'ZHA': u'Zhuang; Chuang',
|
||||
u'ZHO': u'Chinese',
|
||||
u'NNO': u'Norwegian Nynorsk; Nynorsk, Norwegian',
|
||||
u'UIG': u'Uighur; Uyghur',
|
||||
u'MYV': u'Erzya',
|
||||
u'INH': u'Ingush',
|
||||
u'KHM': u'Khmer',
|
||||
u'MYA': u'Burmese',
|
||||
u'KHA': u'Khasi',
|
||||
u'INA': u'Interlingua (International Auxiliary Language Association)',
|
||||
u'NAH': u'Nahuatl',
|
||||
u'TIR': u'Tigrinya',
|
||||
u'NAP': u'Neapolitan',
|
||||
u'NAV': u'Navajo; Navaho',
|
||||
u'NAU': u'Nauru',
|
||||
u'GRN': u'Guarani',
|
||||
u'TIG': u'Tigre',
|
||||
u'YOR': u'Yoruba',
|
||||
u'ILE': u'Interlingue',
|
||||
u'SQI': u'Albanian',
|
||||
}
|
||||
|
||||
# EOF
|
||||
@@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__revision__ = '$Id: releasetypenames.py 8728 2006-12-17 23:42:30Z luks $'
|
||||
|
||||
releaseTypeNames = {
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#None': u'None',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Album': u'Album',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Single': u'Single',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#EP': u'EP',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Compilation': u'Compilation',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Soundtrack': u'Soundtrack',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Spokenword': u'Spokenword',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Interview': u'Interview',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Audiobook': u'Audiobook',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Live': u'Live',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Remix': u'Remix',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Other': u'Other',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Official': u'Official',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Promotion': u'Promotion',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Bootleg': u'Bootleg',
|
||||
u'http://musicbrainz.org/ns/mmd-1.0#Pseudo-Release': u'Pseudo-Release',
|
||||
}
|
||||
|
||||
# EOF
|
||||
@@ -1,59 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__revision__ = '$Id: scriptnames.py 7386 2006-04-30 11:12:55Z matt $'
|
||||
|
||||
scriptNames = {
|
||||
u'Yiii': u'Yi',
|
||||
u'Telu': u'Telugu',
|
||||
u'Taml': u'Tamil',
|
||||
u'Guru': u'Gurmukhi',
|
||||
u'Hebr': u'Hebrew',
|
||||
u'Geor': u'Georgian (Mkhedruli)',
|
||||
u'Ugar': u'Ugaritic',
|
||||
u'Cyrl': u'Cyrillic',
|
||||
u'Hrkt': u'Kanji & Kana',
|
||||
u'Armn': u'Armenian',
|
||||
u'Runr': u'Runic',
|
||||
u'Khmr': u'Khmer',
|
||||
u'Latn': u'Latin',
|
||||
u'Hani': u'Han (Hanzi, Kanji, Hanja)',
|
||||
u'Ital': u'Old Italic (Etruscan, Oscan, etc.)',
|
||||
u'Hano': u'Hanunoo (Hanunóo)',
|
||||
u'Ethi': u'Ethiopic (Ge\'ez)',
|
||||
u'Gujr': u'Gujarati',
|
||||
u'Hang': u'Hangul',
|
||||
u'Arab': u'Arabic',
|
||||
u'Thaa': u'Thaana',
|
||||
u'Buhd': u'Buhid',
|
||||
u'Sinh': u'Sinhala',
|
||||
u'Orya': u'Oriya',
|
||||
u'Hans': u'Han (Simplified variant)',
|
||||
u'Thai': u'Thai',
|
||||
u'Cprt': u'Cypriot',
|
||||
u'Linb': u'Linear B',
|
||||
u'Hant': u'Han (Traditional variant)',
|
||||
u'Osma': u'Osmanya',
|
||||
u'Mong': u'Mongolian',
|
||||
u'Deva': u'Devanagari (Nagari)',
|
||||
u'Laoo': u'Lao',
|
||||
u'Tagb': u'Tagbanwa',
|
||||
u'Hira': u'Hiragana',
|
||||
u'Bopo': u'Bopomofo',
|
||||
u'Goth': u'Gothic',
|
||||
u'Tale': u'Tai Le',
|
||||
u'Mymr': u'Myanmar (Burmese)',
|
||||
u'Tglg': u'Tagalog',
|
||||
u'Grek': u'Greek',
|
||||
u'Mlym': u'Malayalam',
|
||||
u'Cher': u'Cherokee',
|
||||
u'Tibt': u'Tibetan',
|
||||
u'Kana': u'Katakana',
|
||||
u'Syrc': u'Syriac',
|
||||
u'Cans': u'Unified Canadian Aboriginal Syllabics',
|
||||
u'Beng': u'Bengali',
|
||||
u'Limb': u'Limbu',
|
||||
u'Ogam': u'Ogham',
|
||||
u'Knda': u'Kannada',
|
||||
}
|
||||
|
||||
# EOF
|
||||
@@ -1,221 +0,0 @@
|
||||
"""Utilities for working with Audio CDs.
|
||||
|
||||
This module contains utilities for working with Audio CDs.
|
||||
|
||||
The functions in this module need both a working ctypes package (already
|
||||
included in python-2.5) and an installed libdiscid. If you don't have
|
||||
libdiscid, it can't be loaded, or your platform isn't supported by either
|
||||
ctypes or this module, a C{NotImplementedError} is raised when using the
|
||||
L{readDisc()} function.
|
||||
|
||||
@author: Matthias Friedrich <matt@mafr.de>
|
||||
"""
|
||||
__revision__ = '$Id: disc.py 11987 2009-08-22 11:57:51Z matt $'
|
||||
|
||||
import sys
|
||||
import urllib
|
||||
import urlparse
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
from lib.musicbrainz2.model import Disc
|
||||
|
||||
__all__ = [ 'DiscError', 'readDisc', 'getSubmissionUrl' ]
|
||||
|
||||
|
||||
class DiscError(IOError):
|
||||
"""The Audio CD could not be read.
|
||||
|
||||
This may be simply because no disc was in the drive, the device name
|
||||
was wrong or the disc can't be read. Reading errors can occur in case
|
||||
of a damaged disc or a copy protection mechanism, for example.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def _openLibrary():
|
||||
"""Tries to open libdiscid.
|
||||
|
||||
@return: a C{ctypes.CDLL} object, representing the opened library
|
||||
|
||||
@raise NotImplementedError: if the library can't be opened
|
||||
"""
|
||||
# This only works for ctypes >= 0.9.9.3. Any libdiscid is found,
|
||||
# no matter how it's called on this platform.
|
||||
try:
|
||||
if hasattr(ctypes.cdll, 'find'):
|
||||
libDiscId = ctypes.cdll.find('discid')
|
||||
_setPrototypes(libDiscId)
|
||||
return libDiscId
|
||||
except OSError, e:
|
||||
raise NotImplementedError('Error opening library: ' + str(e))
|
||||
|
||||
# Try to find the library using ctypes.util
|
||||
libName = ctypes.util.find_library('discid')
|
||||
if libName != None:
|
||||
try:
|
||||
libDiscId = ctypes.cdll.LoadLibrary(libName)
|
||||
_setPrototypes(libDiscId)
|
||||
return libDiscId
|
||||
except OSError, e:
|
||||
raise NotImplementedError('Error opening library: ' +
|
||||
str(e))
|
||||
|
||||
# For compatibility with ctypes < 0.9.9.3 try to figure out the library
|
||||
# name without the help of ctypes. We use cdll.LoadLibrary() below,
|
||||
# which isn't available for ctypes == 0.9.9.3.
|
||||
#
|
||||
if sys.platform == 'linux2':
|
||||
libName = 'libdiscid.so.0'
|
||||
elif sys.platform == 'darwin':
|
||||
libName = 'libdiscid.0.dylib'
|
||||
elif sys.platform == 'win32':
|
||||
libName = 'discid.dll'
|
||||
else:
|
||||
# This should at least work for Un*x-style operating systems
|
||||
libName = 'libdiscid.so.0'
|
||||
|
||||
try:
|
||||
libDiscId = ctypes.cdll.LoadLibrary(libName)
|
||||
_setPrototypes(libDiscId)
|
||||
return libDiscId
|
||||
except OSError, e:
|
||||
raise NotImplementedError('Error opening library: ' + str(e))
|
||||
|
||||
assert False # not reached
|
||||
|
||||
|
||||
def _setPrototypes(libDiscId):
|
||||
ct = ctypes
|
||||
libDiscId.discid_new.argtypes = ( )
|
||||
libDiscId.discid_new.restype = ct.c_void_p
|
||||
|
||||
libDiscId.discid_free.argtypes = (ct.c_void_p, )
|
||||
|
||||
libDiscId.discid_read.argtypes = (ct.c_void_p, ct.c_char_p)
|
||||
|
||||
libDiscId.discid_get_error_msg.argtypes = (ct.c_void_p, )
|
||||
libDiscId.discid_get_error_msg.restype = ct.c_char_p
|
||||
|
||||
libDiscId.discid_get_id.argtypes = (ct.c_void_p, )
|
||||
libDiscId.discid_get_id.restype = ct.c_char_p
|
||||
|
||||
libDiscId.discid_get_first_track_num.argtypes = (ct.c_void_p, )
|
||||
libDiscId.discid_get_first_track_num.restype = ct.c_int
|
||||
|
||||
libDiscId.discid_get_last_track_num.argtypes = (ct.c_void_p, )
|
||||
libDiscId.discid_get_last_track_num.restype = ct.c_int
|
||||
|
||||
libDiscId.discid_get_sectors.argtypes = (ct.c_void_p, )
|
||||
libDiscId.discid_get_sectors.restype = ct.c_int
|
||||
|
||||
libDiscId.discid_get_track_offset.argtypes = (ct.c_void_p, ct.c_int)
|
||||
libDiscId.discid_get_track_offset.restype = ct.c_int
|
||||
|
||||
libDiscId.discid_get_track_length.argtypes = (ct.c_void_p, ct.c_int)
|
||||
libDiscId.discid_get_track_length.restype = ct.c_int
|
||||
|
||||
|
||||
def getSubmissionUrl(disc, host='mm.musicbrainz.org', port=80):
|
||||
"""Returns a URL for adding a disc to the MusicBrainz database.
|
||||
|
||||
A fully initialized L{musicbrainz2.model.Disc} object is needed, as
|
||||
returned by L{readDisc}. A disc object returned by the web service
|
||||
doesn't provide the necessary information.
|
||||
|
||||
Note that the created URL is intended for interactive use and points
|
||||
to the MusicBrainz disc submission wizard by default. This method
|
||||
just returns a URL, no network connection is needed. The disc drive
|
||||
isn't used.
|
||||
|
||||
@param disc: a fully initialized L{musicbrainz2.model.Disc} object
|
||||
@param host: a string containing a host name
|
||||
@param port: an integer containing a port number
|
||||
|
||||
@return: a string containing the submission URL
|
||||
|
||||
@see: L{readDisc}
|
||||
"""
|
||||
assert isinstance(disc, Disc), 'musicbrainz2.model.Disc expected'
|
||||
discid = disc.getId()
|
||||
first = disc.getFirstTrackNum()
|
||||
last = disc.getLastTrackNum()
|
||||
sectors = disc.getSectors()
|
||||
assert None not in (discid, first, last, sectors)
|
||||
|
||||
tracks = last - first + 1
|
||||
toc = "%d %d %d " % (first, last, sectors)
|
||||
toc = toc + ' '.join( map(lambda x: str(x[0]), disc.getTracks()) )
|
||||
|
||||
query = urllib.urlencode({ 'id': discid, 'toc': toc, 'tracks': tracks })
|
||||
|
||||
if port == 80:
|
||||
netloc = host
|
||||
else:
|
||||
netloc = host + ':' + str(port)
|
||||
|
||||
url = ('http', netloc, '/bare/cdlookup.html', '', query, '')
|
||||
|
||||
return urlparse.urlunparse(url)
|
||||
|
||||
|
||||
def readDisc(deviceName=None):
|
||||
"""Reads an Audio CD in the disc drive.
|
||||
|
||||
This reads a CD's table of contents (TOC) and calculates the MusicBrainz
|
||||
DiscID, which is a 28 character ASCII string. This DiscID can be used
|
||||
to retrieve a list of matching releases from the web service (see
|
||||
L{musicbrainz2.webservice.Query}).
|
||||
|
||||
Note that an Audio CD has to be in drive for this to work. The
|
||||
C{deviceName} argument may be used to set the device. The default
|
||||
depends on the operating system (on linux, it's C{'/dev/cdrom'}).
|
||||
No network connection is needed for this function.
|
||||
|
||||
If the device doesn't exist or there's no valid Audio CD in the drive,
|
||||
a L{DiscError} exception is raised.
|
||||
|
||||
@param deviceName: a string containing the CD drive's device name
|
||||
|
||||
@return: a L{musicbrainz2.model.Disc} object
|
||||
|
||||
@raise DiscError: if there was a problem reading the disc
|
||||
@raise NotImplementedError: if DiscID generation isn't supported
|
||||
"""
|
||||
libDiscId = _openLibrary()
|
||||
|
||||
handle = libDiscId.discid_new()
|
||||
assert handle != 0, "libdiscid: discid_new() returned NULL"
|
||||
|
||||
# Access the CD drive. This also works if deviceName is None because
|
||||
# ctypes passes a NULL pointer in this case.
|
||||
#
|
||||
res = libDiscId.discid_read(handle, deviceName)
|
||||
if res == 0:
|
||||
raise DiscError(libDiscId.discid_get_error_msg(handle))
|
||||
|
||||
|
||||
# Now extract the data from the result.
|
||||
#
|
||||
disc = Disc()
|
||||
|
||||
disc.setId( libDiscId.discid_get_id(handle) )
|
||||
|
||||
firstTrackNum = libDiscId.discid_get_first_track_num(handle)
|
||||
lastTrackNum = libDiscId.discid_get_last_track_num(handle)
|
||||
|
||||
disc.setSectors(libDiscId.discid_get_sectors(handle))
|
||||
|
||||
for i in range(firstTrackNum, lastTrackNum+1):
|
||||
trackOffset = libDiscId.discid_get_track_offset(handle, i)
|
||||
trackSectors = libDiscId.discid_get_track_length(handle, i)
|
||||
|
||||
disc.addTrack( (trackOffset, trackSectors) )
|
||||
|
||||
disc.setFirstTrackNum(firstTrackNum)
|
||||
disc.setLastTrackNum(lastTrackNum)
|
||||
|
||||
libDiscId.discid_free(handle)
|
||||
|
||||
return disc
|
||||
|
||||
# EOF
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,204 +0,0 @@
|
||||
"""Various utilities to simplify common tasks.
|
||||
|
||||
This module contains helper functions to make common tasks easier.
|
||||
|
||||
@author: Matthias Friedrich <matt@mafr.de>
|
||||
"""
|
||||
__revision__ = '$Id: utils.py 11853 2009-07-21 09:26:50Z luks $'
|
||||
|
||||
import re
|
||||
import urlparse
|
||||
import os.path
|
||||
|
||||
__all__ = [
|
||||
'extractUuid', 'extractFragment', 'extractEntityType',
|
||||
'getReleaseTypeName', 'getCountryName', 'getLanguageName',
|
||||
'getScriptName',
|
||||
]
|
||||
|
||||
|
||||
# A pattern to split the path part of an absolute MB URI.
|
||||
PATH_PATTERN = '^/(artist|release|track|label|release-group)/([^/]*)$'
|
||||
|
||||
|
||||
def extractUuid(uriStr, resType=None):
|
||||
"""Extract the UUID part from a MusicBrainz identifier.
|
||||
|
||||
This function takes a MusicBrainz ID (an absolute URI) as the input
|
||||
and returns the UUID part of the URI, thus turning it into a relative
|
||||
URI. If C{uriStr} is None or a relative URI, then it is returned
|
||||
unchanged.
|
||||
|
||||
The C{resType} parameter can be used for error checking. Set it to
|
||||
'artist', 'release', or 'track' to make sure C{uriStr} is a
|
||||
syntactically valid MusicBrainz identifier of the given resource
|
||||
type. If it isn't, a C{ValueError} exception is raised.
|
||||
This error checking only works if C{uriStr} is an absolute URI, of
|
||||
course.
|
||||
|
||||
Example:
|
||||
|
||||
>>> from musicbrainz2.utils import extractUuid
|
||||
>>> extractUuid('http://musicbrainz.org/artist/c0b2500e-0cef-4130-869d-732b23ed9df5', 'artist')
|
||||
'c0b2500e-0cef-4130-869d-732b23ed9df5'
|
||||
>>>
|
||||
|
||||
@param uriStr: a string containing a MusicBrainz ID (an URI), or None
|
||||
@param resType: a string containing a resource type
|
||||
|
||||
@return: a string containing a relative URI, or None
|
||||
|
||||
@raise ValueError: the given URI is no valid MusicBrainz ID
|
||||
"""
|
||||
if uriStr is None:
|
||||
return None
|
||||
|
||||
(scheme, netloc, path) = urlparse.urlparse(uriStr)[:3]
|
||||
|
||||
if scheme == '':
|
||||
return uriStr # no URI, probably already the UUID
|
||||
|
||||
if scheme != 'http' or netloc != 'musicbrainz.org':
|
||||
raise ValueError('%s is no MB ID.' % uriStr)
|
||||
|
||||
m = re.match(PATH_PATTERN, path)
|
||||
|
||||
if m:
|
||||
if resType is None:
|
||||
return m.group(2)
|
||||
else:
|
||||
if m.group(1) == resType:
|
||||
return m.group(2)
|
||||
else:
|
||||
raise ValueError('expected "%s" Id' % resType)
|
||||
else:
|
||||
raise ValueError('%s is no valid MB ID.' % uriStr)
|
||||
|
||||
|
||||
def extractFragment(uriStr, uriPrefix=None):
|
||||
"""Extract the fragment part from a URI.
|
||||
|
||||
If C{uriStr} is None or no absolute URI, then it is returned unchanged.
|
||||
|
||||
The C{uriPrefix} parameter can be used for error checking. If C{uriStr}
|
||||
is an absolute URI, then the function checks if it starts with
|
||||
C{uriPrefix}. If it doesn't, a C{ValueError} exception is raised.
|
||||
|
||||
@param uriStr: a string containing an absolute URI
|
||||
@param uriPrefix: a string containing an URI prefix
|
||||
|
||||
@return: a string containing the fragment, or None
|
||||
|
||||
@raise ValueError: the given URI doesn't start with C{uriPrefix}
|
||||
"""
|
||||
if uriStr is None:
|
||||
return None
|
||||
|
||||
(scheme, netloc, path, params, query, frag) = urlparse.urlparse(uriStr)
|
||||
if scheme == '':
|
||||
return uriStr # this is no URI
|
||||
|
||||
if uriPrefix is None or uriStr.startswith(uriPrefix):
|
||||
return frag
|
||||
else:
|
||||
raise ValueError("prefix doesn't match URI %s" % uriStr)
|
||||
|
||||
|
||||
def extractEntityType(uriStr):
|
||||
"""Returns the entity type an entity URI is referring to.
|
||||
|
||||
@param uriStr: a string containing an absolute entity URI
|
||||
|
||||
@return: a string containing 'artist', 'release', 'track', or 'label'
|
||||
|
||||
@raise ValueError: if the given URI is no valid MusicBrainz ID
|
||||
"""
|
||||
if uriStr is None:
|
||||
raise ValueError('None is no valid entity URI')
|
||||
|
||||
(scheme, netloc, path) = urlparse.urlparse(uriStr)[:3]
|
||||
|
||||
if scheme == '':
|
||||
raise ValueError('%s is no absolute MB ID.' % uriStr)
|
||||
|
||||
if scheme != 'http' or netloc != 'musicbrainz.org':
|
||||
raise ValueError('%s is no MB ID.' % uriStr)
|
||||
|
||||
m = re.match(PATH_PATTERN, path)
|
||||
|
||||
if m:
|
||||
return m.group(1)
|
||||
else:
|
||||
raise ValueError('%s is no valid MB ID.' % uriStr)
|
||||
|
||||
|
||||
def getReleaseTypeName(releaseType):
|
||||
"""Returns the name of a release type URI.
|
||||
|
||||
@param releaseType: a string containing a release type URI
|
||||
|
||||
@return: a string containing a printable name for the release type
|
||||
|
||||
@see: L{musicbrainz2.model.Release}
|
||||
"""
|
||||
from lib.musicbrainz2.data.releasetypenames import releaseTypeNames
|
||||
return releaseTypeNames.get(releaseType)
|
||||
|
||||
|
||||
def getCountryName(id_):
|
||||
"""Returns a country's name based on an ISO-3166 country code.
|
||||
|
||||
The country table this function is based on has been modified for
|
||||
MusicBrainz purposes by using the extension mechanism defined in
|
||||
ISO-3166. All IDs are still valid ISO-3166 country codes, but some
|
||||
IDs have been added to include historic countries and some of the
|
||||
country names have been modified to make them better suited for
|
||||
display purposes.
|
||||
|
||||
If the country ID is not found, None is returned. This may happen
|
||||
for example, when new countries are added to the MusicBrainz web
|
||||
service which aren't known to this library yet.
|
||||
|
||||
@param id_: a two-letter upper case string containing an ISO-3166 code
|
||||
|
||||
@return: a string containing the country's name, or None
|
||||
|
||||
@see: L{musicbrainz2.model}
|
||||
"""
|
||||
from musicbrainz2.data.countrynames import countryNames
|
||||
return countryNames.get(id_)
|
||||
|
||||
|
||||
def getLanguageName(id_):
|
||||
"""Returns a language name based on an ISO-639-2/T code.
|
||||
|
||||
This function uses a subset of the ISO-639-2/T code table to map
|
||||
language IDs (terminologic, not bibliographic ones!) to names.
|
||||
|
||||
@param id_: a three-letter upper case string containing an ISO-639-2/T code
|
||||
|
||||
@return: a string containing the language's name, or None
|
||||
|
||||
@see: L{musicbrainz2.model}
|
||||
"""
|
||||
from musicbrainz2.data.languagenames import languageNames
|
||||
return languageNames.get(id_)
|
||||
|
||||
|
||||
def getScriptName(id_):
|
||||
"""Returns a script name based on an ISO-15924 code.
|
||||
|
||||
This function uses a subset of the ISO-15924 code table to map
|
||||
script IDs to names.
|
||||
|
||||
@param id_: a four-letter string containing an ISO-15924 script code
|
||||
|
||||
@return: a string containing the script's name, or None
|
||||
|
||||
@see: L{musicbrainz2.model}
|
||||
"""
|
||||
from musicbrainz2.data.scriptnames import scriptNames
|
||||
return scriptNames.get(id_)
|
||||
|
||||
|
||||
# EOF
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
from musicbrainz import *
|
||||
from lib.musicbrainzngs.musicbrainz import *
|
||||
|
||||
62
lib/musicbrainzngs/compat.py
Normal file
62
lib/musicbrainzngs/compat.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2012 Kenneth Reitz.
|
||||
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
"""
|
||||
pythoncompat
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
# -------
|
||||
# Pythons
|
||||
# -------
|
||||
|
||||
# Syntax sugar.
|
||||
_ver = sys.version_info
|
||||
|
||||
#: Python 2.x?
|
||||
is_py2 = (_ver[0] == 2)
|
||||
|
||||
#: Python 3.x?
|
||||
is_py3 = (_ver[0] == 3)
|
||||
|
||||
# ---------
|
||||
# Specifics
|
||||
# ---------
|
||||
|
||||
if is_py2:
|
||||
from StringIO import StringIO
|
||||
from urllib2 import HTTPPasswordMgr, HTTPDigestAuthHandler, Request,\
|
||||
HTTPHandler, build_opener, HTTPError, URLError,\
|
||||
build_opener
|
||||
from httplib import BadStatusLine, HTTPException
|
||||
from urlparse import urlunparse
|
||||
from urllib import urlencode
|
||||
|
||||
bytes = str
|
||||
unicode = unicode
|
||||
basestring = basestring
|
||||
elif is_py3:
|
||||
from io import StringIO
|
||||
from urllib.request import HTTPPasswordMgr, HTTPDigestAuthHandler, Request,\
|
||||
HTTPHandler, build_opener
|
||||
from urllib.error import HTTPError, URLError
|
||||
from http.client import HTTPException, BadStatusLine
|
||||
from urllib.parse import urlunparse, urlencode
|
||||
|
||||
unicode = str
|
||||
bytes = bytes
|
||||
basestring = (str,bytes)
|
||||
@@ -4,9 +4,11 @@
|
||||
# See the COPYING file for more information.
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
import string
|
||||
import StringIO
|
||||
import logging
|
||||
|
||||
from lib.musicbrainzngs import compat
|
||||
from lib.musicbrainzngs import util
|
||||
|
||||
try:
|
||||
from ET import fixtag
|
||||
except:
|
||||
@@ -16,7 +18,7 @@ except:
|
||||
# tag and namespace declaration, if any
|
||||
if isinstance(tag, ET.QName):
|
||||
tag = tag.text
|
||||
namespace_uri, tag = string.split(tag[1:], "}", 1)
|
||||
namespace_uri, tag = tag[1:].split("}", 1)
|
||||
prefix = namespaces.get(namespace_uri)
|
||||
if prefix is None:
|
||||
prefix = "ns%d" % len(namespaces)
|
||||
@@ -29,6 +31,7 @@ except:
|
||||
xmlns = None
|
||||
return "%s:%s" % (prefix, tag), xmlns
|
||||
|
||||
|
||||
NS_MAP = {"http://musicbrainz.org/ns/mmd-2.0#": "ws2",
|
||||
"http://musicbrainz.org/ns/ext#-2.0": "ext"}
|
||||
_log = logging.getLogger("python-musicbrainz-ngs")
|
||||
@@ -113,9 +116,7 @@ def parse_inner(inner_els, element):
|
||||
return result
|
||||
|
||||
def parse_message(message):
|
||||
s = message.read()
|
||||
f = StringIO.StringIO(s)
|
||||
tree = ET.ElementTree(file=f)
|
||||
tree = util.bytes_to_elementtree(message)
|
||||
root = tree.getroot()
|
||||
result = {}
|
||||
valid_elements = {"artist": parse_artist,
|
||||
@@ -176,7 +177,8 @@ def parse_artist_list(al):
|
||||
def parse_artist(artist):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "sort-name", "country", "user-rating", "disambiguation"]
|
||||
elements = ["name", "sort-name", "country", "user-rating",
|
||||
"disambiguation", "gender", "ipi"]
|
||||
inner_els = {"life-span": parse_artist_lifespan,
|
||||
"recording-list": parse_recording_list,
|
||||
"release-list": parse_release_list,
|
||||
@@ -199,7 +201,8 @@ def parse_label_list(ll):
|
||||
def parse_label(label):
|
||||
result = {}
|
||||
attribs = ["id", "type", "ext:score"]
|
||||
elements = ["name", "sort-name", "country", "label-code", "user-rating"]
|
||||
elements = ["name", "sort-name", "country", "label-code", "user-rating",
|
||||
"ipi", "disambiguation"]
|
||||
inner_els = {"life-span": parse_artist_lifespan,
|
||||
"release-list": parse_release_list,
|
||||
"tag-list": parse_tag_list,
|
||||
@@ -427,7 +430,7 @@ def parse_track_list(tl):
|
||||
|
||||
def parse_track(track):
|
||||
result = {}
|
||||
elements = ["position", "title"]
|
||||
elements = ["position", "title","length"] #CHANGED!!!
|
||||
inner_els = {"recording": parse_recording}
|
||||
|
||||
result.update(parse_elements(elements, track))
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
# This file is distributed under a BSD-2-Clause type license.
|
||||
# See the COPYING file for more information.
|
||||
|
||||
import urlparse
|
||||
import urllib2
|
||||
import urllib
|
||||
import mbxml
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
import logging
|
||||
import httplib
|
||||
import socket
|
||||
import xml.etree.ElementTree as etree
|
||||
from xml.parsers import expat
|
||||
import base64
|
||||
|
||||
_version = "0.3dev"
|
||||
from lib.musicbrainzngs import mbxml
|
||||
from lib.musicbrainzngs import util
|
||||
from lib.musicbrainzngs import compat
|
||||
|
||||
_version = "0.3devMODIFIED"
|
||||
_log = logging.getLogger("musicbrainzngs")
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ VALID_INCLUDES = {
|
||||
'puid': ["artists", "releases", "puids", "echoprints", "isrcs"],
|
||||
'isrc': ["artists", "releases", "puids", "echoprints", "isrcs"],
|
||||
'iswc': ["artists"],
|
||||
'collection': ['releases'],
|
||||
}
|
||||
VALID_RELEASE_TYPES = [
|
||||
"nat", "album", "single", "ep", "compilation", "soundtrack", "spokenword",
|
||||
@@ -83,29 +84,33 @@ VALID_RELEASE_STATUSES = ["official", "promotion", "bootleg", "pseudo-release"]
|
||||
VALID_SEARCH_FIELDS = {
|
||||
'artist': [
|
||||
'arid', 'artist', 'sortname', 'type', 'begin', 'end', 'comment',
|
||||
'alias', 'country', 'gender', 'tag'
|
||||
'alias', 'country', 'gender', 'tag', 'ipi', 'artistaccent'
|
||||
],
|
||||
'release-group': [
|
||||
'rgid', 'releasegroup', 'reid', 'release', 'arid', 'artist',
|
||||
'artistname', 'creditname', 'type', 'tag'
|
||||
'artistname', 'creditname', 'type', 'tag', 'releasegroupaccent',
|
||||
'releases', 'comment'
|
||||
],
|
||||
'release': [
|
||||
'reid', 'release', 'arid', 'artist', 'artistname', 'creditname',
|
||||
'type', 'status', 'tracks', 'tracksmedium', 'discids',
|
||||
'discidsmedium', 'mediums', 'date', 'asin', 'lang', 'script',
|
||||
'country', 'date', 'label', 'catno', 'barcode', 'puid'
|
||||
'country', 'date', 'label', 'catno', 'barcode', 'puid', 'comment',
|
||||
'format', 'releaseaccent', 'rgid'
|
||||
],
|
||||
'recording': [
|
||||
'rid', 'recording', 'isrc', 'arid', 'artist', 'artistname',
|
||||
'creditname', 'reid', 'release', 'type', 'status', 'tracks',
|
||||
'tracksrelease', 'dur', 'qdur', 'tnum', 'position', 'tag'
|
||||
'tracksrelease', 'dur', 'qdur', 'tnum', 'position', 'tag', 'comment',
|
||||
'country', 'date' 'format', 'recordingaccent'
|
||||
],
|
||||
'label': [
|
||||
'laid', 'label', 'sortname', 'type', 'code', 'country', 'begin',
|
||||
'end', 'comment', 'alias', 'tag'
|
||||
'end', 'comment', 'alias', 'tag', 'ipi', 'labelaccent'
|
||||
],
|
||||
'work': [
|
||||
'wid', 'work', 'iswc', 'type', 'arid', 'artist', 'alias', 'tag'
|
||||
'wid', 'work', 'iswc', 'type', 'arid', 'artist', 'alias', 'tag',
|
||||
'comment', 'workaccent'
|
||||
],
|
||||
}
|
||||
|
||||
@@ -186,9 +191,9 @@ def _check_filter_and_make_params(entity, includes, release_status=[], release_t
|
||||
the filters can be used with the given includes. Return a params
|
||||
dict that can be passed to _do_mb_query.
|
||||
"""
|
||||
if isinstance(release_status, basestring):
|
||||
if isinstance(release_status, compat.basestring):
|
||||
release_status = [release_status]
|
||||
if isinstance(release_type, basestring):
|
||||
if isinstance(release_type, compat.basestring):
|
||||
release_type = [release_type]
|
||||
_check_filter(release_status, VALID_RELEASE_STATUSES)
|
||||
_check_filter(release_type, VALID_RELEASE_TYPES)
|
||||
@@ -225,9 +230,17 @@ def auth(u, p):
|
||||
user = u
|
||||
password = p
|
||||
|
||||
def hpauth(u, p):
|
||||
"""Set the username and password to be used in subsequent queries to
|
||||
the MusicBrainz XML API that require authentication.
|
||||
"""
|
||||
global hpuser, hppassword
|
||||
hpuser = u
|
||||
hppassword = p
|
||||
|
||||
def set_useragent(app, version, contact=None):
|
||||
""" Set the User-Agent to be used for requests to the MusicBrainz webservice.
|
||||
This should be set before requests are made."""
|
||||
"""Set the User-Agent to be used for requests to the MusicBrainz webservice.
|
||||
This must be set before requests are made."""
|
||||
global _useragent, _client
|
||||
if contact is not None:
|
||||
_useragent = "%s/%s python-musicbrainz-ngs/%s ( %s )" % (app, version, _version, contact)
|
||||
@@ -237,6 +250,8 @@ def set_useragent(app, version, contact=None):
|
||||
_log.debug("set user-agent to %s" % _useragent)
|
||||
|
||||
def set_hostname(new_hostname):
|
||||
"""Set the base hostname for MusicBrainz webservice requests.
|
||||
Defaults to 'musicbrainz.org'."""
|
||||
global hostname
|
||||
hostname = new_hostname
|
||||
|
||||
@@ -244,17 +259,26 @@ def set_hostname(new_hostname):
|
||||
|
||||
limit_interval = 1.0
|
||||
limit_requests = 1
|
||||
do_rate_limit = True
|
||||
|
||||
def set_rate_limit(new_interval=1.0, new_requests=1):
|
||||
def set_rate_limit(rate_limit=True, new_interval=1.0, new_requests=1):
|
||||
"""Sets the rate limiting behavior of the module. Must be invoked
|
||||
before the first Web service call. Specify the number of requests
|
||||
(`new_requests`) that may be made per given interval
|
||||
(`new_interval`).
|
||||
before the first Web service call.
|
||||
If the `rate_limit` parameter is set to True, then only a set number
|
||||
of requests (`new_requests`) will be made per given interval
|
||||
(`new_interval`). If `rate_limit` is False, then no rate limiting
|
||||
will occur.
|
||||
"""
|
||||
global limit_interval
|
||||
global limit_requests
|
||||
global do_rate_limit
|
||||
if new_interval <= 0.0:
|
||||
raise ValueError("new_interval can't be less than 0")
|
||||
if new_requests <= 0:
|
||||
raise ValueError("new_requests can't be less than 0")
|
||||
limit_interval = new_interval
|
||||
limit_requests = new_requests
|
||||
do_rate_limit = rate_limit
|
||||
|
||||
class _rate_limit(object):
|
||||
"""A decorator that limits the rate at which the function may be
|
||||
@@ -290,6 +314,7 @@ class _rate_limit(object):
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
with self.lock:
|
||||
if do_rate_limit:
|
||||
self._update_remaining()
|
||||
|
||||
# Delay if necessary.
|
||||
@@ -302,11 +327,8 @@ class _rate_limit(object):
|
||||
self.remaining_requests -= 1.0
|
||||
return self.fun(*args, **kwargs)
|
||||
|
||||
|
||||
# Generic support for making HTTP requests.
|
||||
|
||||
# From pymb2
|
||||
class _RedirectPasswordMgr(urllib2.HTTPPasswordMgr):
|
||||
class _RedirectPasswordMgr(compat.HTTPPasswordMgr):
|
||||
def __init__(self):
|
||||
self._realms = { }
|
||||
|
||||
@@ -321,18 +343,18 @@ class _RedirectPasswordMgr(urllib2.HTTPPasswordMgr):
|
||||
# ignoring the uri parameter intentionally
|
||||
self._realms[realm] = (username, password)
|
||||
|
||||
class _DigestAuthHandler(urllib2.HTTPDigestAuthHandler):
|
||||
class _DigestAuthHandler(compat.HTTPDigestAuthHandler):
|
||||
def get_authorization (self, req, chal):
|
||||
qop = chal.get ('qop', None)
|
||||
if qop and ',' in qop and 'auth' in qop.split (','):
|
||||
chal['qop'] = 'auth'
|
||||
|
||||
return urllib2.HTTPDigestAuthHandler.get_authorization (self, req, chal)
|
||||
return compat.HTTPDigestAuthHandler.get_authorization (self, req, chal)
|
||||
|
||||
class _MusicbrainzHttpRequest(urllib2.Request):
|
||||
class _MusicbrainzHttpRequest(compat.Request):
|
||||
""" A custom request handler that allows DELETE and PUT"""
|
||||
def __init__(self, method, url, data=None):
|
||||
urllib2.Request.__init__(self, url, data)
|
||||
compat.Request.__init__(self, url, data)
|
||||
allowed_m = ["GET", "POST", "DELETE", "PUT"]
|
||||
if method not in allowed_m:
|
||||
raise ValueError("invalid method: %s" % method)
|
||||
@@ -344,7 +366,7 @@ class _MusicbrainzHttpRequest(urllib2.Request):
|
||||
|
||||
# Core (internal) functions for calling the MB API.
|
||||
|
||||
def _safe_open(opener, req, body=None, max_retries=8, retry_delay_delta=2.0):
|
||||
def _safe_open(opener, req, body=None, max_retries=3, retry_delay_delta=2.0):
|
||||
"""Open an HTTP request with a given URL opener and (optionally) a
|
||||
request body. Transient errors lead to retries. Permanent errors
|
||||
and repeated errors are translated into a small set of handleable
|
||||
@@ -362,8 +384,8 @@ def _safe_open(opener, req, body=None, max_retries=8, retry_delay_delta=2.0):
|
||||
else:
|
||||
f = opener.open(req)
|
||||
|
||||
except urllib2.HTTPError, exc:
|
||||
if exc.code in (400, 404):
|
||||
except compat.HTTPError as exc:
|
||||
if exc.code in (400, 404, 411):
|
||||
# Bad request, not found, etc.
|
||||
raise ResponseError(cause=exc)
|
||||
elif exc.code in (503, 502, 500):
|
||||
@@ -374,19 +396,23 @@ def _safe_open(opener, req, body=None, max_retries=8, retry_delay_delta=2.0):
|
||||
# retrying for now.
|
||||
_log.debug("unknown HTTP error %i" % exc.code)
|
||||
last_exc = exc
|
||||
except httplib.BadStatusLine, exc:
|
||||
except compat.BadStatusLine as exc:
|
||||
_log.debug("bad status line")
|
||||
last_exc = exc
|
||||
except httplib.HTTPException, exc:
|
||||
except compat.HTTPException as exc:
|
||||
_log.debug("miscellaneous HTTP exception: %s" % str(exc))
|
||||
last_exc = exc
|
||||
except urllib2.URLError, exc:
|
||||
except compat.URLError as exc:
|
||||
if isinstance(exc.reason, socket.error):
|
||||
code = exc.reason.errno
|
||||
if code == 104: # "Connection reset by peer."
|
||||
continue
|
||||
raise NetworkError(cause=exc)
|
||||
except IOError, exc:
|
||||
except socket.error as exc:
|
||||
if exc.errno == 104:
|
||||
continue
|
||||
raise NetworkError(cause=exc)
|
||||
except IOError as exc:
|
||||
raise NetworkError(cause=exc)
|
||||
else:
|
||||
# No exception! Yay!
|
||||
@@ -425,23 +451,23 @@ def _mb_request(path, method='GET', auth_required=False, client_required=False,
|
||||
|
||||
# Encode Unicode arguments using UTF-8.
|
||||
for key, value in args.items():
|
||||
if isinstance(value, unicode):
|
||||
if isinstance(value, compat.unicode):
|
||||
args[key] = value.encode('utf8')
|
||||
|
||||
# Construct the full URL for the request, including hostname and
|
||||
# query string.
|
||||
url = urlparse.urlunparse((
|
||||
url = compat.urlunparse((
|
||||
'http',
|
||||
hostname,
|
||||
'/ws/2/%s' % path,
|
||||
'',
|
||||
urllib.urlencode(args),
|
||||
compat.urlencode(args),
|
||||
''
|
||||
))
|
||||
_log.debug("%s request for %s" % (method, url))
|
||||
|
||||
# Set up HTTP request handler and URL opener.
|
||||
httpHandler = urllib2.HTTPHandler(debuglevel=0)
|
||||
httpHandler = compat.HTTPHandler(debuglevel=0)
|
||||
handlers = [httpHandler]
|
||||
|
||||
# Add credentials if required.
|
||||
@@ -455,14 +481,24 @@ def _mb_request(path, method='GET', auth_required=False, client_required=False,
|
||||
authHandler.add_password("musicbrainz.org", (), user, password)
|
||||
handlers.append(authHandler)
|
||||
|
||||
opener = urllib2.build_opener(*handlers)
|
||||
opener = compat.build_opener(*handlers)
|
||||
|
||||
# Make request.
|
||||
req = _MusicbrainzHttpRequest(method, url, data)
|
||||
req.add_header('User-Agent', _useragent)
|
||||
|
||||
# Add headphones credentials
|
||||
if hostname == '178.63.142.150:8181':
|
||||
base64string = base64.encodestring('%s:%s' % (hpuser, hppassword)).replace('\n', '')
|
||||
req.add_header("Authorization", "Basic %s" % base64string)
|
||||
|
||||
_log.debug("requesting with UA %s" % _useragent)
|
||||
if body:
|
||||
req.add_header('Content-Type', 'application/xml; charset=UTF-8')
|
||||
elif not data and not req.has_header('Content-Length'):
|
||||
# Explicitly indicate zero content length if no request data
|
||||
# will be sent (avoids HTTP 411 error).
|
||||
req.add_header('Content-Length', '0')
|
||||
f = _safe_open(opener, req, body)
|
||||
|
||||
# Parse the response.
|
||||
@@ -496,6 +532,8 @@ def _do_mb_query(entity, id, includes=[], params={}):
|
||||
response is parsed and returned.
|
||||
"""
|
||||
# Build arguments.
|
||||
if not isinstance(includes, list):
|
||||
includes = [includes]
|
||||
_check_includes(entity, includes)
|
||||
auth_required = _is_auth_required(entity, includes)
|
||||
args = dict(params)
|
||||
@@ -507,15 +545,28 @@ def _do_mb_query(entity, id, includes=[], params={}):
|
||||
path = '%s/%s' % (entity, id)
|
||||
return _mb_request(path, 'GET', auth_required, args=args)
|
||||
|
||||
def _do_mb_search(entity, query='', fields={}, limit=None, offset=None):
|
||||
def _do_mb_search(entity, query='', fields={},
|
||||
limit=None, offset=None, strict=False):
|
||||
"""Perform a full-text search on the MusicBrainz search server.
|
||||
`query` is a free-form query string and `fields` is a dictionary
|
||||
`query` is a lucene query string when no fields are set,
|
||||
but is escaped when any fields are given. `fields` is a dictionary
|
||||
of key/value query parameters. They keys in `fields` must be valid
|
||||
for the given entity type.
|
||||
"""
|
||||
# Encode the query terms as a Lucene query string.
|
||||
query_parts = [query.replace('\x00', '').strip()]
|
||||
for key, value in fields.iteritems():
|
||||
query_parts = []
|
||||
if query:
|
||||
clean_query = util._unicode(query)
|
||||
if fields:
|
||||
clean_query = re.sub(r'([+\-&|!(){}\[\]\^"~*?:\\])',
|
||||
r'\\\1', clean_query)
|
||||
if strict:
|
||||
query_parts.append('"%s"' % clean_query)
|
||||
else:
|
||||
query_parts.append(clean_query.lower())
|
||||
else:
|
||||
query_parts.append(clean_query)
|
||||
for key, value in fields.items():
|
||||
# Ensure this is a valid search field.
|
||||
if key not in VALID_SEARCH_FIELDS[entity]:
|
||||
raise InvalidSearchFieldError(
|
||||
@@ -523,12 +574,19 @@ def _do_mb_search(entity, query='', fields={}, limit=None, offset=None):
|
||||
)
|
||||
|
||||
# Escape Lucene's special characters.
|
||||
value = util._unicode(value)
|
||||
value = re.sub(r'([+\-&|!(){}\[\]\^"~*?:\\])', r'\\\1', value)
|
||||
value = value.replace('\x00', '').strip()
|
||||
value = value.lower() # Avoid binary operators like OR.
|
||||
if value:
|
||||
query_parts.append(u'%s:(%s)' % (key, value))
|
||||
full_query = u' '.join(query_parts).strip()
|
||||
if strict:
|
||||
query_parts.append('%s:"%s"' % (key, value))
|
||||
else:
|
||||
value = value.lower() # avoid AND / OR
|
||||
query_parts.append('%s:(%s)' % (key, value))
|
||||
if strict:
|
||||
full_query = ' AND '.join(query_parts).strip()
|
||||
else:
|
||||
full_query = ' '.join(query_parts).strip()
|
||||
|
||||
if not full_query:
|
||||
raise ValueError('at least one query term is required')
|
||||
|
||||
@@ -562,23 +620,23 @@ def _do_mb_post(path, body):
|
||||
|
||||
# Single entity by ID
|
||||
def get_artist_by_id(id, includes=[], release_status=[], release_type=[]):
|
||||
params = _check_filter_and_make_params(includes, release_status, release_type)
|
||||
params = _check_filter_and_make_params("artist", includes, release_status, release_type)
|
||||
return _do_mb_query("artist", id, includes, params)
|
||||
|
||||
def get_label_by_id(id, includes=[], release_status=[], release_type=[]):
|
||||
params = _check_filter_and_make_params(includes, release_status, release_type)
|
||||
params = _check_filter_and_make_params("label", includes, release_status, release_type)
|
||||
return _do_mb_query("label", id, includes, params)
|
||||
|
||||
def get_recording_by_id(id, includes=[], release_status=[], release_type=[]):
|
||||
params = _check_filter_and_make_params(includes, release_status, release_type)
|
||||
params = _check_filter_and_make_params("recording", includes, release_status, release_type)
|
||||
return _do_mb_query("recording", id, includes, params)
|
||||
|
||||
def get_release_by_id(id, includes=[], release_status=[], release_type=[]):
|
||||
params = _check_filter_and_make_params(includes, release_status, release_type)
|
||||
params = _check_filter_and_make_params("release", includes, release_status, release_type)
|
||||
return _do_mb_query("release", id, includes, params)
|
||||
|
||||
def get_release_group_by_id(id, includes=[], release_status=[], release_type=[]):
|
||||
params = _check_filter_and_make_params(includes, release_status, release_type)
|
||||
params = _check_filter_and_make_params("release-group", includes, release_status, release_type)
|
||||
return _do_mb_query("release-group", id, includes, params)
|
||||
|
||||
def get_work_by_id(id, includes=[]):
|
||||
@@ -587,54 +645,68 @@ def get_work_by_id(id, includes=[]):
|
||||
|
||||
# Searching
|
||||
|
||||
def search_artists(query='', limit=None, offset=None, **fields):
|
||||
"""Search for artists by a free-form `query` string and/or any of
|
||||
def search_artists(query='', limit=None, offset=None, strict=False, **fields):
|
||||
"""Search for artists by a free-form `query` string or any of
|
||||
the following keyword arguments specifying field queries:
|
||||
arid, artist, sortname, type, begin, end, comment, alias, country,
|
||||
gender, tag
|
||||
When `fields` are set, special lucene characters are escaped
|
||||
in the `query`.
|
||||
"""
|
||||
return _do_mb_search('artist', query, fields, limit, offset)
|
||||
return _do_mb_search('artist', query, fields, limit, offset, strict)
|
||||
|
||||
def search_labels(query='', limit=None, offset=None, **fields):
|
||||
"""Search for labels by a free-form `query` string and/or any of
|
||||
def search_labels(query='', limit=None, offset=None, strict=False, **fields):
|
||||
"""Search for labels by a free-form `query` string or any of
|
||||
the following keyword arguments specifying field queries:
|
||||
laid, label, sortname, type, code, country, begin, end, comment,
|
||||
alias, tag
|
||||
When `fields` are set, special lucene characters are escaped
|
||||
in the `query`.
|
||||
"""
|
||||
return _do_mb_search('label', query, fields, limit, offset)
|
||||
return _do_mb_search('label', query, fields, limit, offset, strict)
|
||||
|
||||
def search_recordings(query='', limit=None, offset=None, **fields):
|
||||
"""Search for recordings by a free-form `query` string and/or any of
|
||||
def search_recordings(query='', limit=None, offset=None, strict=False, **fields):
|
||||
"""Search for recordings by a free-form `query` string or any of
|
||||
the following keyword arguments specifying field queries:
|
||||
rid, recording, isrc, arid, artist, artistname, creditname, reid,
|
||||
release, type, status, tracks, tracksrelease, dur, qdur, tnum,
|
||||
position, tag
|
||||
When `fields` are set, special lucene characters are escaped
|
||||
in the `query`.
|
||||
"""
|
||||
return _do_mb_search('recording', query, fields, limit, offset)
|
||||
return _do_mb_search('recording', query, fields, limit, offset, strict)
|
||||
|
||||
def search_releases(query='', limit=None, offset=None, **fields):
|
||||
"""Search for releases by a free-form `query` string and/or any of
|
||||
def search_releases(query='', limit=None, offset=None, strict=False, **fields):
|
||||
"""Search for releases by a free-form `query` string or any of
|
||||
the following keyword arguments specifying field queries:
|
||||
reid, release, arid, artist, artistname, creditname, type, status,
|
||||
tracks, tracksmedium, discids, discidsmedium, mediums, date, asin,
|
||||
lang, script, country, date, label, catno, barcode, puid
|
||||
When `fields` are set, special lucene characters are escaped
|
||||
in the `query`.
|
||||
"""
|
||||
return _do_mb_search('release', query, fields, limit, offset)
|
||||
return _do_mb_search('release', query, fields, limit, offset, strict)
|
||||
|
||||
def search_release_groups(query='', limit=None, offset=None, **fields):
|
||||
"""Search for release groups by a free-form `query` string and/or
|
||||
def search_release_groups(query='', limit=None, offset=None,
|
||||
strict=False, **fields):
|
||||
"""Search for release groups by a free-form `query` string or
|
||||
any of the following keyword arguments specifying field queries:
|
||||
rgid, releasegroup, reid, release, arid, artist, artistname,
|
||||
creditname, type, tag
|
||||
When `fields` are set, special lucene characters are escaped
|
||||
in the `query`.
|
||||
"""
|
||||
return _do_mb_search('release-group', query, fields, limit, offset)
|
||||
return _do_mb_search('release-group', query, fields,
|
||||
limit, offset, strict)
|
||||
|
||||
def search_works(query='', limit=None, offset=None, **fields):
|
||||
"""Search for works by a free-form `query` string and/or any of
|
||||
def search_works(query='', limit=None, offset=None, strict=False, **fields):
|
||||
"""Search for works by a free-form `query` string or any of
|
||||
the following keyword arguments specifying field queries:
|
||||
wid, work, iswc, type, arid, artist, alias, tag
|
||||
When `fields` are set, special lucene characters are escaped
|
||||
in the `query`.
|
||||
"""
|
||||
return _do_mb_search('work', query, fields, limit, offset)
|
||||
return _do_mb_search('work', query, fields, limit, offset, strict)
|
||||
|
||||
|
||||
# Lists of entities
|
||||
@@ -721,35 +793,40 @@ def get_releases_in_collection(collection):
|
||||
# Submission methods
|
||||
|
||||
def submit_barcodes(barcodes):
|
||||
"""
|
||||
Submits a set of {release1: barcode1, release2:barcode2}
|
||||
Must call auth(user, pass) first
|
||||
"""
|
||||
"""Submits a set of {release1: barcode1, release2:barcode2}
|
||||
|
||||
Must call auth(user, pass) first"""
|
||||
query = mbxml.make_barcode_request(barcodes)
|
||||
return _do_mb_post("release", query)
|
||||
|
||||
def submit_puids(puids):
|
||||
"""Submit PUIDs.
|
||||
|
||||
Must call auth(user, pass) first"""
|
||||
query = mbxml.make_puid_request(puids)
|
||||
return _do_mb_post("recording", query)
|
||||
|
||||
def submit_echoprints(echoprints):
|
||||
"""Submit echoprints.
|
||||
|
||||
Must call auth(user, pass) first"""
|
||||
query = mbxml.make_echoprint_request(echoprints)
|
||||
return _do_mb_post("recording", query)
|
||||
|
||||
def submit_isrcs(recordings_isrcs):
|
||||
"""
|
||||
Submit ISRCs.
|
||||
Submits a set of {recording-id: [isrc1, irc1]}
|
||||
Must call auth(user, pass) first
|
||||
"""
|
||||
"""Submit ISRCs.
|
||||
Submits a set of {recording-id: [isrc1, isrc1, ...]}
|
||||
|
||||
Must call auth(user, pass) first"""
|
||||
query = mbxml.make_isrc_request(recordings_isrcs=recordings_isrcs)
|
||||
return _do_mb_post("recording", query)
|
||||
|
||||
def submit_tags(artist_tags={}, recording_tags={}):
|
||||
""" Submit user tags.
|
||||
"""Submit user tags.
|
||||
Artist or recording parameters are of the form:
|
||||
{'entityid': [taglist]}
|
||||
"""
|
||||
|
||||
Must call auth(user, pass) first"""
|
||||
query = mbxml.make_tag_request(artist_tags, recording_tags)
|
||||
return _do_mb_post("tag", query)
|
||||
|
||||
@@ -757,15 +834,24 @@ def submit_ratings(artist_ratings={}, recording_ratings={}):
|
||||
""" Submit user ratings.
|
||||
Artist or recording parameters are of the form:
|
||||
{'entityid': rating}
|
||||
"""
|
||||
|
||||
Must call auth(user, pass) first"""
|
||||
query = mbxml.make_rating_request(artist_ratings, recording_ratings)
|
||||
return _do_mb_post("rating", query)
|
||||
|
||||
def add_releases_to_collection(collection, releases=[]):
|
||||
"""Add releases to a collection.
|
||||
Collection and releases should be identified by their MBIDs
|
||||
|
||||
Must call auth(user, pass) first"""
|
||||
# XXX: Maximum URI length of 16kb means we should only allow ~400 releases
|
||||
releaselist = ";".join(releases)
|
||||
_do_mb_put("collection/%s/releases/%s" % (collection, releaselist))
|
||||
|
||||
def remove_releases_from_collection(collection, releases=[]):
|
||||
"""Remove releases from a collection.
|
||||
Collection and releases should be identified by their MBIDs
|
||||
|
||||
Must call auth(user, pass) first"""
|
||||
releaselist = ";".join(releases)
|
||||
_do_mb_delete("collection/%s/releases/%s" % (collection, releaselist))
|
||||
|
||||
37
lib/musicbrainzngs/util.py
Normal file
37
lib/musicbrainzngs/util.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# This file is part of the musicbrainzngs library
|
||||
# Copyright (C) Alastair Porter, Adrian Sampson, and others
|
||||
# This file is distributed under a BSD-2-Clause type license.
|
||||
# See the COPYING file for more information.
|
||||
|
||||
import sys
|
||||
import locale
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from . import compat
|
||||
|
||||
def _unicode(string, encoding=None):
|
||||
"""Try to decode byte strings to unicode.
|
||||
This can only be a guess, but this might be better than failing.
|
||||
It is safe to use this on numbers or strings that are already unicode.
|
||||
"""
|
||||
if isinstance(string, compat.unicode):
|
||||
unicode_string = string
|
||||
elif isinstance(string, compat.bytes):
|
||||
# use given encoding, stdin, preferred until something != None is found
|
||||
if encoding is None:
|
||||
encoding = sys.stdin.encoding
|
||||
if encoding is None:
|
||||
encoding = locale.getpreferredencoding()
|
||||
unicode_string = string.decode(encoding, "ignore")
|
||||
else:
|
||||
unicode_string = compat.unicode(string)
|
||||
return unicode_string.replace('\x00', '').strip()
|
||||
|
||||
def bytes_to_elementtree(_bytes):
|
||||
if compat.is_py3:
|
||||
s = _unicode(_bytes.read(), "utf-8")
|
||||
else:
|
||||
s = _bytes.read()
|
||||
f = compat.StringIO(s)
|
||||
tree = ET.ElementTree(file=f)
|
||||
return tree
|
||||
Reference in New Issue
Block a user