7 import xml.etree.ElementTree
as ET
10 from musicbrainzngs
import util
19 if isinstance(tag, ET.QName):
21 namespace_uri, tag = tag[1:].split(
"}", 1)
22 prefix = namespaces.get(namespace_uri)
24 prefix =
"ns%d" % len(namespaces)
25 namespaces[namespace_uri] = prefix
29 xmlns = (
"xmlns:%s" % prefix, namespace_uri)
32 return "%s:%s" % (prefix, tag), xmlns
35 NS_MAP = {
"http://musicbrainz.org/ns/mmd-2.0#":
"ws2",
36 "http://musicbrainz.org/ns/ext#-2.0":
"ext"}
37 _log = logging.getLogger(
"musicbrainzngs")
40 """ Given an error XML message from the webservice containing
41 <error><text>x</text><text>y</text></error>, return a list
44 tree = util.bytes_to_elementtree(error)
47 if root.tag ==
"error":
50 errors.append(ch.text)
57 for artist
in artists:
58 if isinstance(artist, dict):
60 names.append(artist.get(
"name",
""))
62 names.append(artist.get(
"artist", {}).
get(
"name",
""))
68 """ Extract single level subelements from an element.
69 For example, given the element:
71 <subelement>Text</subelement>
73 and a list valid_els that contains "subelement",
74 return a dict {'subelement': 'Text'}
76 Delegate the parsing of multi-level subelements to another function.
77 For example, given the element:
83 and a dictionary {'subelement': parse_subelement},
84 call parse_subelement(<subelement>) and
85 return a dict {'subelement': <result>}
86 if parse_subelement returns a tuple of the form
87 ('subelement-key', <result>) then return a dict
88 {'subelement-key': <result>} instead
92 t =
fixtag(sub.tag, NS_MAP)[0]
96 result[t] = sub.text
or ""
97 elif t
in inner_els.keys():
98 inner_result = inner_els[t](sub)
99 if isinstance(inner_result, tuple):
100 result[inner_result[0]] = inner_result[1]
102 result[t] = inner_result
104 m = re.match(
r'([a-z0-9-]+)-list', t)
105 if m
and "count" in sub.attrib:
106 result[
"%s-count" % m.group(1)] = int(sub.attrib[
"count"])
108 _log.info(
"in <%s>, uncaught <%s>",
109 fixtag(element.tag, NS_MAP)[0], t)
113 """ Extract attributes from an element.
114 For example, given the element:
115 <element type="Group" />
116 and a list attributes that contains "type",
117 return a dict {'type': 'Group'}
120 for attr
in element.attrib:
122 a =
fixtag(attr, NS_MAP)[0]
126 result[a] = element.attrib[attr]
128 _log.info(
"in <%s>, uncaught attribute %s",
fixtag(element.tag, NS_MAP)[0], attr)
133 tree = util.bytes_to_elementtree(message)
134 root = tree.getroot()
136 valid_elements = {
"area": parse_area,
137 "artist": parse_artist,
138 "instrument": parse_instrument,
139 "label": parse_label,
140 "place": parse_place,
141 "event": parse_event,
142 "release": parse_release,
143 "release-group": parse_release_group,
144 "series": parse_series,
145 "recording": parse_recording,
150 "cdstub": parse_cdstub,
153 "annotation-list": parse_annotation_list,
154 "area-list": parse_area_list,
155 "artist-list": parse_artist_list,
156 "label-list": parse_label_list,
157 "place-list": parse_place_list,
158 "event-list": parse_event_list,
159 "instrument-list": parse_instrument_list,
160 "release-list": parse_release_list,
161 "release-group-list": parse_release_group_list,
162 "series-list": parse_series_list,
163 "recording-list": parse_recording_list,
164 "work-list": parse_work_list,
165 "url-list": parse_url_list,
167 "collection-list": parse_collection_list,
168 "collection": parse_collection,
170 "message": parse_response_message
183 attribs = [
"id",
"type",
"entity-type"]
184 elements = [
"name",
"editor"]
186 inner_els = {
"release-list": parse_release_list}
197 attribs = [
"type",
"ext:score"]
198 elements = [
"entity",
"name",
"text"]
213 attribs = [
"id",
"type",
"ext:score"]
214 elements = [
"name",
"sort-name",
"disambiguation"]
215 inner_els = {
"life-span": parse_lifespan,
216 "alias-list": parse_alias_list,
217 "relation-list": parse_relation_list,
218 "annotation": parse_annotation,
219 "iso-3166-1-code-list": parse_element_list,
220 "iso-3166-2-code-list": parse_element_list,
221 "iso-3166-3-code-list": parse_element_list}
233 attribs = [
"id",
"type",
"ext:score"]
234 elements = [
"name",
"sort-name",
"country",
"user-rating",
235 "disambiguation",
"gender",
"ipi"]
236 inner_els = {
"area": parse_area,
237 "begin-area": parse_area,
238 "end-area": parse_area,
239 "life-span": parse_lifespan,
240 "recording-list": parse_recording_list,
241 "relation-list": parse_relation_list,
242 "release-list": parse_release_list,
243 "release-group-list": parse_release_group_list,
244 "work-list": parse_work_list,
245 "tag-list": parse_tag_list,
246 "user-tag-list": parse_tag_list,
247 "rating": parse_rating,
248 "ipi-list": parse_element_list,
249 "isni-list": parse_element_list,
250 "alias-list": parse_alias_list,
251 "annotation": parse_annotation}
266 attribs = [
"id",
"type",
"ext:score"]
267 elements = [
"name",
"address",
268 "ipi",
"disambiguation"]
269 inner_els = {
"area": parse_area,
270 "coordinates": parse_coordinates,
271 "life-span": parse_lifespan,
272 "tag-list": parse_tag_list,
273 "user-tag-list": parse_tag_list,
274 "alias-list": parse_alias_list,
275 "relation-list": parse_relation_list,
276 "annotation": parse_annotation}
288 attribs = [
"id",
"type"]
289 elements = [
"name",
"time"]
290 inner_els = {
"life-span": parse_lifespan,
291 "relation-list": parse_relation_list}
300 attribs = [
"id",
"type",
"ext:score"]
301 elements = [
"name",
"description",
"disambiguation"]
302 inner_els = {
"relation-list": parse_relation_list,
303 "tag-list": parse_tag_list,
304 "alias-list": parse_alias_list,
305 "annotation": parse_annotation}
316 attribs = [
"id",
"type",
"ext:score"]
317 elements = [
"name",
"sort-name",
"country",
"label-code",
"user-rating",
318 "ipi",
"disambiguation"]
319 inner_els = {
"area": parse_area,
320 "life-span": parse_lifespan,
321 "release-list": parse_release_list,
322 "tag-list": parse_tag_list,
323 "user-tag-list": parse_tag_list,
324 "rating": parse_rating,
325 "ipi-list": parse_element_list,
326 "alias-list": parse_alias_list,
327 "relation-list": parse_relation_list,
328 "annotation": parse_annotation}
337 if 'id' in attributes:
338 return (
'target-id', attributes[
'id'])
340 return (
'target-id', tgt.text)
343 attribs = [
"target-type"]
345 key =
"%s-relation-list" % ttype[
"target-type"]
350 attribs = [
"type",
"type-id"]
351 elements = [
"target",
"direction",
"begin",
"end",
"ended",
"ordering-key"]
352 inner_els = {
"area": parse_area,
353 "artist": parse_artist,
354 "instrument": parse_instrument,
355 "label": parse_label,
356 "place": parse_place,
357 "event": parse_event,
358 "recording": parse_recording,
359 "release": parse_release,
360 "release-group": parse_release_group,
361 "series": parse_series,
362 "attribute-list": parse_element_list,
364 "target": parse_relation_target
373 attribs = [
"id",
"ext:score"]
374 elements = [
"title",
"status",
"disambiguation",
"quality",
"country",
375 "barcode",
"date",
"packaging",
"asin"]
376 inner_els = {
"text-representation": parse_text_representation,
377 "artist-credit": parse_artist_credit,
378 "label-info-list": parse_label_info_list,
379 "medium-list": parse_medium_list,
380 "release-group": parse_release_group,
381 "tag-list": parse_tag_list,
382 "user-tag-list": parse_tag_list,
383 "relation-list": parse_relation_list,
384 "annotation": parse_annotation,
385 "cover-art-archive": parse_caa,
386 "release-event-list": parse_release_event_list}
390 if "artist-credit" in result:
392 result[
"artist-credit"])
405 inner_els = {
"area": parse_area}
412 elements = [
"position",
"format",
"title"]
413 inner_els = {
"disc-list": parse_disc_list,
414 "pregap": parse_track,
415 "track-list": parse_track_list,
416 "data-track-list": parse_track_list}
429 attribs = [
"id",
"type",
"ext:score"]
430 elements = [
"title",
"user-rating",
"first-release-date",
"primary-type",
432 inner_els = {
"artist-credit": parse_artist_credit,
433 "release-list": parse_release_list,
434 "tag-list": parse_tag_list,
435 "user-tag-list": parse_tag_list,
436 "secondary-type-list": parse_element_list,
437 "relation-list": parse_relation_list,
438 "rating": parse_rating,
439 "annotation": parse_annotation}
443 if "artist-credit" in result:
450 attribs = [
"id",
"ext:score"]
451 elements = [
"title",
"length",
"user-rating",
"disambiguation",
"video"]
452 inner_els = {
"artist-credit": parse_artist_credit,
453 "release-list": parse_release_list,
454 "tag-list": parse_tag_list,
455 "user-tag-list": parse_tag_list,
456 "rating": parse_rating,
457 "isrc-list": parse_external_id_list,
458 "echoprint-list": parse_external_id_list,
459 "relation-list": parse_relation_list,
460 "annotation": parse_annotation}
464 if "artist-credit" in result:
474 attribs = [
"id",
"type",
"ext:score"]
475 elements = [
"name",
"disambiguation"]
476 inner_els = {
"alias-list": parse_alias_list,
477 "relation-list": parse_relation_list,
478 "annotation": parse_annotation}
489 return [e.text
for e
in el]
496 attribs = [
"id",
"ext:score",
"type"]
497 elements = [
"title",
"user-rating",
"language",
"iswc",
"disambiguation"]
498 inner_els = {
"tag-list": parse_tag_list,
499 "user-tag-list": parse_tag_list,
500 "rating": parse_rating,
501 "alias-list": parse_alias_list,
502 "iswc-list": parse_element_list,
503 "relation-list": parse_relation_list,
504 "annotation": parse_response_message,
505 "attribute-list": parse_work_attribute_list
521 result[
"attribute"] = wa.text
532 elements = [
"resource"]
533 inner_els = {
"relation-list": parse_relation_list}
543 elements = [
"sectors"]
544 inner_els = {
"release-list": parse_release_list,
545 "offset-list": parse_offset_list
556 elements = [
"title",
"artist",
"barcode"]
557 inner_els = {
"track-list": parse_track_list}
565 return [int(o.text)
for o
in ol]
588 inner_els = {
"recording-list": parse_recording_list}
603 for namecredit
in ac:
606 if "joinphrase" in join:
607 result.append(join[
"joinphrase"])
613 inner_els = {
"artist": parse_artist}
628 elements = [
"catalog-number"]
629 inner_els = {
"label": parse_label}
643 elements = [
"number",
"position",
"title",
"length"]
644 inner_els = {
"recording": parse_recording,
645 "artist-credit": parse_artist_credit}
649 if "artist-credit" in result.get(
"recording", {})
and "artist-credit" not in result:
650 result[
"artist-credit"] = result[
"recording"][
"artist-credit"]
651 if "artist-credit" in result:
654 track_or_recording =
None
655 if "length" in result:
656 track_or_recording = result[
"length"]
657 elif result.get(
"recording", {}).
get(
"length"):
658 track_or_recording = result.get(
"recording", {}).
get(
"length")
659 if track_or_recording:
660 result[
"track_or_recording_length"] = track_or_recording
678 attribs = [
"votes-count"]
681 result[
"rating"] = rating.text
690 attribs = [
"locale",
"sort-name",
"type",
"primary",
691 "begin-date",
"end-date"]
694 result[
"alias"] = alias.text
700 elements = [
"artwork",
"count",
"front",
"back",
"darkened"]
709 NS =
"http://musicbrainz.org/ns/mmd-2.0#"
710 root = ET.Element(
"{%s}metadata" % NS)
711 rel_list = ET.SubElement(root,
"{%s}release-list" % NS)
712 for release, barcode
in release2barcode.items():
713 rel_xml = ET.SubElement(rel_list,
"{%s}release" % NS)
714 bar_xml = ET.SubElement(rel_xml,
"{%s}barcode" % NS)
715 rel_xml.set(
"{%s}id" % NS, release)
716 bar_xml.text = barcode
718 return ET.tostring(root,
"utf-8")
721 NS =
"http://musicbrainz.org/ns/mmd-2.0#"
722 root = ET.Element(
"{%s}metadata" % NS)
723 for entity_type
in [
'artist',
'label',
'place',
'recording',
'release',
'release_group',
'work']:
724 entity_tags = kwargs.pop(entity_type +
'_tags',
None)
725 if entity_tags
is not None:
726 e_list = ET.SubElement(root,
"{%s}%s-list" % (NS, entity_type.replace(
'_',
'-')))
727 for e, tags
in entity_tags.items():
728 e_xml = ET.SubElement(e_list,
"{%s}%s" % (NS, entity_type.replace(
'_',
'-')))
729 e_xml.set(
"{%s}id" % NS, e)
730 taglist = ET.SubElement(e_xml,
"{%s}user-tag-list" % NS)
732 usertag_xml = ET.SubElement(taglist,
"{%s}user-tag" % NS)
733 name_xml = ET.SubElement(usertag_xml,
"{%s}name" % NS)
736 raise TypeError(
"make_tag_request() got an unexpected keyword argument '%s'" % kwargs.popitem()[0])
738 return ET.tostring(root,
"utf-8")
741 NS =
"http://musicbrainz.org/ns/mmd-2.0#"
742 root = ET.Element(
"{%s}metadata" % NS)
743 for entity_type
in [
'artist',
'label',
'recording',
'release_group',
'work']:
744 entity_ratings = kwargs.pop(entity_type +
'_ratings',
None)
745 if entity_ratings
is not None:
746 e_list = ET.SubElement(root,
"{%s}%s-list" % (NS, entity_type.replace(
'_',
'-')))
747 for e, rating
in entity_ratings.items():
748 e_xml = ET.SubElement(e_list,
"{%s}%s" % (NS, entity_type.replace(
'_',
'-')))
749 e_xml.set(
"{%s}id" % NS, e)
750 rating_xml = ET.SubElement(e_xml,
"{%s}user-rating" % NS)
751 rating_xml.text = str(rating)
753 raise TypeError(
"make_rating_request() got an unexpected keyword argument '%s'" % kwargs.popitem()[0])
755 return ET.tostring(root,
"utf-8")
758 NS =
"http://musicbrainz.org/ns/mmd-2.0#"
759 root = ET.Element(
"{%s}metadata" % NS)
760 rec_list = ET.SubElement(root,
"{%s}recording-list" % NS)
761 for rec, isrcs
in recording2isrcs.items():
763 rec_xml = ET.SubElement(rec_list,
"{%s}recording" % NS)
764 rec_xml.set(
"{%s}id" % NS, rec)
765 isrc_list_xml = ET.SubElement(rec_xml,
"{%s}isrc-list" % NS)
766 isrc_list_xml.set(
"{%s}count" % NS, str(len(isrcs)))
768 isrc_xml = ET.SubElement(isrc_list_xml,
"{%s}isrc" % NS)
769 isrc_xml.set(
"{%s}id" % NS, isrc)
770 return ET.tostring(root,
"utf-8")