
    gd                        d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	m
c mZ
 d dlmZ d dlmZ d dlmZ d dlmZ d dlmZ dZ ej*                  d      Zd	Zg d
ZeD  cg c]  } | dz   	 c} ZddgZddgZi dddgez   dg dez   ez   ez   dg dddgez   ez   dg dez   ez   ez   dddgez   ez   ddgez   ez   ez   dg dez   ez   ez   dg dez   ez   dg dez   ez   ez   d ddgez   d!ddgez   ez   ez   d"ed#g dez   d$g d%d&d'gd(d)gZdgez   ez   ez   dgez   ez   ez   dgez   ez   ez   d*d+gez   ez   ez   g d,ez   dgez   ez   d*gez   ez   ez   eddgez   ez   ez   d-	Zg d.Zg d/Zg d0g d1g d2g d3g d4g d5g d6g d7g d8g d9g d:g d;d<Z  G d= d>      Z! G d? d@      Z" G dA dB      Z# G dC dDe$      Z% G dE dFe%      Z& G dG dHe&      Z' G dI dJe&      Z( G dK dLe&      Z) G dM dNe%      Z* G dO dPe*      Z+ G dQ dRe*      Z, G dS dTe*      Z-dU Z.dV Z/dW Z0g g fdXZ1dY Z2dZ Z3d[ Z4d\ Z5d]xa6a7d^a8d_a9d]a:d]a;d` Z<ddaZ=ddcZ>dda?dea@d_aAddfZB G dg dheC      ZD G di djej                        ZF G dk dlej                        ZH G dm dnej                        ZJddoZK eLe
dp      re
j                  ej                  fZOnej                  ZOdq ZPdr ZQeQaRdsaSddtZTdduZUeDdve"dbdddfdw       ZVdx ZWg i fdyZXd]i dddbfdzZYd{ ZZd| Z[d} Z\ e2d      g g g fd~       Z] e2d      g g g fd       Z^ e2d      g g g fd       Z_ e2d      g g g fd       Z` e2d      g g g fd       Za e2d      g g g fd       Zb e2d      g g g fd       Zc e2d      g g g fd       Zd e2d      g g g fd       Ze e2d       g fd       Zf e2d!      g fd       Zg e2d"      g fd       Zh e4d      dd       Zi e4d      dd       Zj e4d      dd       Zk e4d      dd       Zl e4d      dd       Zm e4d      dd       Zn e4d      dd       Zo e4d      	 	 dd       Zp e4d      dd       Zq e4d      	 	 dd       Zr e4d       dd       Zs e4d!      dd       Zt e2d#      g dd_dfd       Zu e2d      g g g fd       Zv e2d!      g fd       Zwg g fdZx e3d      ddddg ddfd       Zy e3d      dddg ddfd       Zz e3d      dg ddfd       Z{ e3d      dg ddfd       Z| e3d      ddg ddfd       Z} e3d      dddddg g g ddf
d       Z~ e3d      ddg g ddfd       Z e3d"      dg ddfd       Z e3d!      dg ddfd       Zd Zd ZddZddZddZddZddZddZd Zd Zd Zd Zg fdZg fdZyc c} w )    N)expat)warn)mbxml)util)compatz0.7.1musicbrainzngsz([+\-&|!(){}\[\]\^"~*?:\\\/]))areaartistlabelplaceevent	recordingreleaserelease-groupseriesurlwork
instrumentz-relstags	user-tagsratingsuser-ratingsr	   aliases
annotationr
   )

recordingsreleasesrelease-groupsworkszvarious-artistsdiscidsmediaisrcsr   r   r   r   )r   r   r    r   r   r   r   r   )	artistsr   r   r    artist-creditsr!   work-level-relsr   r   r   )r"   labelsr   r   r    r#   r   r!   zrecording-level-relsr$   r   r   r   )r"   r   r   r    r#   r   r   r   r   r   discidisrc)r"   r   r!   iswcr"   
collectionr   r#   r!   )r#   r%   r   r!   r   r    r   )	r
   r   r   r   r   r   r   r   r   )natalbumsingleep	broadcastothercompilation
soundtrack
spokenword	interview	audiobookliveremixzdj-mixzmixtape/street)official	promotionbootlegzpseudo-release)entitynametexttype)aidaliasr	   
areaaccentbegincommentendendedisoiso1iso2iso3sortnametagr=   )r?   r	   aridr
   artistaccentrA   	beginarearB   countryrC   endarearD   genderipiisniprimary_aliasrI   rJ   r=   )r>   r?   r	   rK   r
   rA   rB   eidrC   rD   r   eventaccentpidr   rJ   r=   )r?   rB   descriptioniidr   instrumentaccentrJ   r=   )r?   r	   rA   coderB   rN   rC   rD   rQ   r   labelaccentlaidrelease_countrI   rJ   r=   )addressr?   r	   rA   rB   rC   rD   latlongrV   r   placeaccentr=   )r?   rK   r
   
artistnamerB   rN   
creditnamedatedurformatr'   numberpositionprimarytypeqdurr   recordingaccentreidr   rgidridsecondarytypestatusrJ   tidtnumtrackstracksreleaser=   video)r?   rK   r
   rb   rB   rc   ri   rl   r   releasegroupreleasegroupaccentr   rm   ro   rp   rJ   r=   )r?   rK   r
   rb   asinbarcodecatnorB   rN   rc   rd   r   discidsmediumrf   r   r\   langmediumsri   qualityrl   r   releaseaccentrm   scriptro   rp   rJ   rs   tracksmediumr=   )r?   rB   orderingattributer   seriesaccentsidrJ   r=   )r?   rK   r
   rB   r(   r|   r   recording_countrn   rJ   r=   widr   
workaccent)r   r	   r
   r   r   r   r   r   r   r   r   r   c                       e Zd Zy)AUTH_YESN__name__
__module____qualname__     //opt/Tautulli/lib/musicbrainzngs/musicbrainz.pyr   r          r   r   c                       e Zd Zy)AUTH_NONr   r   r   r   r   r      r   r   r   c                       e Zd Zy)
AUTH_IFSETNr   r   r   r   r   r      r   r   r   c                       e Zd ZdZy)MusicBrainzErrorz5Base class for all exceptions related to MusicBrainz.Nr   r   r   __doc__r   r   r   r   r      s    <r   r   c                       e Zd ZdZy)
UsageErrorz*Error related to misuse of the module API.Nr   r   r   r   r   r      s    1r   r   c                       e Zd Zy)InvalidSearchFieldErrorNr   r   r   r   r   r      s    r   r   c                   &     e Zd Zd fd	Zd Z xZS )InvalidIncludeErrorc                 H    t         t        |   |        || _        || _        y N)superr   __init__msgreasonselfr   r   	__class__s      r   r   zInvalidIncludeError.__init__   s!    T+D1$($+r   c                     | j                   S r   r   r   s    r   __str__zInvalidIncludeError.__str__       	/r   zInvalid IncludesNr   r   r   r   r   __classcell__r   s   @r   r   r          
r   r   c                   &     e Zd Zd fd	Zd Z xZS )InvalidFilterErrorc                 H    t         t        |   |        || _        || _        y r   )r   r   r   r   r   r   s      r   r   zInvalidFilterError.__init__   s!    D*40$($+r   c                     | j                   S r   r   r   s    r   r   zInvalidFilterError.__str__   r   r   r   r   r   s   @r   r   r      r   r   r   c                       e Zd ZdZddZd Zy)WebServiceErrorz*Error related to MusicBrainz API requests.Nc                      || _         || _        y)zFPass ``cause`` if this exception was caused by another
		exception.
		N)messagecause)r   r   r   s      r   r   zWebServiceError.__init__   s     $,$*r   c                 x    | j                   rd| j                   z  }nd}|dt        | j                        z  z  }|S )Nz%s,  zcaused by: %s)r   strr   )r   r   s     r   r   zWebServiceError.__str__   s8    	\\	$,,	3	33tzz?	**#	*r   NN)r   r   r   r   r   r   r   r   r   r   r      s    1r   r   c                       e Zd ZdZy)NetworkErrorz)Problem communicating with the MB server.Nr   r   r   r   r   r      s    0r   r   c                       e Zd ZdZy)ResponseErrorz#Bad response sent by the MB server.Nr   r   r   r   r   r      s    *r   r   c                       e Zd ZdZy)AuthenticationErrorzBReceived a HTTP 401 response while accessing a protected resource.Nr   r   r   r   r   r      s    Ir   r   c                 6    | D ]  }||vst        d|z         y )Nz'Bad includes: %s is not a valid include)r   )includesvalid_includesis      r   _check_includes_implr      s9     GN"% 'BDE'F G GGr   c                 *    t        |t        |           y r   )r   VALID_INCLUDES)r:   incs     r   _check_includesr      s    nV45r   c                 0    | D ]  }||vst        |       y r   )r   )valuesvalidvs      r   _check_filterr      s#    
 Qe^	A	r   c                    t        |t        j                        r|g}t        |t        j                        r|g}t        |t               t        |t
               |rd|vr| dk7  rt        d      |rd|vrd|vr| dvrt        d      i }t        |      rdj                  |      |d<   t        |      rdj                  |      |d	<   |S )
zCheck that the status or type values are valid. Then, check that
    the filters can be used with the given includes. Return a params
    dict that can be passed to _do_mb_query.
    r   r   z+Can't have a status with no release includer   )r   r   zECan't have a release type with no releases or release-groups involved|rp   r=   )	
isinstancer   
basestringr   VALID_RELEASE_STATUSESVALID_RELEASE_TYPESr   lenjoin)r:   r   release_statusrelease_typeparamss        r   _check_filter_and_make_paramsr      s    
 .&"3"34(), 1 12$~."89, 34(*v/B !NOO 0Zx5O::  "> ? 	? F
>88N3x
<,/vMr   c                 X    t        t        j                  | g             }t        d|      S Nr   )listr   get_docstring_implr:   r   s     r   _docstring_getr     s&    N&&vr23H:x00r   c                 X    t        t        j                  | g             }t        d|      S r   )r   VALID_BROWSE_INCLUDESr   r   r   s     r   _docstring_browser     s'    )--fb9:H:x00r   c                 X    t        t        j                  | g             }t        d|      S )Nfields)r   VALID_SEARCH_FIELDSr   r   )r:   search_fieldss     r   _docstring_searchr     s'    ,00<=M8]33r   c                       fd}|S )Nc                     dj                        }|i}| j                  r! | j                  j                  di || _        | S )N, r   )r   r   rf   )funcvstrargsr;   r   s      r   
_decoratorz#_docstring_impl.<locals>._decorator!  sA    yy d|<<.4<<..66DLr   r   )r;   r   r   s   `` r   r   r      s     r   r   musicbrainz.orgTc                     | a |ay)zySet the username and password to be used in subsequent queries to
	the MusicBrainz XML API that require authentication.
	N)userpassword)ups     r   authr   3  s    
 	
r   c                     | r|st        d      || d|dt        d|dan| d|dt        a| d|at        j                  dt        z         y)	zxSet the User-Agent to be used for requests to the MusicBrainz webservice.
    This must be set before requests are made.z App and version can not be emptyN/z python-musicbrainzngs/z ( z )-zset user-agent to %s)
ValueError_version
_useragent_client_logdebug)appversioncontacts      r   set_useragentr	  ;  sR     g;<<@CWhX_`
9<gxP
g&GJJ%
23r   Fc                     | a |ay)a  Set the hostname for MusicBrainz webservice requests.
    Defaults to 'musicbrainz.org', accessing over https.
    For backwards compatibility, `use_https` is False by default.

    :param str new_hostname: The hostname (and port) of the MusicBrainz server to connect to
    :param bool use_https: `True` if the host should be accessed using https. Default is `False`

    Specify a non-standard port by adding it to the hostname,
    for example 'localhost:8000'.N)hostnamehttps)new_hostname	use_httpss     r   set_hostnamer  I  s     HEr         ?   c                 v    t        | t              r| ay| dk  rt        d      |dk  rt        d      da| a|ay)aQ  Sets the rate limiting behavior of the module. Must be invoked
    before the first Web service call.
    If the `limit_or_interval` parameter is set to False then
    rate limiting will be disabled. If it is a number then only
    a set number of requests (`new_requests`) will be made per
    given interval (`limit_or_interval`).
            z&limit_or_interval can't be less than 0r   z!new_requests can't be less than 0TN)r   booldo_rate_limitr   limit_intervallimit_requests)limit_or_intervalnew_requestss     r   set_rate_limitr  ^  sK     #T*)#EFF1@AA*%r   c                   "    e Zd ZdZd Zd Zd Zy)_rate_limitao  A decorator that limits the rate at which the function may be
    called. The rate is controlled by the `limit_interval` and
    `limit_requests` global variables.  The limiting is thread-safe;
    only one thread may be in the function at a time (acts like a
    monitor in this sense). The globals must be set before the first
    call to the limited function.
    c                 `    || _         d| _        t        j                         | _        d | _        y )Nr  )fun	last_call	threadingLocklockremaining_requests)r   r  s     r   r   z_rate_limit.__init__|  s&    NN$	"&r   c                 P   | j                   t        t              | _         nlt        j                         | j                  z
  }| xj                   |t        t
        z  z  z  c_         t        | j                   t        t                    | _         t        j                         | _        y)zeUpdate remaining requests based on the elapsed time since
        they were last calculated.
        N)r#  floatr  timer  r  min)r   since_last_calls     r   _update_remainingz_rate_limit._update_remaining  s     ""*&+N&;D# #iikDNN:O##(6(G(I I#&)$*A*A*/*?'AD# r   c                 ~   | j                   5  t        r| j                          | j                  dk  rPt	        j
                  d| j                  z
  t        t        z  z         | j                          | j                  dk  rP| xj                  dz  c_         | j                  |i |cd d d        S # 1 sw Y   y xY w)Ng+?r  )	r"  r  r)  r#  r&  sleepr  r  r  )r   r   kwargss      r   __call__z_rate_limit.__call__  s    YY 	-&&( --5JJd&=&= = . ? A B**, --5 ''3.'488T,V,	- 	- 	-s   A5B3&B33B<N)r   r   r   r   r   r)  r-  r   r   r   r  r  t  s    '%$-r   r  c                       e Zd Zd Zd Zd Zy)_RedirectPasswordMgrc                     i | _         y r   _realmsr   s    r   r   z_RedirectPasswordMgr.__init__  s	    $,r   c                 @    	 | j                   |   S # t        $ r Y yw xY w)Nr   )r2  KeyError)r   realmuris      r   find_user_passwordz'_RedirectPasswordMgr.find_user_password  s(    
,,u
	 
s    	c                 &    ||f| j                   |<   y r   r1  )r   r5  r6  usernamer   s        r   add_passwordz!_RedirectPasswordMgr.add_password  s    !8,$,,ur   N)r   r   r   r   r7  r:  r   r   r   r/  r/    s    -r   r/  c                       e Zd Zd Zd Zd Zy)_DigestAuthHandlerc                     |j                  dd       }|rd|v rd|j                  d      v rd|d<   t        j                  j	                  | ||      S )Nqop,r   )r   splitr   HTTPDigestAuthHandlerget_authorization)r   reqchalr>  s       r   rB  z$_DigestAuthHandler.get_authorization  sN    hht$3#:&CIIsO"; DK++==tS$OOr   c                     t         j                  j                  xs t        j                         }	 |j                  |      }|j                  d      S # t        $ r Y w xY w)z<The MusicBrainz server also accepts UTF-8 encoded passwords.zutf-8)sysstdinencodinglocalegetpreferredencodingdecodeAttributeErrorencode)r   r   rH  s      r   _encode_utf8z_DigestAuthHandler._encode_utf8  sW    99%%F)D)D)F	**X&C zz'""  		s   A 	A A c                 b     |j                         }|dk(  r fdn
|dk(  r fdfd}|fS )NMD5c                 h    t        j                  j                  |             j                         S r   )hashlibmd5rN  	hexdigestxr   s    r   <lambda>z8_DigestAuthHandler.get_algorithm_impls.<locals>.<lambda>  s$    '++d&7&7&:;EEG r   SHAc                 h    t        j                  j                  |             j                         S r   )rR  sha1rN  rT  rU  s    r   rW  z8_DigestAuthHandler.get_algorithm_impls.<locals>.<lambda>  s$    ',,t'8'8';<FFH r   c                      | d|      S )N:r   )sdHs     r   rW  z8_DigestAuthHandler.get_algorithm_impls.<locals>.<lambda>  s    !q!,- r   )upper)r   	algorithmKDr_  s   `  @r   get_algorithm_implsz&_DigestAuthHandler.get_algorithm_impls  s7    OO%	GA%HA-"ur   N)r   r   r   rB  rN  rc  r   r   r   r<  r<    s    P	#
r   r<  c                       e Zd ZdZddZd Zy)_MusicbrainzHttpRequestz4 A custom request handler that allows DELETE and PUTNc                     t         j                  j                  | ||       g d}||vrt        d|z        || _        y )N)GETPOSTDELETEPUTzinvalid method: %s)r   Requestr   r   method)r   rl  r   data	allowed_ms        r   r   z _MusicbrainzHttpRequest.__init__  s=    ..$T*.)9	(61	22$+r   c                     | j                   S r   )rl  r   s    r   
get_methodz"_MusicbrainzHttpRequest.get_method  s    	r   r   )r   r   r   r   r   rp  r   r   r   re  re    s    ;r   re  c                 "   d}t        |      D ]l  }|r0t        j                  d|z         t        j                  ||z         	 |r| j                  ||      }n| j                  |      }|j                         c S  t+        d|z  |      # t        j                  $ r}|j                  dv rt        |      |j                  dv r#t        j                  d|j                  z         n<|j                  dv rt        |      t        j                  d|j                  z         |}Y d}~ d}~wt        j                  $ r"}t        j                  d	       |}Y d}~Sd}~wt        j                  $ r.}t        j                  d
t        |      z         |}Y d}~d}~wt        j                  $ rV}t!        |j"                  t$        j&                        r!|j"                  j(                  }	|	dk(  rY d}~t+        |      d}~wt$        j,                  $ r"}t        j                  d       |}Y d}~,d}~wt$        j&                  $ r&}|j(                  dk(  rY d}~Wt+        |      d}~wt.        $ r}t+        |      d}~ww xY w)zOpen 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
	exceptions. Return a bytestring.
	Nzretrying after delay (#%i))i  i  i  r   )i  i  i  zHTTP error %i)i  zunknown HTTP error %izbad status linez miscellaneous HTTP exception: %sh   zsocket timeoutzretried %i times)ranger  infor&  r+  openreadr   	HTTPErrorrZ   r   r   BadStatusLineHTTPExceptionr   URLErrorr   r   socketerrorerrnor   timeoutIOError)
openerrC  bodymax_retriesretry_delay_deltalast_exc	retry_numfexcrZ   s
             r   
_safe_readr    s    $ .!Y99)I56::i++,)!
CACA
&&(?.!b &4h??K 
		 	hh/!
c
""O#IIo()G
C
(( 	II%018			 998			 99/#c(:;8	 !V\\*::Ds{	C	  	 998	 !	ii3	C	  	 !	C	  !sm   5BJB	D..JE!!J7#F  J6?H;HJH::JI1%I11J=J		J
ParseErrorc                     | S )zReturn the raw response (XML)r   )resps    r   mb_parser_nullr  %  s    Kr   c                     	 t        j                  |       S # t        $ r}t        |      d}~wt        $ r"}t        |t              rt        |       d}~ww xY w)z2Return a Python dict representing the XML responserr  N)r   parse_messageUnicodeErrorr   	Exceptionr   ETREE_EXCEPTIONS)r  r  s     r   mb_parser_xmlr  )  sU    ""4(( '#&& c+,c**	s    	A,AAAxmlc                 D    | t         } t        |       st        d      | ay)zSets the function used to parse the response from the
    MusicBrainz web service.

    If no parser is given, the parser is reset to the default parser
    :func:`mb_parser_xml`.
    Nznew_parser_fun must be callable)r  callabler   
parser_fun)new_parser_funs    r   
set_parserr  :  s(     &N#:;;Jr   c                     | dk(  r| a t                y| dk(  r'| a t        d       t        t        j                         yt        d| z        )a  Sets the format that should be returned by the Web Service.
    The server currently supports `xml` and `json`.

    This method will set a default parser for the specified format,
    but you can modify it with :func:`set_parser`.

    .. warning:: The json format used by the server is different from
        the json format returned by the `musicbrainzngs` internal parser
        when using the `xml` format! This format may change at any time.
    r  jsonz:The json format is non-official and may change at any timezinvalid format: %sN)	ws_formatr  r   r  loadsr   )fmts    r   
set_formatr  H  sG     e|			IJ4::-344r   rg  c           	         |i }nt        |      xs i }t        dk(  rt        d      |r	t        |d<   t        dk7  r	t        |d<   g }t        |j                               D ]C  \  }}	t        |	t        j                        r|	j                  d      }	|j                  ||	f       E t        j                  t        rdndt        d	| z  dt        j                  |      df      }
t         j#                  |d
|
       t        j$                  d      }|g}d}|t&        k(  r+t         j#                  d|
z         t(        st        d      d}|t*        k(  r t(        rt         j#                  d|
z         d}|rBt-               }t/        |      }|j1                  ddt(        t2               |j                  |       t        j4                  | }t7        ||
|      }|j9                  dt               t         j#                  dt        z         |r|j9                  dd       n%|s#|j;                  d      s|j9                  dd       t=        |||      }t?        |      S )aG  Makes a request for the specified `path` (endpoint) on /ws/2 on
    the globally-specified hostname. Parses the responses and returns
    the resulting object.  `auth_required` and `client_required` control
    whether exceptions should be raised if the username/password and
    client are left unspecified, respectively.
    r   zset a proper user-agent with set_useragent("application name", "application version", "contact info (preferably URL or email for your application)")clientr  r  utf8r  httpz/ws/2/%sz request for r   )
debuglevelFzAuth required for %sz2authorization required; use auth(user, pass) firstTz.Using auth for %s because user and pass is setr   r   z
User-Agentzrequesting with UA %szContent-Typezapplication/xml; charset=UTF-8zContent-Length0) dictr  r   r  r  sorteditemsr   r   unicoderM  append
urlunparser  r  	urlencoder  r  HTTPHandlerr   r   r   r/  r<  r:  r   build_openerre  
add_header
has_headerr  r  )pathrl  auth_requiredclient_requiredr   rm  r  newargskeyvaluer   httpHandlerhandlersadd_authpasswordMgrauthHandlerr  rC  r  s                      r   _mb_requestr  _  s/    |DzRR Y Z 	Z  XEU GTZZ\* %
UeV^^,LL(EU|$% 

fT
!
 C 	JJfc23 $$2K}H H 

)C/0 : ; ;
"t

CcIJ*,(5  !2BhG$  (+F "&#t
4CNN<,JJ&34~'GH#..)9: 	'-fc4(Ddr   c                 h    d|v sd|v rt         S | j                  d      r|st         S t        S t        S )z^ Some calls require authentication. This returns
    True if a call does, False otherwise
    r   r   r)   )r   
startswithr   r   )r:   idr   s      r   _get_auth_typer    s9     h.H"<			<	(Or   c                     t        |t              s|g}t        | |       t        | ||      }t	        |      }t        |      dkD  rdj                  |      }||d<   | d|}t        |d||      S )ap  Make a single GET call to the MusicBrainz XML API. `entity` is a
	string indicated the type of object to be retrieved. The id may be
	empty, in which case the query is a search. `includes` is a list
	of strings that must be valid includes for the entity type. `params`
	is a dictionary of additional parameters for the API call. The
	response is parsed and returned.
	r    r   r   rg  )r   )r   r   r   r  r  r   r   r  )r:   r  r   r   r  r   r   r  s           r   _do_mb_queryr    sv     	8T"Z("H5VMA#$u+ 2D%T::r   c                 L   g }|rzt        j                  |      }|rRt        j                  t        d|      }|r|j                  d|z         n1|j                  |j                                n|j                  |       |j                         D ]  \  }}	|t        |    vrt        |d|       t        j                  |	      }	t        j                  t        d|	      }	|	sT|r|j                  |d|	d       n|	j                         }	|j                  |d|	d        |r dj                  |      j                         }
nd	j                  |      j                         }
|
st        d
      d|
i}|rt        |      |d<   |rt        |      |d<   t        | dg |      S )a  Perform a full-text search on the MusicBrainz search server.
	`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.
	z\\\1z"%s"z! is not a valid search field for z:""z:()z AND r  z#at least one query term is requiredquerylimitoffsetr   )r   _unicoderesubLUCENE_SPECIALr  lowerr  r   r   r   stripr   r   r  )r:   r  r   r  r  strictquery_partsclean_queryr  r  
full_queryr   s               r   _do_mb_searchr    s    	e$+
;v+,{((*+k"<<> 1ZS%#F++	 /2F;
 
 --
%
&&%
0%
C/0KKMEC/01  ||K(..0*xx$**,*899 J
	J&/
[&VRV,,r   c                 &    t        | dt        d      S )z1Send a DELETE request for the specified object.
	ri  Tr  r   r  s    r   _do_mb_deleter    s     	D(Hd33r   c                 &    t        | dt        d      S )z.Send a PUT request for the specified object.
	rj  Tr  r  s    r   
_do_mb_putr    s     	D%400r   c                 *    t        | dt        d|      S )zLPerform a single POST call for an endpoint with a specified
	request body.
	rh  T)r  r  )r  r  s     r   _do_mb_postr    s     	D&(Dt<<r   c                 :    t        d|||      }t        d| ||      S )zjGet the area with the MusicBrainz `id` as a dict with an 'area' key.

    *Available includes*: {includes}r	   r   r  r  r   r   r   r   s        r   get_area_by_idr  %  s)    
 +68+9<IFHf55r   c                 :    t        d|||      }t        d| ||      S )znGet the artist with the MusicBrainz `id` as a dict with an 'artist' key.

    *Available includes*: {includes}r
   r  r  s        r   get_artist_by_idr  .  s)    
 +8X+9<IF"h77r   c                 :    t        d|||      }t        d| ||      S )zrGet the instrument with the MusicBrainz `id` as a dict with an 'artist' key.

    *Available includes*: {includes}r   r  r  s        r   get_instrument_by_idr  7  s)    
 +<+9<IFb(F;;r   c                 :    t        d|||      }t        d| ||      S )zkGet the label with the MusicBrainz `id` as a dict with a 'label' key.

    *Available includes*: {includes}r   r  r  s        r   get_label_by_idr  @  )    
 +7H+9<IFXv66r   c                 :    t        d|||      }t        d| ||      S )zlGet the place with the MusicBrainz `id` as a dict with an 'place' key.

    *Available includes*: {includes}r   r  r  s        r   get_place_by_idr  I  r  r   c                 :    t        d|||      }t        d| ||      S )zGet the event with the MusicBrainz `id` as a dict with an 'event' key.

    The event dict has the following keys:
    `id`, `type`, `name`, `time`, `disambiguation` and `life-span`.

    *Available includes*: {includes}r   r  r  s        r   get_event_by_idr  R  s)     +7H+9<IFXv66r   c                 :    t        d|||      }t        d| ||      S )zwGet the recording with the MusicBrainz `id` as a dict
    with a 'recording' key.

    *Available includes*: {includes}r   r  r  s        r   get_recording_by_idr  ^  s)     +;+9<IFR6::r   c                 :    t        d|||      }t        d| ||      S )zoGet the release with the MusicBrainz `id` as a dict with a 'release' key.

    *Available includes*: {includes}r   r  r  s        r   get_release_by_idr  h  s)    
 +9h+9<IF	2x88r   c                 :    t        d|||      }t        d| ||      S )zGet the release group with the MusicBrainz `id` as a dict
    with a 'release-group' key.

    *Available includes*: {includes}r   r  r  s        r   get_release_group_by_idr  q  s)     +?H+9<IFXv>>r   c                     t        d| |      S )zmGet the series with the MusicBrainz `id` as a dict with a 'series' key.

    *Available includes*: {includes}r   r  r  r   s     r   get_series_by_idr  |  s    
 "h//r   c                     t        d| |      S )ziGet the work with the MusicBrainz `id` as a dict with a 'work' key.

    *Available includes*: {includes}r   r  r  s     r   get_work_by_idr    s    
 H--r   c                     t        d| |      S )zgGet the url with the MusicBrainz `id` as a dict with a 'url' key.

    *Available includes*: {includes}r   r  r  s     r   get_url_by_idr    s    
 r8,,r   c                 "    t        d| ||||      S )zpSearch for annotations and return a dict with an 'annotation-list' key.

    *Available search fields*: {fields}r   r  r  r  r  r  r   s        r   search_annotationsr        
 ufeVVLLr   c                 "    t        d| ||||      S )zdSearch for areas and return a dict with an 'area-list' key.

    *Available search fields*: {fields}r	   r  r  s        r   search_areasr         
 vvFFr   c                 "    t        d| ||||      S )zhSearch for artists and return a dict with an 'artist-list' key.

    *Available search fields*: {fields}r
   r  r  s        r   search_artistsr        
 5&%HHr   c                 "    t        d| ||||      S )zfSearch for events and return a dict with an 'event-list' key.

    *Available search fields*: {fields}r   r  r  s        r   search_eventsr        
 %GGr   c                 "    t        d| ||||      S )zoSearch for instruments and return a dict with a 'instrument-list' key.

    *Available search fields*: {fields}r   r  r  s        r   search_instrumentsr	    r  r   c                 "    t        d| ||||      S )zeSearch for labels and return a dict with a 'label-list' key.

    *Available search fields*: {fields}r   r  r  s        r   search_labelsr    r  r   c                 "    t        d| ||||      S )zeSearch for places and return a dict with a 'place-list' key.

    *Available search fields*: {fields}r   r  r  s        r   search_placesr    r  r   c                 "    t        d| ||||      S )mSearch for recordings and return a dict with a 'recording-list' key.

    *Available search fields*: {fields}r   r  r  s        r   search_recordingsr    s     eVUFFKKr   c                 "    t        d| ||||      S )r  r   r  r  s        r   search_releasesr    s    
 E65&&IIr   c                 "    t        d| ||||      S )zySearch for release groups and return a dict
    with a 'release-group-list' key.

    *Available search fields*: {fields}r   r  r  s        r   search_release_groupsr    s     %OOr   c                 "    t        d| ||||      S )zfSearch for series and return a dict with a 'series-list' key.

    *Available search fields*: {fields}r   r  r  s        r   search_seriesr    r  r   c                 "    t        d| ||||      S )zcSearch for works and return a dict with a 'work-list' key.

    *Available search fields*: {fields}r   r  r  s        r   search_worksr    r  r   c                 f    t        d|g g       }|r||d<   |sd|d<   |r||d<   t        d| ||      S )a  Search for releases with a :musicbrainz:`Disc ID` or table of contents.

    When a `toc` is provided and no release with the disc ID is found,
    a fuzzy search by the toc is done.
    The `toc` should have to same format as :attr:`discid.Disc.toc_string`.
    When a `toc` is provided, the format of the discid itself is not
    checked server-side, so any value may be passed if searching by only
    `toc` is desired.

    If no toc matches in musicbrainz but a :musicbrainz:`CD Stub` does,
    the CD Stub will be returned. Prevent this from happening by
    passing `cdstubs=False`.

    By default only results that match a format that allows discids
    (e.g. CD) are included. To include all media formats, pass
    `media_format='all'`.

    The result is a dict with either a 'disc' , a 'cdstub' key
    or a 'release-list' (fuzzy match with TOC).
    A 'disc' has an 'offset-count', an 'offset-list' and a 'release-list'.
    A 'cdstub' key has direct 'artist' and 'title' keys.

    *Available includes*: {includes}r&   )r   r   tocnocdstubszmedia-formatr  )r  r   r  r  media_formatr   s         r   get_releases_by_discidr    sN    2 +8Xb8:<F
u y!-~"h77r   c                 :    t        d|||      }t        d| ||      S )zSearch for recordings with an :musicbrainz:`ISRC`.
    The result is a dict with an 'isrc' key,
    which again includes a 'recording-list'.

    *Available includes*: {includes}r'   r  )r'   r   r   r   r   s        r   get_recordings_by_isrcr     s)     +68+9<IFh77r   c                     t        d| |      S )zSearch for works with an :musicbrainz:`ISWC`.
    The result is a dict with a`work-list`.

    *Available includes*: {includes}r(   r  )r(   r   s     r   get_works_by_iswcr"    s     h//r   c                    t        |t              r|n|g}t        |    }t        ||       i }|j	                         D ]  \  }	}
|
s	|
||	<    t        |      dkD  r+t        ddj                  |j                               z         |r||d<   |r||d<   t        | |||      }|j                  |       t        | d||      S )Nr  zCan't have more than one of r   r  r  r   )r   r   r   r   r  r   r  r   keysr   updater  )r:   r   r  r  r   r   r   r   r   kr   filterps               r   _browse_implr(  &  s    %h5xH:H*62N>2
A||~ !AaD 1vz66;;=9QQRR5ajVq{+FHnl[GHHWHa00r   c                 .    | |||d}t        d||||      S )zGet all artists linked to a recording, a release or a release group.
    You need to give one MusicBrainz ID.

    *Available includes*: {includes})r   r   r   r   r
   r(  )r   r   release_groupr   r   r  r  r   s           r   browse_artistsr,  9  s,     % ,F (E66BBr   c                 ,    | ||d}t        d||||      S )zGet all events linked to a area, a artist or a place.
    You need to give one MusicBrainz ID.

    *Available includes*: {includes})r	   r
   r   r   r*  )r	   r
   r   r   r  r  r   s          r   browse_eventsr.  F  s)     F 5&&AAr   c                 (    d| i}t        d||||      S )zkGet all labels linked to a relase. You need to give a MusicBrainz ID.

    *Available includes*: {includes}r   r   r*  )r   r   r  r  r   s        r   browse_labelsr0  R  s!    
 !F5&&AAr   c                 (    d| i}t        d||||      S )zjGet all places linked to an area. You need to give a MusicBrainz ID.

    *Available includes*: {includes}r	   r   r*  )r	   r   r  r  r   s        r   browse_placesr2  Z  s     
 d^F5&&AAr   c                 *    | |d}t        d||||      S )zGet all recordings linked to an artist or a release.
    You need to give one MusicBrainz ID.

    *Available includes*: {includes}r
   r   r   r*  )r
   r   r   r  r  r   s         r   browse_recordingsr5  b  s$      "FXuffEEr   c
           	      4    | ||||d}
t        d|||	|
||      S )a  Get all releases linked to an artist, a label, a recording
    or a release group. You need to give one MusicBrainz ID.

    You can also browse by `track_artist`, which gives all releases where some
    tracks are attributed to that artist, but not the whole release.

    You can filter by :data:`musicbrainz.VALID_RELEASE_TYPES` or
    :data:`musicbrainz.VALID_RELEASE_STATUSES`.

    *Available includes*: {includes})r
   track_artistr   r   r   r   r*  )r
   r7  r   r   r+  r   r   r   r  r  r   s              r   browse_releasesr8  m  s8     *$,	.F
 	8UF> >r   c           	      .    | |d}t        d||||g |      S )zGet all release groups linked to an artist or a release.
    You need to give one MusicBrainz ID.

    You can filter by :data:`musicbrainz.VALID_RELEASE_TYPES`.

    *Available includes*: {includes}r4  r   r*  )r
   r   r   r   r  r  r   s          r   browse_release_groupsr:    s-      "F5L: :r   c                 (    d| i}t        d||||      S )ztGet urls by actual URL string.
    You need to give a URL string as 'resource'

    *Available includes*: {includes}resourcer   r*  )r<  r   r  r  r   s        r   browse_urlsr=    s!     (#Fx??r   c                 (    d| i}t        d||||      S )zGGet all works linked to an artist

    *Available includes*: {includes}r
   r   r*  )r
   r   r  r  r   s        r   browse_worksr?    s!    
 F%@@r   c                      t        dd      S )ztList the collections for the currently :func:`authenticated <auth>` user
    as a dict with a 'collection-list' key.r)   r   r  r   r   r   get_collectionsrA    s     b))r   c                 H    i }|r||d<   |r||d<   t        d| d|g |      S )Nr  r  r)   r   r  )r)   collection_typer  r  r   s        r   _do_collection_queryrD    s5    FfWo&vh_&MrSYZZr   c                     t        | d||      S )zList the artists in a collection.
    Returns a dict with a 'collection' key, which again has a 'artist-list'.

    See `Browsing`_ for how to use `limit` and `offset`.
    r"   rD  r)   r  r  s      r   get_artists_in_collectionrH    s      
IufEEr   c                     t        | d||      S )zList the releases in a collection.
    Returns a dict with a 'collection' key, which again has a 'release-list'.

    See `Browsing`_ for how to use `limit` and `offset`.
    r   rF  rG  s      r   get_releases_in_collectionrJ    s      
JvFFr   c                     t        | d||      S )zList the events in a collection.
    Returns a dict with a 'collection' key, which again has a 'event-list'.

    See `Browsing`_ for how to use `limit` and `offset`.
    eventsrF  rG  s      r   get_events_in_collectionrM          
HeVDDr   c                     t        | d||      S )zList the places in a collection.
    Returns a dict with a 'collection' key, which again has a 'place-list'.

    See `Browsing`_ for how to use `limit` and `offset`.
    placesrF  rG  s      r   get_places_in_collectionrQ    rN  r   c                     t        | d||      S )zList the recordings in a collection.
    Returns a dict with a 'collection' key, which again has a 'recording-list'.

    See `Browsing`_ for how to use `limit` and `offset`.
    r   rF  rG  s      r   get_recordings_in_collectionrS    s      
L%HHr   c                     t        | d||      S )zList the works in a collection.
    Returns a dict with a 'collection' key, which again has a 'work-list'.

    See `Browsing`_ for how to use `limit` and `offset`.
    r   rF  rG  s      r   get_works_in_collectionrU    s      
GUFCCr   c                 D    t        j                  |       }t        d|      S )z,Submits a set of {release_id1: barcode, ...}r   )r   make_barcode_requestr  )release_barcoder  s     r   submit_barcodesrY    s    &&7Ey%((r   c                     t               }| j                         D ]  \  }}t        |t              r|n|g||<    t	        j
                  |      }t        d|      S )zmSubmit ISRCs.
    Submits a set of {recording-id1: [isrc1, ...], ...}
    or {recording_id1: isrc, ...}.
    r   )r  r  r   r   r   make_isrc_requestr  )recording_isrcs	rec2isrcsrecr!   r  s        r   submit_isrcsr_    s\    
 I'--/ Ge",UD"9w	#G##I.E{E**r   c                      | j                         D ]8  \  }}|j                         D ]   \  }}t        |t              r|n|g| |   |<   " : t        j                  di | }t        d|      S )a  Submit user tags.
    Takes parameters named e.g. 'artist_tags', 'recording_tags', etc.,
    and of the form:
    {entity_id1: [tag1, ...], ...}
    If you only have one tag for an entity you can use a string instead
    of a list.

    The user's tags for each entity will be set to that list, adding or
    removing tags as necessary. Submitting an empty list for an entity
    will remove all tags for that entity by the user.
    rJ   r   )r  r   r   r   make_tag_requestr  )r,  r&  r   r  r   r  s         r   submit_tagsrb    sv      G1	 	GHB$.tT$:DF1IbM	GG "",V,Eue$$r   c                  D    t        j                  di | }t        d|      S )a  Submit user ratings.
    Takes parameters named e.g. 'artist_ratings', 'recording_ratings', etc.,
    and of the form:
    {entity_id1: rating, ...}

    Ratings are numbers from 0-100, at intervals of 20 (20 per 'star').
    Submitting a rating of 0 will remove the user's rating.
    ratingr   )r   make_rating_requestr  )r,  r  s     r   submit_ratingsrf    s$     %%//Ex''r   c                 F    dj                  |      }t        d| d|      S )zbAdd releases to a collection.
    Collection and releases should be identified by their MBIDs
    ;collection/
/releases/)r   r  r)   r   releaselists      r   add_releases_to_collectionrm    s"    
 ((8$KZMNNr   c                 F    dj                  |      }t        d| d|      S )zgRemove releases from a collection.
    Collection and releases should be identified by their MBIDs
    rh  ri  rj  )r   r  rk  s      r   remove_releases_from_collectionro    s"     ((8$K
KPQQr   r   )F)r  r  )N   g       @)r  )r   NNFr   )r  r   r&  loggingr|  rR  rI  rF  r  xml.etree.ElementTreeetreeElementTreexml.parsersr   warningsr   r   r   r   r   r  	getLoggerr  r  RELATABLE_TYPESRELATION_INCLUDESTAG_INCLUDESRATING_INCLUDESr   r   r   r   r   r   r   r   r  r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r   r	  r  r  r  r  r  objectr  HTTPPasswordMgrr/  rA  r<  rk  re  r  hasattrr  
ExpatErrorr  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r	  r  r  r  r  r  r  r  r  r   r"  r(  r,  r.  r0  r2  r5  r8  r:  r=  r?  rA  rD  rH  rJ  rM  rQ  rS  rU  rY  r_  rb  rf  rm  ro  )r:   s   0r   <module>r     sq
   
       
  % %      !w)*1 P4CD&Vg%D $n-0
i&)::0  		 )	) ,;	;0  0 9l ()0   		 )	) ,;	;0" y,'*;;lJ#0$ yk--<N%0&   		 '	' *;	;'00   		 )	)10:   	 '' *;;;0B iC0H <&'):;I0N 
O0P   		Q0Z ,[0\ YK]0^ :,_0d kL(?:=NN[<'/9<MM[<'/9<MM"G,|;oMPaa68IJ[<'*;;&',6HK\\%4FIZZ   P 

<
k9 x      
y ! j *  & "? O / G
6
 DFTV :114  x

4" &,--& --`-611 -55 >
fnn 
8@x 5,%%u'7'78%%
 
	 5. "' %Dt$T Tl ') ;, !#2
tE4-l4
1
=  "2B 6 6 "$Rb 8 8 &(" < < !#BR 7 7 !#BR 7 7 !#BR 	7 	7 %' ; ; 	#%br 9 9  )++-B? !? "$ 0 0  " . . ! - - < M !M 6G G 8I I 7H H < M !M 7H H 7H H ;37"L  L 9J J ?#7;P $P 8I I 6G G (*dQU  8  8F *,R(*	8 	8 %' 0 0 JLZ\ 1& 8!4trd
C 
C 7Dd4	B 	B 74 B B 7bT B B ;!4" F  F 9d$$"&rtD> >, ?#!%t"#%T$: $: 5$t @ @ 6rd A A*[FGEEID)	+%&
( 57 O :< Ry' Es   *W