mirror of
https://github.com/fergalmoran/picard.git
synced 2026-03-06 13:34:05 +00:00
Remove mbxml
This commit is contained in:
485
picard/mbxml.py
485
picard/mbxml.py
@@ -1,485 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Picard, the next-generation MusicBrainz tagger
|
||||
# Copyright (C) 2007 Lukáš Lalinský
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import re
|
||||
from picard import config
|
||||
from picard.util import (format_time, translate_from_sortname, parse_amazon_url,
|
||||
linear_combination_of_weights)
|
||||
from picard.const import RELEASE_FORMATS
|
||||
|
||||
|
||||
_artist_rel_types = {
|
||||
"composer": "composer",
|
||||
"writer": "writer",
|
||||
"conductor": "conductor",
|
||||
"chorus master": "conductor",
|
||||
"performing orchestra": "performer:orchestra",
|
||||
"arranger": "arranger",
|
||||
"orchestrator": "arranger",
|
||||
"instrumentator": "arranger",
|
||||
"lyricist": "lyricist",
|
||||
"librettist": "lyricist",
|
||||
"remixer": "remixer",
|
||||
"producer": "producer",
|
||||
"engineer": "engineer",
|
||||
"audio": "engineer",
|
||||
#"Mastering": "engineer",
|
||||
"sound": "engineer",
|
||||
"live sound": "engineer",
|
||||
"mix": "mixer",
|
||||
#"Recording": "engineer",
|
||||
"mix-DJ": "djmixer",
|
||||
}
|
||||
|
||||
|
||||
def _decamelcase(text):
|
||||
return re.sub(r'([A-Z])', r' \1', text).strip()
|
||||
|
||||
|
||||
_REPLACE_MAP = {}
|
||||
_EXTRA_ATTRS = ['guest', 'additional', 'minor']
|
||||
_BLANK_SPECIAL_RELTYPES = {'vocal': 'vocals'}
|
||||
|
||||
|
||||
def _parse_attributes(attrs, reltype):
|
||||
attrs = [_decamelcase(_REPLACE_MAP.get(a, a)) for a in attrs]
|
||||
prefix = ' '.join([a for a in attrs if a in _EXTRA_ATTRS])
|
||||
attrs = [a for a in attrs if a not in _EXTRA_ATTRS]
|
||||
if len(attrs) > 1:
|
||||
attrs = '%s and %s' % (', '.join(attrs[:-1]), attrs[-1:][0])
|
||||
elif len(attrs) == 1:
|
||||
attrs = attrs[0]
|
||||
else:
|
||||
attrs = _BLANK_SPECIAL_RELTYPES.get(reltype, '')
|
||||
return ' '.join([prefix, attrs]).strip().lower()
|
||||
|
||||
|
||||
def _relations_to_metadata(relation_lists, m):
|
||||
use_credited_as = not config.setting["standardize_artists"]
|
||||
for relation_list in relation_lists:
|
||||
if relation_list.target_type == 'artist':
|
||||
for relation in relation_list.relation:
|
||||
artist = relation.artist[0]
|
||||
value, valuesort = _translate_artist_node(artist)
|
||||
has_translation = (value != artist.name[0].text)
|
||||
if not has_translation and use_credited_as and 'target_credit' in relation.children:
|
||||
credited_as = relation.target_credit[0].text
|
||||
if credited_as:
|
||||
value, valuesort = credited_as, credited_as
|
||||
reltype = relation.type
|
||||
attribs = []
|
||||
if 'attribute_list' in relation.children:
|
||||
attribs = [a.text for a in relation.attribute_list[0].attribute]
|
||||
if reltype in ('vocal', 'instrument', 'performer'):
|
||||
name = 'performer:' + _parse_attributes(attribs, reltype)
|
||||
elif reltype == 'mix-DJ' and len(attribs) > 0:
|
||||
if not hasattr(m, "_djmix_ars"):
|
||||
m._djmix_ars = {}
|
||||
for attr in attribs:
|
||||
m._djmix_ars.setdefault(attr.split()[1], []).append(value)
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
name = _artist_rel_types[reltype]
|
||||
except KeyError:
|
||||
continue
|
||||
if value not in m[name]:
|
||||
m.add(name, value)
|
||||
if name == 'composer' and valuesort not in m['composersort']:
|
||||
m.add('composersort', valuesort)
|
||||
elif relation_list.target_type == 'work':
|
||||
for relation in relation_list.relation:
|
||||
if relation.type == 'performance':
|
||||
performance_to_metadata(relation, m)
|
||||
work_to_metadata(relation.work[0], m)
|
||||
elif relation_list.target_type == 'url':
|
||||
for relation in relation_list.relation:
|
||||
if relation.type == 'amazon asin' and 'asin' not in m:
|
||||
amz = parse_amazon_url(relation.target[0].text)
|
||||
if amz is not None:
|
||||
m['asin'] = amz['asin']
|
||||
elif relation.type == 'license':
|
||||
url = relation.target[0].text
|
||||
m.add('license', url)
|
||||
|
||||
|
||||
def _translate_artist_node(node):
|
||||
transl, translsort = None, None
|
||||
if config.setting['translate_artist_names']:
|
||||
locale = config.setting["artist_locale"]
|
||||
lang = locale.split("_")[0]
|
||||
if "alias_list" in node.children:
|
||||
result = (-1, (None, None))
|
||||
for alias in node.alias_list[0].alias:
|
||||
if alias.attribs.get("primary") != "primary":
|
||||
continue
|
||||
if "locale" not in alias.attribs:
|
||||
continue
|
||||
parts = []
|
||||
if alias.locale == locale:
|
||||
score = 0.8
|
||||
elif alias.locale == lang:
|
||||
score = 0.6
|
||||
elif alias.locale.split("_")[0] == lang:
|
||||
score = 0.4
|
||||
else:
|
||||
continue
|
||||
parts.append((score, 5))
|
||||
if alias.attribs.get("type") == "Artist name":
|
||||
score = 0.8
|
||||
elif alias.attribs.get("type") == "Legal Name":
|
||||
score = 0.5
|
||||
else:
|
||||
# as 2014/09/19, only Artist or Legal names should have the
|
||||
# Primary flag
|
||||
score = 0.0
|
||||
parts.append((score, 5))
|
||||
comb = linear_combination_of_weights(parts)
|
||||
if comb > result[0]:
|
||||
result = (comb, (alias.text, alias.attribs["sort_name"]))
|
||||
transl, translsort = result[1]
|
||||
if not transl:
|
||||
translsort = node.sort_name[0].text
|
||||
transl = translate_from_sortname(node.name[0].text, translsort)
|
||||
else:
|
||||
transl, translsort = node.name[0].text, node.sort_name[0].text
|
||||
return (transl, translsort)
|
||||
|
||||
|
||||
def artist_credit_from_node(node):
|
||||
artist = ""
|
||||
artistsort = ""
|
||||
artists = []
|
||||
artistssort = []
|
||||
use_credited_as = not config.setting["standardize_artists"]
|
||||
for credit in node.name_credit:
|
||||
a = credit.artist[0]
|
||||
translated, translated_sort = _translate_artist_node(a)
|
||||
has_translation = (translated != a.name[0].text)
|
||||
if has_translation:
|
||||
name = translated
|
||||
elif use_credited_as and 'name' in credit.children:
|
||||
name = credit.name[0].text
|
||||
else:
|
||||
name = a.name[0].text
|
||||
artist += name
|
||||
artistsort += translated_sort
|
||||
artists.append(name)
|
||||
artistssort.append(translated_sort)
|
||||
if 'joinphrase' in credit.attribs:
|
||||
artist += credit.joinphrase
|
||||
artistsort += credit.joinphrase
|
||||
return (artist, artistsort, artists, artistssort)
|
||||
|
||||
|
||||
def artist_credit_to_metadata(node, m, release=False):
|
||||
ids = [n.artist[0].id for n in node.name_credit]
|
||||
artist, artistsort, artists, artistssort = artist_credit_from_node(node)
|
||||
if release:
|
||||
m["musicbrainz_albumartistid"] = ids
|
||||
m["albumartist"] = artist
|
||||
m["albumartistsort"] = artistsort
|
||||
m["~albumartists"] = artists
|
||||
m["~albumartists_sort"] = artistssort
|
||||
else:
|
||||
m["musicbrainz_artistid"] = ids
|
||||
m["artist"] = artist
|
||||
m["artistsort"] = artistsort
|
||||
m["artists"] = artists
|
||||
m["~artists_sort"] = artistssort
|
||||
|
||||
|
||||
def country_list_from_node(node):
|
||||
"""Extract list of country codes from `release_event_list` node.
|
||||
This is in contrast with `country` element, which has single release
|
||||
event information.
|
||||
"""
|
||||
if "release_event_list" in node.children:
|
||||
country = []
|
||||
for release_event in node.release_event_list[0].release_event:
|
||||
try:
|
||||
country.append(
|
||||
release_event.area[0].iso_3166_1_code_list[0].iso_3166_1_code[0].text)
|
||||
except AttributeError:
|
||||
pass
|
||||
return country
|
||||
|
||||
|
||||
def label_info_from_node(node):
|
||||
labels = []
|
||||
catalog_numbers = []
|
||||
if node.children:
|
||||
for label_info in node.label_info:
|
||||
if 'label' in label_info.children and label_info.label[0].children:
|
||||
label = label_info.label[0].name[0].text
|
||||
if label not in labels:
|
||||
labels.append(label)
|
||||
if 'catalog_number' in label_info.children:
|
||||
cat_num = label_info.catalog_number[0].text
|
||||
if cat_num not in catalog_numbers:
|
||||
catalog_numbers.append(cat_num)
|
||||
return (labels, catalog_numbers)
|
||||
|
||||
|
||||
def media_formats_from_node(node):
|
||||
formats_count = {}
|
||||
formats_order = []
|
||||
for medium in node.medium:
|
||||
if "format" in medium.children:
|
||||
text = medium.format[0].text
|
||||
else:
|
||||
text = "(unknown)"
|
||||
if text in formats_count:
|
||||
formats_count[text] += 1
|
||||
else:
|
||||
formats_count[text] = 1
|
||||
formats_order.append(text)
|
||||
formats = []
|
||||
for medium_format in formats_order:
|
||||
count = formats_count[medium_format]
|
||||
medium_format = RELEASE_FORMATS.get(medium_format, medium_format)
|
||||
if count > 1:
|
||||
medium_format = string_(count) + "×" + medium_format
|
||||
formats.append(medium_format)
|
||||
return " + ".join(formats)
|
||||
|
||||
|
||||
def track_to_metadata(node, track):
|
||||
m = track.metadata
|
||||
recording_to_metadata(node.recording[0], m, track)
|
||||
m.add_unique('musicbrainz_trackid', node.id)
|
||||
# overwrite with data we have on the track
|
||||
for name, nodes in node.children.items():
|
||||
if not nodes:
|
||||
continue
|
||||
if name == 'title':
|
||||
m['title'] = nodes[0].text
|
||||
elif name == 'position':
|
||||
m['tracknumber'] = nodes[0].text
|
||||
elif name == 'number':
|
||||
m['~musicbrainz_tracknumber'] = nodes[0].text
|
||||
elif name == 'length' and nodes[0].text:
|
||||
m.length = int(nodes[0].text)
|
||||
elif name == 'artist_credit':
|
||||
artist_credit_to_metadata(nodes[0], m)
|
||||
m['~length'] = format_time(m.length)
|
||||
|
||||
|
||||
def recording_to_metadata(node, m, track=None):
|
||||
m.length = 0
|
||||
m.add_unique('musicbrainz_recordingid', node.id)
|
||||
for name, nodes in node.children.items():
|
||||
if not nodes:
|
||||
continue
|
||||
if name == 'title':
|
||||
m['title'] = nodes[0].text
|
||||
m['~recordingtitle'] = nodes[0].text
|
||||
elif name == 'length' and nodes[0].text:
|
||||
m.length = int(nodes[0].text)
|
||||
elif name == 'disambiguation':
|
||||
m['~recordingcomment'] = nodes[0].text
|
||||
elif name == 'artist_credit':
|
||||
artist_credit_to_metadata(nodes[0], m)
|
||||
if 'name_credit' in nodes[0].children and track:
|
||||
for name_credit in nodes[0].name_credit:
|
||||
if 'artist' in name_credit.children:
|
||||
for artist in name_credit.artist:
|
||||
trackartist = track.append_track_artist(artist.id)
|
||||
if 'tag_list' in artist.children:
|
||||
add_folksonomy_tags(artist.tag_list[0],
|
||||
trackartist)
|
||||
if 'user_tag_list' in artist.children:
|
||||
add_user_folksonomy_tags(artist.user_tag_list[0],
|
||||
trackartist)
|
||||
elif name == 'relation_list':
|
||||
_relations_to_metadata(nodes, m)
|
||||
elif name == 'tag_list':
|
||||
add_folksonomy_tags(nodes[0], track)
|
||||
elif name == 'user_tag_list':
|
||||
add_user_folksonomy_tags(nodes[0], track)
|
||||
elif name == 'isrc_list':
|
||||
add_isrcs_to_metadata(nodes[0], m)
|
||||
elif name == 'user_rating':
|
||||
m['~rating'] = nodes[0].text
|
||||
elif name == 'video' and nodes[0].text == 'true':
|
||||
m['~video'] = '1'
|
||||
m['~length'] = format_time(m.length)
|
||||
|
||||
|
||||
def performance_to_metadata(relation, m):
|
||||
if 'attribute_list' in relation.children:
|
||||
if 'attribute' in relation.attribute_list[0].children:
|
||||
for attribute in relation.attribute_list[0].attribute:
|
||||
m.add_unique("~performance_attributes", attribute.text)
|
||||
|
||||
|
||||
def work_to_metadata(work, m):
|
||||
m.add_unique("musicbrainz_workid", work.id)
|
||||
if 'language' in work.children:
|
||||
m.add_unique("language", work.language[0].text)
|
||||
if 'title' in work.children:
|
||||
m.add_unique("work", work.title[0].text)
|
||||
if 'relation_list' in work.children:
|
||||
_relations_to_metadata(work.relation_list, m)
|
||||
|
||||
|
||||
def medium_to_metadata(node, m):
|
||||
for name, nodes in node.children.items():
|
||||
if not nodes:
|
||||
continue
|
||||
if name == 'position':
|
||||
m['discnumber'] = nodes[0].text
|
||||
elif name == 'track_list':
|
||||
m['totaltracks'] = nodes[0].count
|
||||
elif name == 'title':
|
||||
m['discsubtitle'] = nodes[0].text
|
||||
elif name == 'format':
|
||||
m['media'] = nodes[0].text
|
||||
|
||||
|
||||
def artist_to_metadata(node, m):
|
||||
"""Make meatadata dict from a XML 'artist' node."""
|
||||
m.add_unique("musicbrainz_artistid", node.id)
|
||||
for name, nodes in node.children.items():
|
||||
if not nodes:
|
||||
continue
|
||||
if name == "name":
|
||||
m["name"] = nodes[0].text
|
||||
elif name == "area":
|
||||
m["area"] = nodes[0].name[0].text
|
||||
elif name == "gender":
|
||||
m["gender"] = nodes[0].text
|
||||
elif name == "life_span":
|
||||
if "begin" in nodes[0].children:
|
||||
m["begindate"] = nodes[0].begin[0].text
|
||||
if "ended" in nodes[0].children:
|
||||
ended = nodes[0].ended[0].text
|
||||
if ended == "true" and "end" in nodes[0].children:
|
||||
m["enddate"] = nodes[0].end[0].text
|
||||
elif name == "begin_area":
|
||||
m["beginarea"] = nodes[0].name[0].text
|
||||
elif name == "end_area":
|
||||
m["endarea"] = nodes[0].name[0].text
|
||||
|
||||
try:
|
||||
m["type"] = node.type
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def release_to_metadata(node, m, album=None):
|
||||
"""Make metadata dict from a XML 'release' node."""
|
||||
m.add_unique('musicbrainz_albumid', node.id)
|
||||
for name, nodes in node.children.items():
|
||||
if not nodes:
|
||||
continue
|
||||
if name == 'status':
|
||||
m['releasestatus'] = nodes[0].text.lower()
|
||||
elif name == 'title':
|
||||
m['album'] = nodes[0].text
|
||||
elif name == 'disambiguation':
|
||||
m['~releasecomment'] = nodes[0].text
|
||||
elif name == 'asin':
|
||||
m['asin'] = nodes[0].text
|
||||
elif name == 'artist_credit':
|
||||
artist_credit_to_metadata(nodes[0], m, release=True)
|
||||
# set tags from artists
|
||||
if album is not None:
|
||||
if 'name_credit' in nodes[0].children:
|
||||
for name_credit in nodes[0].name_credit:
|
||||
if 'artist' in name_credit.children:
|
||||
for artist in name_credit.artist:
|
||||
albumartist = album.append_album_artist(artist.id)
|
||||
if 'tag_list' in artist.children:
|
||||
add_folksonomy_tags(artist.tag_list[0],
|
||||
albumartist)
|
||||
if 'user_tag_list' in artist.children:
|
||||
add_user_folksonomy_tags(artist.user_tag_list[0],
|
||||
albumartist)
|
||||
elif name == 'date':
|
||||
m['date'] = nodes[0].text
|
||||
elif name == 'country':
|
||||
m['releasecountry'] = nodes[0].text
|
||||
elif name == 'barcode':
|
||||
m['barcode'] = nodes[0].text
|
||||
elif name == 'relation_list':
|
||||
_relations_to_metadata(nodes, m)
|
||||
elif name == 'label_info_list' and nodes[0].children:
|
||||
m['label'], m['catalognumber'] = label_info_from_node(nodes[0])
|
||||
elif name == 'text_representation':
|
||||
if 'language' in nodes[0].children:
|
||||
m['~releaselanguage'] = nodes[0].language[0].text
|
||||
if 'script' in nodes[0].children:
|
||||
m['script'] = nodes[0].script[0].text
|
||||
elif name == 'tag_list':
|
||||
add_folksonomy_tags(nodes[0], album)
|
||||
elif name == 'user_tag_list':
|
||||
add_user_folksonomy_tags(nodes[0], album)
|
||||
|
||||
|
||||
def release_group_to_metadata(node, m, release_group=None):
|
||||
"""Make metadata dict from a XML 'release-group' node taken from inside a 'release' node."""
|
||||
m.add_unique('musicbrainz_releasegroupid', node.id)
|
||||
for name, nodes in node.children.items():
|
||||
if not nodes:
|
||||
continue
|
||||
if name == 'title':
|
||||
m['~releasegroup'] = nodes[0].text
|
||||
elif name == 'disambiguation':
|
||||
m['~releasegroupcomment'] = nodes[0].text
|
||||
elif name == 'first_release_date':
|
||||
m['originaldate'] = nodes[0].text
|
||||
if m['originaldate']:
|
||||
m['originalyear'] = m['originaldate'][:4]
|
||||
elif name == 'tag_list':
|
||||
add_folksonomy_tags(nodes[0], release_group)
|
||||
elif name == 'user_tag_list':
|
||||
add_user_folksonomy_tags(nodes[0], release_group)
|
||||
elif name == 'primary_type':
|
||||
m['~primaryreleasetype'] = nodes[0].text.lower()
|
||||
elif name == 'secondary_type_list':
|
||||
add_secondary_release_types(nodes[0], m)
|
||||
m['releasetype'] = m.getall('~primaryreleasetype') + m.getall('~secondaryreleasetype')
|
||||
|
||||
|
||||
def add_secondary_release_types(node, m):
|
||||
if 'secondary_type' in node.children:
|
||||
for secondary_type in node.secondary_type:
|
||||
m.add_unique('~secondaryreleasetype', secondary_type.text.lower())
|
||||
|
||||
|
||||
def add_folksonomy_tags(node, obj):
|
||||
if obj and 'tag' in node.children:
|
||||
for tag in node.tag:
|
||||
name = tag.name[0].text
|
||||
count = int(tag.attribs['count'])
|
||||
obj.add_folksonomy_tag(name, count)
|
||||
|
||||
|
||||
def add_user_folksonomy_tags(node, obj):
|
||||
if obj and 'user_tag' in node.children:
|
||||
for tag in node.user_tag:
|
||||
name = tag.name[0].text
|
||||
obj.add_folksonomy_tag(name, 1)
|
||||
|
||||
|
||||
def add_isrcs_to_metadata(node, metadata):
|
||||
if 'isrc' in node.children:
|
||||
for isrc in node.isrc:
|
||||
metadata.add('isrc', isrc.id)
|
||||
Reference in New Issue
Block a user