MythTV  master
mbutils.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # This file is part of MythTV.
3 # Copyright 2016, Paul Harrison.
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 
16 """Various utilities using the musicbrainzngs python bindings to access the MB database"""
17 
18 from __future__ import print_function
19 from __future__ import unicode_literals
20 from optparse import OptionParser
21 import musicbrainzngs
22 from musicbrainzngs.compat import is_py2
23 import sys
24 import pprint
25 
26 __author__ = "Paul Harrison'"
27 __title__ = "Music Brainz utilities"
28 __description__ = "Various utilities using the musicbrainzngs python bindings to access the MB database"
29 __version__ = "0.1"
30 
31 debug = False
32 
33 musicbrainzngs.set_useragent(
34  "MythTV",
35  "0.28",
36  "https://www.mythtv.org",
37 )
38 
39 #musicbrainzngs.set_hostname("musicbrainz.org")
40 #musicbrainzngs.set_hostname("music.peavers.com:5000")
41 musicbrainzngs.set_hostname("musicbrainz-mirror.eu:5000")
42 
43 def log(debug, txt):
44  if debug:
45  print(txt)
46 
47 def convert_etree(etostr):
48  """lxml.etree.tostring is a bytes object in python3, and a str in python2.
49  """
50  if is_py2:
51  return(etostr)
52  else:
53  return(etostr.decode())
54 
55 def search_releases(artist, album, limit):
56  from lxml import etree
57 
58  root = etree.XML(u'<searchreleases></searchreleases>')
59 
60  result = musicbrainzngs.search_releases(artist=artist, release=album, country="GB", limit=limit)
61 
62  if not result['release-list']:
63  etree.SubElement(root, "error").text = "No Releases found"
64  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
65  sys.exit(1)
66 
67  for (idx, release) in enumerate(result['release-list']):
68  pprint.pprint(release)
69  relNode = etree.SubElement(root, "release")
70 
71  etree.SubElement(relNode, "id").text = release['id']
72  etree.SubElement(relNode, "ext-score").text = release['ext:score']
73  etree.SubElement(relNode, "title").text = release['title']
74  etree.SubElement(relNode, "artist-credit-phrase").text = release["artist-credit-phrase"]
75  etree.SubElement(relNode, "country").text = release["country"]
76 
77  if 'date' in release:
78  etree.SubElement(relNode, "date").text = release['date']
79  etree.SubElement(relNode, "status").text = release['status']
80 
81  if 'release-group' in release:
82  etree.SubElement(relNode, "releasegroup").text = release["release-group"]["id"]
83 
84  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
85 
86  sys.exit(0)
87 
88 def search_artists(artist, limit):
89  from lxml import etree
90 
91  root = etree.XML(u'<artists></artists>')
92  result = musicbrainzngs.search_artists(artist=artist, limit=limit)
93 
94  if debug:
95  pprint.pprint(result)
96 
97  if result['artist-list']:
98  for (idx, artist) in enumerate(result['artist-list']):
99  artistNode = etree.SubElement(root, "id")
100  etree.SubElement(artistNode, "id").text = artist['id']
101  etree.SubElement(artistNode, "name").text = artist['name']
102  etree.SubElement(artistNode, "sort-name").text = artist['sort-name']
103  else:
104  etree.SubElement(root, "error").text = "No Artists found"
105  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
106  sys.exit(1)
107 
108  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
109 
110  sys.exit(0)
111 
112 def get_artist(artistid):
113  from lxml import etree
114 
115  root = etree.XML(u'<artist></artist>')
116 
117  # lookup the artist id
118  result = musicbrainzngs.get_artist_by_id(artistid, includes=['url-rels'])
119 
120  if debug:
121  pprint.pprint(result)
122 
123  if result:
124  etree.SubElement(root, "id").text = result['artist']['id']
125  etree.SubElement(root, "name").text = result['artist']['name']
126  etree.SubElement(root, "sort-name").text = result['artist']['sort-name']
127 
128  if 'url-relation-list' in result['artist']:
129  urls = result['artist']['url-relation-list']
130  if urls:
131  for (idx, url) in enumerate(urls):
132  node = etree.SubElement(root, "url")
133  node.text = url['target']
134  node.set("type", url['type'])
135  else:
136  etree.SubElement(root, "error").text = "MusicBrainz ID was not found"
137  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
138  sys.exit(1)
139 
140  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
141  sys.exit(0)
142 
143 def find_coverart(release):
144  from lxml import etree
145 
146  root = etree.XML(u'<coverart></coverart>')
147 
148  try:
149  data = musicbrainzngs.get_image_list(release)
150  except musicbrainzngs.ResponseError as err:
151  if err.cause.code == 404:
152  etree.SubElement(root, "error").text = "Release ID not found"
153  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
154  sys.exit(1)
155  else:
156  etree.SubElement(root, "error").text = "Received bad response from the MB server"
157  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
158  sys.exit(1)
159 
160  if debug:
161  pprint.pprint(data)
162 
163  for image in data["images"]:
164  imageNode = etree.SubElement(root, "image")
165  etree.SubElement(imageNode, "image").text = image["image"]
166  etree.SubElement(imageNode, "approved").text = str(image["approved"])
167  etree.SubElement(imageNode, "front").text = str(image["front"])
168  etree.SubElement(imageNode, "back").text = str(image["back"])
169  #etree.SubElement(imageNode, "types").text = image["types"]
170  etree.SubElement(imageNode, "thumb-small").text = image["thumbnails"]["small"]
171  etree.SubElement(imageNode, "thumb-large").text = image["thumbnails"]["large"]
172 
173  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
174  sys.exit(0)
175 
176 def find_coverart_releasegroup(releaseGroup):
177  from lxml import etree
178 
179  root = etree.XML(u'<coverart></coverart>')
180 
181  try:
182  data = musicbrainzngs.get_release_group_image_list(releaseGroup)
183  except musicbrainzngs.ResponseError as err:
184  if err.cause.code == 404:
185  etree.SubElement(root, "error").text = "Release ID not found"
186  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
187  sys.exit(1)
188  else:
189  etree.SubElement(root, "error").text = "Received bad response from the MB server"
190  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
191  sys.exit(1)
192 
193  if debug:
194  pprint.pprint(data)
195 
196  for image in data["images"]:
197  imageNode = etree.SubElement(root, "image")
198  etree.SubElement(imageNode, "image").text = image["image"]
199  etree.SubElement(imageNode, "approved").text = str(image["approved"])
200  etree.SubElement(imageNode, "front").text = str(image["front"])
201  etree.SubElement(imageNode, "back").text = str(image["back"])
202  #etree.SubElement(imageNode, "types").text = image["types"]
203  etree.SubElement(imageNode, "thumb-small").text = image["thumbnails"]["small"]
204  etree.SubElement(imageNode, "thumb-large").text = image["thumbnails"]["large"]
205 
206  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
207  sys.exit(0)
208 
209 def find_disc(cddrive):
210  import discid
211  from lxml import etree
212 
213  root = etree.XML(u'<finddisc></finddisc>')
214 
215  try:
216  disc = discid.read(cddrive, ["mcn", "isrc"])
217  id = disc.id
218  toc = disc.toc_string
219  except discid.DiscError as err:
220  etree.SubElement(root, "error").text = "Failed to get discid ({})".format(str(err))
221  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
222  sys.exit(1)
223 
224  etree.SubElement(root, "discid").text = id
225  etree.SubElement(root, "toc").text = toc
226 
227  try:
228  # the "labels" include enables the cat#s we display
229  result = musicbrainzngs.get_releases_by_discid(id, includes=["labels"], toc=toc, cdstubs=False)
230  except musicbrainzngs.ResponseError as err:
231  if err.cause.code == 404:
232  etree.SubElement(root, "error").text = "Disc not found"
233  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
234  sys.exit(1)
235  else:
236  etree.SubElement(root, "error").text = "Received bad response from the MB server"
237  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
238  sys.exit(1)
239 
240  # The result can either be a "disc" or a "cdstub"
241  if result.get('disc'):
242  discnode = etree.SubElement(root, "disc")
243 
244  etree.SubElement(discnode, "sectors").text = result['disc']['sectors']
245 
246  if "offset-list" in result['disc']:
247  offsets = None
248  for offset in result['disc']['offset-list']:
249  if offsets == None:
250  offsets = str(offset)
251  else:
252  offsets += " " + str(offset)
253 
254  etree.SubElement(discnode, "offsets").text = offsets
255  etree.SubElement(discnode, "tracks").text = str(result['disc']['offset-count'])
256 
257  for release in result['disc']['release-list']:
258  relnode = etree.SubElement(discnode, "release")
259 
260  etree.SubElement(relnode, "title").text = release['title']
261  etree.SubElement(relnode, "musicbrainzid").text = release['id']
262 
263  if release.get('barcode'):
264  etree.SubElement(relnode, "barcode").text = release['barcode']
265  for info in release['label-info-list']:
266  if info.get('catalog-number'):
267  etree.SubElement(relnode, "catalog-number").text = info['catalog-number']
268  elif result.get('cdstub'):
269  stubnode = etree.SubElement(root, "cdstub")
270 
271  etree.SubElement(stubnode, "artist").text = result['cdstub']['artist']
272  etree.SubElement(stubnode, "title").text = result['cdstub']['title']
273 
274  if result['cdstub'].get('barcode'):
275  etree.SubElement(stubnode, "barcode").text = result['cdstub']['barcode']
276  else:
277  etree.SubElement(root, "error").text = "No valid results"
278  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
279  sys.exit(1)
280 
281  log(True, convert_etree(etree.tostring(root, encoding='UTF-8', pretty_print=True, xml_declaration=True)))
282  sys.exit(0)
283 
285  from lxml import etree
286  version = etree.XML(u'<version></version>')
287  etree.SubElement(version, "name").text = __title__
288  etree.SubElement(version, "author").text = __author__
289  etree.SubElement(version, "command").text = 'mbutils.py'
290  etree.SubElement(version, "description").text = __description__
291  etree.SubElement(version, "version").text = __version__
292 
293  log(True, convert_etree(etree.tostring(version, encoding='UTF-8', pretty_print=True,
294  xml_declaration=True)))
295  sys.exit(0)
296 
298  err = 0
299  try:
300  import discid
301  except:
302  err = 1
303  print ("Failed to import python discid lirary. Is libdiscid installed?")
304  try:
305  import lxml
306  except:
307  err = 1
308  print("Failed to import python lxml library.")
309 
310  if not err:
311  print("Everything appears in order.")
312  sys.exit(err)
313 
314 
315 def main():
316  global debug
317 
318  parser = OptionParser()
319 
320  parser.add_option('-v', "--version", action="store_true", default=False,
321  dest="version", help="Display version and author")
322  parser.add_option('-t', "--test", action="store_true", default=False,
323  dest="test", help="Perform self-test for dependencies.")
324  parser.add_option('-r', "--searchreleases", action="store_true", default=False,
325  dest="searchreleases", help="Search for musicbrainz release id's for a given artist & album. Requires --artist/--album. Optional --limit.")
326  parser.add_option('-s', "--searchartists", action="store_true", default=False,
327  dest="searchartists", help="Search for musicbrainz artist id's for a given artist. Requires --artist. Optional --limit.")
328  parser.add_option('-g', "--getartist", action="store_true", default=False,
329  dest="getartist", help="Lookup the details of a given musicbrainz id of an artist. Requires --id.")
330  parser.add_option('-c', "--finddisc", action="store_true", default=None,
331  dest="finddisc", help="Find the musicbrainz id for a cd. Requires --cddevice.")
332  parser.add_option('-f', "--findcoverart", action="store_true", default=None,
333  dest="findcoverart", help="Find coverart for a given musicbrainz id of a release or release group. Requires --id or --relgroupid.")
334  parser.add_option('-d', '--debug', action="store_true", default=False,
335  dest="debug", help=("Show debug messages"))
336  parser.add_option('-a', '--artist', metavar="ARTIST", default=None,
337  dest="artist", help=("Name of artist"))
338  parser.add_option('-b', '--album', metavar="ALBUM", default=None,
339  dest="album", help=("Name of Album"))
340  parser.add_option('-l', '--limit', metavar="LIMIT", default=None,
341  dest="limit", help=("Limits the maximum number of results to return for some commands (defaults to 5)"))
342  parser.add_option('-i', '--id', metavar="ID", default=None,
343  dest="id", help=("Music Brainz ID to use"))
344  parser.add_option('-I', '--relgroupid', metavar="RELEASEGROUPID", default=None,
345  dest="relgroupid", help=("Music Brainz ID of Release Group to use"))
346  parser.add_option('-D', '--cddevice', metavar="CDDEVICE", default=None,
347  dest="cddevice", help=("CD device to use"))
348 
349  opts, args = parser.parse_args()
350 
351  if opts.debug:
352  debug = True
353 
354  if opts.version:
355  buildVersion()
356 
357  if opts.test:
359 
360  if opts.searchreleases:
361  if opts.artist == None:
362  print("Missing --artist argument")
363  sys.exit(1)
364 
365  if opts.album == None:
366  print("Missing --album argument")
367  sys.exit(1)
368 
369  limit = 5
370  if opts.limit:
371  limit = int(opts.limit)
372 
373  search_releases(opts.artist, opts.album, limit)
374 
375  if opts.searchartists:
376  if opts.artist == None:
377  print("Missing --artist argument")
378  sys.exit(1)
379 
380  limit = 5
381  if opts.limit:
382  limit = int(opts.limit)
383 
384  search_artists(opts.artist, limit)
385 
386  if opts.getartist:
387  if opts.id == None:
388  print("Missing --id argument")
389  sys.exit(1)
390 
391  get_artist(opts.id)
392 
393  if opts.finddisc:
394  if opts.cddevice == None:
395  print("Missing --cddevice argument")
396  sys.exit(1)
397 
398  find_disc(opts.cddevice)
399 
400  if opts.findcoverart:
401  if opts.id == None and opts.relgroupid == None:
402  print("Missing --id or --relgroupid argument")
403  sys.exit(1)
404 
405  if opts.id != None:
406  find_coverart(opts.id)
407  else:
408  find_coverart_releasegroup(opts.relgroupid)
409 
410  sys.exit(0)
411 
412 if __name__ == '__main__':
413  main()
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)
def performSelfTest()
Definition: mbutils.py:297
def search_releases(artist, album, limit)
Definition: mbutils.py:55
def find_coverart(release)
Definition: mbutils.py:143
def buildVersion()
Definition: mbutils.py:284
def find_disc(cddrive)
Definition: mbutils.py:209
def search_artists(artist, limit)
Definition: mbutils.py:88
def find_coverart_releasegroup(releaseGroup)
Definition: mbutils.py:176
def log(debug, txt)
Definition: mbutils.py:43
def get_artist(artistid)
Definition: mbutils.py:112
def convert_etree(etostr)
Definition: mbutils.py:47
def main()
Definition: mbutils.py:315