
    lj                       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ZmZmZ d dl	m
Z
mZmZmZ d dlZ	 d dlZd dlmZ d dlmZ 	 d dlZd dlmZmZmZmZmZmZmZmZ d dlm Z m!Z! d dl"m#Z# d d	l$m%Z%m&Z&m'Z'm(Z( d d
l)m*Z* d dl+m,Z, d dl-m.Z. d dl/m0Z0m1Z1 d dl2m3Z4  G d de       Z5 G d de       Z6 G d de       Z7 G d de       Z8 G d de       Z9 G d de       Z: G d de       Z; G d de       Z< G d d       Z= G d! d"      Z>d+d#Z?d$ Z@ G d% d&e       ZA G d' d(e       ZB G d) d*e       ZCy# e$ r dZY w xY w# e$ r dZY w xY w),    N)datetime	timedeltatimezone)	parse_qsl	urlencodeurlsplit
urlunsplit)serialization)ed25519)BASE_HEADERSCONFIGTIMEOUTX_PLEX_ENABLE_FAST_CONNECTX_PLEX_IDENTIFIERlog	logfilterutils)
PlexObjectcached_data_property)
PlexClient)
BadRequestNotFoundUnauthorizedTwoFactorRequired)LibrarySection)
PlexServer)PlexSonosClient)SyncItemSyncList)_codesc                   f    e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
ZdZdZdZdZdZdZdZdZdZdZdZdZdZ fd	Zd Zd Zd Zed        Zed        Zed        Z e!d        Z"d Z#d  Z$d[d!Z%d" Z&d\d#Z'd$ Z(d% Z)d& Z*d' Z+d( Z,d) Z-	 	 d]d*Z.	 	 d]d+Z/	 	 d]d,Z0d- Z1d. Z2d^d/Z3d^d0Z4d1 Z5d2 Z6d3 Z7d4 Z8d5 Z9	 	 d_d6Z:d7 Z;d8 Z<d`d9Z=d`d:Z>d; Z?d< Z@d= ZAd> ZBd? ZCd@ ZDd\dAZEd\dBZFd\dCZGdD ZHd\dEZIdF ZJdG ZKdH ZLdadIZMdJ ZNdK ZOdL ZPdM ZQdN ZRdO ZSdP ZTdbdQZUe!dR        ZVdS ZWdT ZXdU ZYdV ZZdW Z[dX Z\dY Z] xZ^S )cMyPlexAccounta   MyPlex account and profile information. This object represents the data found Account on
        the myplex.tv servers at the url https://plex.tv/api/v2/user. You may create this object
        directly by passing in your username & password (or token). There is also a convenience
        method provided at :class:`~plexapi.server.PlexServer.myPlexAccount()` which will create
        and return this object.

        Parameters:
            username (str): Plex login username if not using a token.
            password (str): Plex login password if not using a token.
            token (str): Plex authentication token instead of username and password.
            session (requests.Session, optional): Use your own session object if you want to
                cache the http responses from PMS.
            timeout (int): timeout in seconds on initial connect to myplex (default config.TIMEOUT).
            code (str): Two-factor authentication code to use when logging in with username and password.
            remember (bool): Remember the account token for 14 days (Default True).

        Attributes:
            key (str): 'https://plex.tv/api/v2/user'
            adsConsent (str): Unknown.
            adsConsentReminderAt (str): Unknown.
            adsConsentSetAt (str): Unknown.
            anonymous (str): Unknown.
            authToken (str): The account token.
            backupCodesCreated (bool): If the two-factor authentication backup codes have been created.
            confirmed (bool): If the account has been confirmed.
            country (str): The account country.
            email (str): The account email address.
            emailOnlyAuth (bool): If login with email only is enabled.
            experimentalFeatures (bool): If experimental features are enabled.
            friendlyName (str): Your account full name.
            entitlements (List<str>): List of devices your allowed to use with this account.
            guest (bool): If the account is a Plex Home guest user.
            hasPassword (bool): If the account has a password.
            home (bool): If the account is a Plex Home user.
            homeAdmin (bool): If the account is the Plex Home admin.
            homeSize (int): The number of accounts in the Plex Home.
            id (int): The Plex account ID.
            joinedAt (datetime): Date the account joined Plex.
            locale (str): the account locale
            mailingListActive (bool): If you are subscribed to the Plex newsletter.
            mailingListStatus (str): Your current mailing list status.
            maxHomeSize (int): The maximum number of accounts allowed in the Plex Home.
            pin (str): The hashed Plex Home PIN.
            profileAutoSelectAudio (bool): If the account has automatically select audio and subtitle tracks enabled.
            profileDefaultAudioLanguage (str): The preferred audio language for the account.
            profileDefaultSubtitleLanguage (str): The preferred subtitle language for the account.
            profileAutoSelectSubtitle (int): The auto-select subtitle mode
                (0 = Manually selected, 1 = Shown with foreign audio, 2 = Always enabled).
            profileDefaultSubtitleAccessibility (int): The subtitles for the deaf or hard-of-hearing (SDH) searches mode
                (0 = Prefer non-SDH subtitles, 1 = Prefer SDH subtitles, 2 = Only show SDH subtitles,
                3 = Only shown non-SDH subtitles).
            profileDefaultSubtitleForced (int): The forced subtitles searches mode
                (0 = Prefer non-forced subtitles, 1 = Prefer forced subtitles, 2 = Only show forced subtitles,
                3 = Only show non-forced subtitles).
            protected (bool): If the account has a Plex Home PIN enabled.
            rememberExpiresAt (datetime): Date the token expires.
            restricted (bool): If the account is a Plex Home managed user.
            roles: (List<str>) Lit of account roles. Plexpass membership listed here.
            scrobbleTypes (List<int>): Unknown.
            subscriptionActive (bool): If the account's Plex Pass subscription is active.
            subscriptionDescription (str): Description of the Plex Pass subscription.
            subscriptionFeatures: (List<str>) List of features allowed on your Plex Pass subscription.
            subscriptionPaymentService (str): Payment service used for your Plex Pass subscription.
            subscriptionPlan (str): Name of Plex Pass subscription plan.
            subscriptionStatus (str): String representation of ``subscriptionActive``.
            subscriptionSubscribedAt (datetime): Date the account subscribed to Plex Pass.
            thumb (str): URL of the account thumbnail.
            title (str): The title of the account (username or friendly name).
            twoFactorEnabled (bool): If two-factor authentication is enabled.
            username (str): The account username.
            uuid (str): The account UUID.
    z6https://plex.tv/api/servers/{machineId}/shared_serverszhttps://plex.tv/api/home/usersz,https://plex.tv/api/home/users?title={title}z6https://plex.tv/api/home/users?invitedEmail={username}zAhttps://plex.tv/api/servers/{machineId}/shared_servers/{serverId}z'https://plex.tv/api/servers/{machineId}z(https://plex.tv/api/v2/sharings/{userId}z'https://plex.tv/api/home/users/{userId}z5https://plex.tv/api/v2/home/users/restricted/{userId}z#https://plex.tv/api/v2/users/signinz$https://plex.tv/api/v2/users/signoutz$https://plex.tv/api/v2/user/webhooksz8https://plex.tv/api/v2/user/{userUUID}/settings/opt_outsz https://plex.tv/api/v2/pins/linkz+https://plex.tv/api/v2/user/view_state_synczhttps://plex.tv/api/v2/pingzhttps://vod.provider.plex.tvzhttps://music.provider.plex.tvz!https://discover.provider.plex.tvz!https://metadata.provider.plex.tvzhttps://plex.tv/api/v2/userc                 6   t        j                  |xs t        j                  d            | _        |xs t        j                         | _        |xs t        | _	        g | _
        d| _        | j                  |||||      \  }}	t        t        | ?  | ||	       y )Nzauth.server_tokenr   )r   
add_secretr   get_tokenrequestsSession_sessionr   _timeout_sonos_cache_sonos_cache_timestamp_signinsuperr"   __init__)selfusernamepasswordtokensessiontimeoutcoderememberdatainitpath	__class__s             ;/DATA/.local/lib/python3.12/site-packages/plexapi/myplex.pyr/   zMyPlexAccount.__init__   s    **5+SFJJ?R4ST58#3#3#5*7&'#h$'RhmT+D$A    c                 `   | j                   r'| j                  | j                        | j                  fS |xs t        j                  d      |xs t        j                  d      |d}|r||d<   | j                  | j
                  | j                  j                  ||      }|| j
                  fS )Nzauth.myplex_usernamezauth.myplex_password)loginr2   
rememberMeverificationCode)methodr8   r5   )r&   querykeyr   r%   SIGNINr)   post)r0   r1   r2   r6   r7   r5   payloadr8   s           r;   r-   zMyPlexAccount._signin   s    ;;::dhh'11C,B!C FFJJ/E$F"

 *.G&'zz$++dmm.@.@wX_z`T[[  r<   c                 d    | j                  | j                  | j                  j                        S )zE Sign out of the Plex account. Invalidates the authentication token. rA   )rB   SIGNOUTr)   deleter0   s    r;   signoutzMyPlexAccount.signout   s#    zz$,,t}}/C/CzDDr<   c                    t        j                  |j                  j                  d            | _        g | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d      | _	        | j                  | _
        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _        |j                  j                  d	      | _        t        j                  t        |j                  j                  d
            | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        t        j                  t2        |j                  j                  d            | _        t        j                  t2        |j                  j                  d            | _        t        j8                  |j                  j                  d            | _        |j                  j                  d      | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _         t        j                  t2        |j                  j                  d            | _!        |j                  j                  d      | _"        t        j                  t        |j                  j                  d            | _#        t        j8                  |j                  j                  d            | _$        t        j                  t        |j                  j                  d            | _%        |j                  j                  d      jM                  d      D cg c]  }t        j                  t2        |       c}| _'        |j                  j                  d      | _(        |j                  j                  d      | _)        t        j                  t        |j                  j                  d             | _*        |j                  j                  d!      | _+        |j                  j                  d"      | _,        |j[                  d#      }t        j                  t        |j                  j                  d$            | _.        |j                  j                  d%      | _/        |j                  j                  d&      | _0        |j                  j                  d'      | _1        |j                  j                  d(      | _2        t        j8                  |j                  j                  d)      xs d*d+      | _3        |j[                  d,      }t        j                  t        |j                  j                  d-            | _4        |j                  j                  d.      | _5        |j                  j                  d/      | _6        t        j                  t2        |j                  j                  d0            | _7        t        j                  t2        |j                  j                  d1            | _8        t        j                  t2        |j                  j                  d2            | _9        d*| _:        y*c c}w )3/ Load attribute values from Plex XML response. 	authToken
adsConsentadsConsentReminderAtadsConsentSetAt	anonymousbackupCodesCreated	confirmedcountryemailemailOnlyAuthexperimentalFeaturesfriendlyNameguesthasPasswordhome	homeAdminhomeSizeidjoinedAtlocalemailingListActivemailingListStatusmaxHomeSizepin	protectedrememberExpiresAt
restrictedscrobbleTypes,thumbtitletwoFactorEnabledr1   uuidsubscriptionactivesubscriptionDescriptionpaymentServiceplanstatussubscribedAtNz%Y-%m-%d %H:%M:%S %ZprofileautoSelectAudiodefaultAudioLanguagedefaultSubtitleLanguageautoSelectSubtitledefaultSubtitleAccessibilitydefaultSubtitleForces);r   r$   attribr%   r&   	_webhooksrP   rQ   rR   rS   rO   r   castboolrT   rU   rV   rW   rX   rY   rZ   r[   r\   r]   r^   intr_   r`   
toDatetimera   rb   rc   rd   re   rf   rg   rh   ri   splitrj   rl   rm   rn   r1   ro   findsubscriptionActiverr   subscriptionPaymentServicesubscriptionPlansubscriptionStatussubscriptionSubscribedAtprofileAutoSelectAudioprofileDefaultAudioLanguageprofileDefaultSubtitleLanguageprofileAutoSelectSubtitle#profileDefaultSubtitleAccessibilityprofileDefaultSubtitleForcesservices)r0   r8   xrp   rw   s        r;   	_loadDatazMyPlexAccount._loadData   sI   **4;;??;+GH++//,7$(KKOO4J$K!#{{/@A5"'**T4;;??CW3X"YD$++//+*FG{{y1[[__W-
"ZZdkkooo.NO$)JJtT[[__E[5\$]! KKOON;ZZdkkoog&>?
 ::dDKKOOM,JKJJtT[[__V%<=	D$++//+*FG

3
(CD**S$++//$"78(()DEkkooh/!&D$++//BU2V!W!%1D!E ::c4;;??=+IJ;;??5)D$++//+*FG!&!1!1$++//BU2V!W**T4;;??<+HI:>++///:Z:`:`ad:ef:eQejja0:ef[[__W-
[[__W-
 %

4AS1T U
3KKOOF+	yy0"'**T<3F3F3J3J83T"U'+{{7P'Q$*6*=*=*A*ABR*S' , 3 3 7 7 ?"."5"5"9"9("C(-(8(8##N3;t=S)
% ))I&&+jjw~~7I7IJ[7\&]#+2>>+=+=>T+U(.5nn.@.@AZ.[+).C9K9KL`9a)b&38::c7>>CUCUVtCu3v0,1JJsGNN<N<NOf<g,h) 5 gs   .!a	c                 b    | j                   j                  d      }| j                  |ddd      S )Nrp   r`   featuresfeaturertagetag)_datar   	listAttrs)r0   rp   s     r;   subscriptionFeaturesz"MyPlexAccount.subscriptionFeatures   s+    zz~6~~lDz	~RRr<   c                 @    | j                  | j                  ddd      S )Nr`   entitlementsentitlementr   r   r   rK   s    r;   r   zMyPlexAccount.entitlements   s    ~~djj$^-~XXr<   c                 @    | j                  | j                  ddd      S )Nr`   rolesroler   r   rK   s    r;   r   zMyPlexAccount.roles   s    ~~djj$W6~JJr<   c                     | j                   S )zL Returns the authentication token for the account. Alias for ``authToken``. )rO   rK   s    r;   authenticationTokenz!MyPlexAccount.authenticationToken   s     ~~r<   c                 ^    | j                  | j                        }| j                  |       | S )z Perform the actual reload. )rB   rC   _invalidateCacheAndLoadData)r0   kwargsr8   s      r;   _reloadzMyPlexAccount._reload   s'    zz$((#((.r<   c                     t        j                         }| j                  r| j                  |d<   |j                  |       |S )zF Returns dict containing base headers for all requests to the server. X-Plex-Token)r   copyr&   updater0   r   headerss      r;   _headerszMyPlexAccount._headers   s6    ##%;;&*kkGN#vr<   c           	          |xs | j                   j                  }|xs | j                  }t        j                  d|j
                  j                         ||j                  dd              | j                  di |xs i } ||f||d|}|j                  dvrt        j                  |j                        d   }|j                  j                  dd      }d	|j                   d
| d|j                   d| }	|j                  dk(  r$d|j                  v rt        |	      t        |	      |j                  dk(  rt        |	      |j                  dk(  rd|j                  v rt        |	      t!        |	      d|j"                  j                  dd      v r|j%                         S d|j"                  j                  dd      v r|j                  j'                         S t)        j*                  |j                        S )Nz%s %s %sjson r   r5            r   
 () ; i  zverification codei  i  zInvalid tokenapplication/jsonContent-Typez
text/plain )r)   r%   r*   r   debug__name__upperr   status_codecodestextreplaceurlr   r   r   r   r   r   stripr   parseXMLString)
r0   r   rA   r   r5   r   responsecodenameerrtextmessages
             r;   rB   zMyPlexAccount.query   s   ,4==,,*T]]		*foo335sFJJvr<RS$--0'-R0#JwJ6J6yy!5!56q9Hmm++D#6G(../r(2hll^1WIVG##s*&(--7+G44"7++%%,w''%%,HMM1Q"7++ ))!1!1!5!5nb!II==?"X--11."EE==&&((##HMM22r<   c                     | j                  | j                        }|$t        j                  t        |j
                        S y)zs Ping the Plex.tv API.
            This will refresh the authentication token to prevent it from expiring.
        F)rB   PINGr   r   r   r   )r0   pongs     r;   pingzMyPlexAccount.ping  s4     zz$))$::dDII..r<   c                     | j                         D ]B  }|r+|j                  j                         |j                         k(  s|j                  |k(  s@|c S  t	        d|       )z Returns the :class:`~plexapi.myplex.MyPlexDevice` that matches the name specified.

            Parameters:
                name (str): Name to match against.
                clientId (str): clientIdentifier to match against.
        zUnable to find device )devicesnamelowerclientIdentifierr   )r0   r   clientIddevices       r;   r   zMyPlexAccount.device  sY     llnF**,

<@W@W[c@c % /v677r<   c                 ~    | j                  t        j                        }|D cg c]  }t        | |       c}S c c}w )z^ Returns a list of all :class:`~plexapi.myplex.MyPlexDevice` objects connected to the server. )rB   MyPlexDevicerC   r0   r8   elems      r;   r   zMyPlexAccount.devices)  s5    zz,**+59:TTT4(T:::   :c                     | j                         D ]@  }|j                  j                         |j                         k(  s|j                  |k(  s>|c S  t	        d|       )z Returns the :class:`~plexapi.myplex.MyPlexResource` that matches the name specified.

            Parameters:
                name (str): Name  or machine identifier to match against.
        zUnable to find resource )	resourcesr   r   r   r   )r0   r   resources      r;   r   zMyPlexAccount.resource.  sX     (H}}""$

48Q8QUY8Y ) 1$899r<   c                 ~    | j                  t        j                        }|D cg c]  }t        | |       c}S c c}w )z` Returns a list of all :class:`~plexapi.myplex.MyPlexResource` objects connected to the server. )rB   MyPlexResourcerC   r   s      r;   r   zMyPlexAccount.resources9  s5    zz.,,-7;<tttT*t<<<r   c                     d| j                   vrg S t        j                         }|| j                  z
  dkD  r7|| _        | j                  d      }|D cg c]  }t	        | |       c}| _        | j
                  S c c}w )Ncompanions_sonos   zhttps://sonos.plex.tv/resources)r   timer,   rB   r   r+   )r0   tr8   r   s       r;   sonos_speakerszMyPlexAccount.sonos_speakers>  sz    T%>%>>IIIKt***Q.*+D'::?@DIM Nt!< ND    !Os   A:c                 J    t        fd| j                         D        d       S )Nc              3      K   | ]6  }|j                   j                  d       d   j                         k(  s3| 8 yw)+r   N)rm   r   r   ).0r   r   s     r;   	<genexpr>z.MyPlexAccount.sonos_speaker.<locals>.<genexpr>K  s7     _ 51s9KA9N9T9T9VZ^9^Q 5s   4??nextr   )r0   r   s    `r;   sonos_speakerzMyPlexAccount.sonos_speakerJ  s     _ 3 3 5_aeffr<   c                 J    t        fd| j                         D        d       S )Nc              3   Z   K   | ]"  }|j                   j                        s| $ y wN)machineIdentifier
startswith)r   r   
identifiers     r;   r   z4MyPlexAccount.sonos_speaker_by_id.<locals>.<genexpr>N  s(     d 519L9L9W9WXb9cQ 5s    ++r   )r0   r   s    `r;   sonos_speaker_by_idz!MyPlexAccount.sonos_speaker_by_idM  s     d 3 3 5dfjkkr<   c
                    t        |t              r|j                  n|}
t        |t              r|j                  n|}| j                  ||      }|||
d|rdnd|rdnd|rdnd| j                  |xs i       | j                  |xs i       | j                  |	xs i       dd}ddi}| j                  j                  |      }| j                  || j                  j                  ||	      S )
   Share library content with the specified user.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexUser`): `MyPlexUser` object, username, or email
                    of the user to be added.
                server (:class:`~plexapi.server.PlexServer`): `PlexServer` object, or machineIdentifier
                    containing the library sections to share.
                sections (List<:class:`~plexapi.library.LibrarySection`>): List of `LibrarySection` objects, or names
                    to be shared (default None). `sections` must be defined in order to update shared libraries.
                allowSync (Bool): Set True to allow user to sync content.
                allowCameraUpload (Bool): Set True to allow user to upload photos.
                allowChannels (Bool): Set True to allow user to utilize installed channels.
                filterMovies (Dict): Dict containing key 'contentRating' and/or 'label' each set to a list of
                    values to be filtered. ex: `{'contentRating':['G'], 'label':['foo']}`
                filterTelevision (Dict): Dict containing key 'contentRating' and/or 'label' each set to a list of
                    values to be filtered. ex: `{'contentRating':['G'], 'label':['foo']}`
                filterMusic (Dict): Dict containing key 'label' set to a list of values to be filtered.
                    ex: `{'label':['foo']}`
        library_section_idsinvited_email10	allowSyncallowCameraUploadallowChannelsfilterMoviesfilterTelevisionfilterMusic	server_idshared_serversharing_settingsr   r   	machineIdr   r   )
isinstance
MyPlexUserr1   r   r   _getSectionIds_filterDictToStrFRIENDINVITEformatrB   r)   rE   )r0   userserversectionsr  r  r  r  r  r  r1   r  
sectionIdsparamsr   r   s                   r;   inviteFriendzMyPlexAccount.inviteFriendP  s    * %/tZ$@4==d0:6:0NF,,TZ	((H=
"5?RZ[%.cC->cC)6#C $ 5 5l6Hb I$($9$9:J:Pb$Q#44[5FBG!
 "#56&&&;zz#t}}11zPPr<   c
                    t        |t              r|j                  n|}
| j                  ||      }ddi}| j                  j                  |      }| j                  || j                  j                  |      }i }|j                  d      D ]   }|j                  j                  d      |d<   " t        j                  |       |
||d   d|rdnd	|rdnd	|rdnd	| j                  |xs i       | j                  |xs i       | j                  |	xs i       d
d}| j                  j                  |
      }| j                  || j                  j                  ||      }||fS )r   r   r   )rm   r   .r`   r   
invited_idr   r   r   r  r  r  )r  r   r   r  HOMEUSERCREATEr  rB   r)   rE   findallr~   r%   r   r   r  r  )r0   r  r  r  r  r  r  r  r  r  r  r  r   r   user_creationuserIdsr   r  library_assignments                      r;   createHomeUserzMyPlexAccount.createHomeUserx  sa   * 1;6:0NF,,TZ	((:
!#56!!((t(4

3(:(:G
L!))#.D KKOOD1GDM / 			'"5?wW[}]%.cC->cC)6#C $ 5 5l6Hb I$($9$9:J:Pb$Q#44[5FBG!
 &&&;!ZZT]]-?-?fV]Z^000r<   c
                    ddi}
t        |t              r|j                  }n[|| j                         D cg c]  }|j                   c}v r| j	                  |      j                  }n|}| j
                  j                  |      }| j                  || j                  j                  |
      }t        |t              r|j                  n|}| j                  ||      }|||d|rdnd|rdnd|rdnd| j                  |xs i       | j                  |xs i       | j                  |	xs i       dd	}| j                  j                  |
      }| j                  || j                  j                  ||
      }||fS | j
                  j                  |      }| j                  || j                  j                  |
      S c c}w )r   r   r   )r1   r  r   r   r   r   r  r  r  )r  r  r1   usersr  EXISTINGUSERr  rB   r)   rE   r   r   r  r  r  )r0   r  r  r  r  r  r  r  r  r  r   r1   _usernewUserr   r!  r  r  r  r#  s                       r;   createExistingUserz MyPlexAccount.createExistingUser  s   * "#56dJ'}}H$**,?,enn,??yy//H G##**G*<C JJsDMM,>,>JPM4>vz4R00X^I,,VX>J&9CV]!^)2#1B#-:c$($9$9,:L"$M(,(=(=>N>TRT(U#'#8#89J#K%F ##**Y*?C!%C1C1C&Za!b "444&&&9zz#t}}117zCC5 @s   Gc                     t        |t              r|n| j                  |      }| j                  j	                  |j
                        }| j                  || j                  j                        S )z Remove the specified user from your friends.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexUser` or str): :class:`~plexapi.myplex.MyPlexUser`,
                    username, or email of the user to be removed.
        userId)	r  r  r  FRIENDUPDATEr  r`   rB   r)   rJ   r0   r  r   s      r;   removeFriendzMyPlexAccount.removeFriend  sT     "$
3t4&&dgg&6zz#t}}3344r<   c                     t        |t              r|n| j                  |      }| j                  j	                  |j
                        }| j                  || j                  j                        S )z Remove the specified user from your home users.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexUser` or str): :class:`~plexapi.myplex.MyPlexUser`,
                    username, or email of the user to be removed.
        r,  )	r  r  r  HOMEUSERr  r`   rB   r)   rJ   r/  s      r;   removeHomeUserzMyPlexAccount.removeHomeUser  sR     "$
3t4mm""$''"2zz#t}}3344r<   c                 F   t        |t              r|n| j                  |      }| j                   d|j                   d}i }|r||d<   | j                  || j                  j                  |      }|j                  j                  d      }t        || j                        S )a   Returns a new :class:`~plexapi.myplex.MyPlexAccount` object switched to the given home user.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexUser` or str): :class:`~plexapi.myplex.MyPlexUser`,
                    username, or email of the home user to switch to.
                pin (str): PIN for the home user (required if the home user has a PIN set).

            Example:

                .. code-block:: python

                    from plexapi.myplex import MyPlexAccount
                    # Login to a Plex Home account
                    account = MyPlexAccount('<USERNAME>', '<PASSWORD>')
                    # Switch to a different Plex Home user
                    userAccount = account.switchHomeUser('Username')

        /z/switchrf   r  r   )r3   r4   )r  r  r  	HOMEUSERSr`   rB   r)   rE   r~   r%   r"   )r0   r  rf   r   r  r8   	userTokens          r;   switchHomeUserzMyPlexAccount.switchHomeUser  s    & "$
3t4 $'''2F5Mzz#t}}11&zAKKOO$9:	9dmmDDr<   c                     | j                   j                  | j                        }d|i}|r||d<   | j                  || j                  j
                  |      S )z Set a new Plex Home PIN for the account.

            Parameters:
                newPin (str): New PIN to set for the account.
                currentPin (str): Current PIN for the account (required to change the PIN).
        r,  rf   
currentPinr6  )r2  r  r`   rB   r)   put)r0   newPinr;  r   r  s        r;   setPinzMyPlexAccount.setPin  sT     mm""$''"2#-F< zz#t}}00z@@r<   c                 &    | j                  d|      S )z Remove the Plex Home PIN for the account.

            Parameters:
                currentPin (str): Current PIN for the account (required to remove the PIN).
        r   )r>  )r0   r;  s     r;   	removePinzMyPlexAccount.removePin  s     {{2z**r<   c                     t        |t              r|n| j                  |      }| j                  j	                  |j
                        }d|i}| j                  || j                  j                  |      S )ap   Set a new Plex Home PIN for a managed home user. This must be done from the Plex Home admin account.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexUser` or str): :class:`~plexapi.myplex.MyPlexUser`
                    or username of the managed home user.
                newPin (str): New PIN to set for the managed home user.
        r,  rf   r6  	r  r  r  MANAGEDHOMEUSERr  r`   rB   r)   rE   )r0   r  r=  r   r  s        r;   setManagedUserPinzMyPlexAccount.setManagedUserPin%  sa     "$
3t4"")))9zz#t}}11&zAAr<   c                     t        |t              r|n| j                  |      }| j                  j	                  |j
                        }ddi}| j                  || j                  j                  |      S )a)   Remove the Plex Home PIN for a managed home user. This must be done from the Plex Home admin account.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexUser` or str): :class:`~plexapi.myplex.MyPlexUser`
                    or username of the managed home user.
        r,  r@     r6  rB  )r0   r  r   r  s       r;   removeManagedUserPinz"MyPlexAccount.removeManagedUserPin2  sb     "$
3t4"")))9q!zz#t}}11&zAAr<   c                    t        |t              r|n| j                  |d      }t        |j                        t        |j
                        t        |j                        d}t        j                  d|j                   z   t        j                  |      z   }| j                  || j                  j                        S )a   Accept a pending friend invite from the specified user.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexInvite` or str): :class:`~plexapi.myplex.MyPlexInvite`,
                    username, or email of the friend invite to accept.
        F)includeSentfriendr]   r  r5  )r  MyPlexInvitependingInviter   rK  r]   r  REQUESTSr`   r   joinArgsrB   r)   r<  r0   r  inviter  r   s        r;   acceptInvitezMyPlexAccount.acceptInvite>  s     $D,7T=O=OPTbg=O=h&--($&--(

 ##&))o5v8NNzz#t}}0011r<   c                    t        |t              r|n| j                  |d      }t        |j                        t        |j
                        t        |j                        d}t        j                  d|j                   z   t        j                  |      z   }| j                  || j                  j                        S )a   Cancel a pending firend invite for the specified user.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexInvite` or str): :class:`~plexapi.myplex.MyPlexInvite`,
                    username, or email of the friend invite to cancel.
        F)includeReceivedrJ  r5  )r  rL  rM  r   rK  r]   r  	REQUESTEDr`   r   rO  rB   r)   rJ   rP  s        r;   cancelInvitezMyPlexAccount.cancelInviteN  s     $D,7T=O=OPTfk=O=l&--($&--(

 $$699+69OOzz#t}}3344r<   c                    d}d}t        |t              r|n| j                  |      }t        |t              r|j                  n|}| j                  ||      }ddi}|j                  D cg c]  }|j                  |k(  s| }}|r6|r4|d   j                  }|d|id}| j                  j                  ||      }n.|||j                  dd}| j                  j                  |	      }|r|r|d
u r*| j                  || j                  j                  ||      }n}d|j                  dd      v r*| j                  || j                  j                  ||      }n?| j                  || j                  j                   ||      }nt#        j$                  d       | j&                  j                  |j                        }i }t        |t(              r	|rdnd|d<   t        |t(              r	|rdnd|d<   t        |t(              r	|rdnd|d<   t        |t*              r| j-                  |xs i       |d<   t        |	t*              r| j-                  |	xs i       |d<   t        |t*              r| j-                  |
xs i       |d<   |r>|t/        j0                  |      z  }| j                  || j                  j                         }||fS c c}w )a   Update the specified user's share settings.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexUser`): `MyPlexUser` object, username, or email
                    of the user to be updated.
                server (:class:`~plexapi.server.PlexServer`): `PlexServer` object, or machineIdentifier
                    containing the library sections to share.
                sections (List<:class:`~plexapi.library.LibrarySection`>): List of `LibrarySection` objects, or names
                    to be shared (default None). `sections` must be defined in order to update shared libraries.
                removeSections (Bool): Set True to remove all shares. Supersedes sections.
                allowSync (Bool): Set True to allow user to sync content.
                allowCameraUpload (Bool): Set True to allow user to upload photos.
                allowChannels (Bool): Set True to allow user to utilize installed channels.
                filterMovies (Dict): Dict containing key 'contentRating' and/or 'label' each set to a list of
                    values to be filtered. ex: `{'contentRating':['G'], 'label':['foo']}`
                filterTelevision (Dict): Dict containing key 'contentRating' and/or 'label' each set to a list of
                    values to be filtered. ex: `{'contentRating':['G'], 'label':['foo']}`
                filterMusic (Dict): Dict containing key 'label' set to a list of values to be filtered.
                    ex: `{'label':['foo']}`
        r   r   r   r   r   )r  r	  r  serverIdr  r  Tr  r  r	  zLSection name, number of section object is required changing library sectionsr,  r   r   r  r  r  r  r  r  )r  r  r  r   r   r  serversr`   FRIENDSERVERSr  r  rB   r)   rJ   r%   rE   r<  r   warningr.  r   dictr  r   rO  )r0   r  r  r  removeSectionsr  r  r  r  r  r  response_filtersresponse_serversr  r  r   suser_serversrY  r  r   s                        r;   updateFriendzMyPlexAccount.updateFriend^  s   . !$
3t40:6:0NF,,TZ	((H=
!#56#'<<T<a13F3F)3S<TJ#A))H#,@UWa?bcF$$++i(+SC#,?IY]Y`Y`'acF##**Y*?Cz%#'::c4==3G3Gf^e:#f OR!@@#'::c4==3E3EF\c:#d #'::c4==3D3D6[b:#c KKfg&&dgg&6i&)2#F;'.1B#F&'mT*-:cF?#lD)%)%:%:<;M2%NF>"&-)-)>)>?O?USU)VF%&mT*$($9$9+:K$LF=!5>>&))C#zz#t}}/@/@A!111K Us   *K	?K	c                    t        |      }| j                         D ]  }|j                         |j                  j                         k(  r|c S |j                  s?|j
                  sL|j                  sY|j                         |j                  j                         |j
                  j                         t        |j                        fv s|c S  t        d|       )z Returns the :class:`~plexapi.myplex.MyPlexUser` that matches the specified username or email.

            Parameters:
                username (str): Username, email or id of the user to return.
        zUnable to find user )strr&  r   rm   r1   rW   r`   r   )r0   r1   r  s      r;   r  zMyPlexAccount.user  s     x=JJLD~~4::#3#3#55--DJJ477x~~?O]]((*DJJ,<,<,>DGGM@N ! -hZ899r<   c                 n    | j                  t        j                        }| j                  |t              S )zf Returns a list of all :class:`~plexapi.myplex.MyPlexUser` objects connected to your account.
        cls)rB   r  rC   	findItems)r0   r   s     r;   r&  zMyPlexAccount.users  s)     zz*..)~~d
~33r<   c                 h   t        |      }| j                  ||      D ]  }|j                  s|j                  s|j                  s*|j                         |j                  j                         |j                  j                         t        |j                        fv s|c S  t        d|       )a   Returns the :class:`~plexapi.myplex.MyPlexInvite` that matches the specified username or email.
            Note: This can be a pending invite sent from your account or received to your account.

            Parameters:
                username (str): Username, email or id of the user to return.
                includeSent (bool): True to include sent invites.
                includeReceived (bool): True to include received invites.
        zUnable to find invite )re  pendingInvitesr1   rW   r`   r   r   )r0   r1   rI  rT  rQ  s        r;   rM  zMyPlexAccount.pendingInvite  s     x=))+GFFLLVYY8>>CS__**,fll.@.@.BC		NSDT H
 /z:;;r<   c                     g }|r9| j                  t        j                        }|| j                  |t              z  }|r9| j                  t        j                        }|| j                  |t              z  }|S )av   Returns a list of all :class:`~plexapi.myplex.MyPlexInvite` objects connected to your account.
            Note: This includes all pending invites sent from your account and received to your account.

            Parameters:
                includeSent (bool): True to include sent invites.
                includeReceived (bool): True to include received invites.
        rg  )rB   rL  rU  ri  rN  )r0   rI  rT  invitesr   s        r;   rk  zMyPlexAccount.pendingInvites  sj     ::l445Dt~~d~==G::l334Dt~~d~==Gr<   c                    |sg S i }t        |t              r|j                  n|}| j                  j	                  |      }| j                  || j                  j                        }|d   D ]  }t        j                  t        |j                  j                  d            }t        j                  t        |j                  j                  d            }	|j                  j                  dd      j                         }
|||<   |||	<   |||
<    t        j                  |       g }|D ]B  }t        |t              r|j                   n|j                         }|j#                  ||          D |S )zW Converts a list of section objects or names to sectionIds needed for library sharing. r  r   r`   rC   rm   r   )r  r   r   PLEXSERVERSr  rB   r)   r%   r   r   r   r~   r   r   r   r   rC   append)r0   r  r  allSectionIdsr   r   r8   r   _id_key_titler  section
sectionKeys                 r;   r  zMyPlexAccount._getSectionIds  s3   8B6:8VF44\b%%0A%Bzz#t}}001GD**S$++//$"78C::c4;;??5#9:D[[__Wb1779F!$M#"%M$$'M&!  			- 
G(27N(KQXQ^Q^Q`JmJ78   r<   c                     g }|j                         D ]<  \  }}|dvrt        d|       |j                  | ddj                  |              > dj                  |      S )zC Converts friend filters to a string representation for transport. )contentRatinglabelzcontentRating!zlabel!zUnknown filter key: =z%2C|)itemsr   rp  join)r0   
filterDictvaluesrC   valss        r;   r  zMyPlexAccount._filterDictToStr  sl    #))+ICPP #7u!=>>MMSE5::d#3"456 , xxr<   c                 J    | j                   d d  |gz   }| j                  |      S r   )r   setWebhooksr0   r   urlss      r;   
addWebhookzMyPlexAccount.addWebhook  s(    ~~a C5(%%r<   c                     t        j                   | j                        }||vrt        d|       |j                  |       | j	                  |      S )NzWebhook does not exist: )r   r   r   remover  r  s      r;   deleteWebhookzMyPlexAccount.deleteWebhook  sK    yy(d?7u=>>C%%r<   c                    t        j                  d|       t        |      rd|inddi}| j                  | j                  | j
                  j                  |      }| j                  |dd      | _        | j                  S )	NzSetting webhooks: %szurls[]r  r   )r8   r   webhookr   )	r   infolenrB   WEBHOOKSr)   rE   r   r   )r0   r  r8   s      r;   r  zMyPlexAccount.setWebhooks	  si    '.#&t9$62,zz$--););$zGe)D~~r<   c                     | j                  | j                        }| j                  |dd      | _        | j                  S )Nr   r  r  )rB   r  r   r   r0   r8   s     r;   webhookszMyPlexAccount.webhooks  s4    zz$--(e)D~~r<   c                     i }|t        |      |d<   |t        |      |d<   d}| j                  || j                  j                  |      S )zm Opt in or out of sharing stuff with plex.
            See: https://www.plex.tv/about/privacy-legal/
        optOutPlaybackoptOutLibraryStatsz#https://plex.tv/api/v2/user/privacy)rA   r8   )r   rB   r)   r<  )r0   playbacklibraryr  r   s        r;   optOutzMyPlexAccount.optOut  sW     '*8}F#$+.w<F'(3zz#dmm&7&7fzEEr<   c                     |r|j                   }n|t        }| j                  t        j                  j                  |            }t        | |      S )a   Returns an instance of :class:`~plexapi.sync.SyncList` for specified client.

            Parameters:
                client (:class:`~plexapi.myplex.MyPlexDevice`): a client to query SyncItems for.
                clientId (str): an identifier of a client to query SyncItems for.

            If both `client` and `clientId` provided the client would be preferred.
            If neither `client` nor `clientId` provided the clientId would be set to current clients's identifier.
        r   )r   r   rB   r   rC   r  )r0   clientr   r8   s       r;   	syncItemszMyPlexAccount.syncItems!  sJ     ..H(Hzz(,,--x-@Ad##r<   c                    |s|st         }|s8| j                         D ]  }|j                  |k(  s|} n |st        d|       d|j                  vrt        d      i d|j
                  d|j                  d|j                  d|j                  d|j                  d	|j                  j                  d
t        t        |j                  j                              dt        t        |j                  d      r|j                  j                   nd      d|j"                  dt        |j$                  j&                        dt        |j$                  j(                        dt        |j$                  j*                        dt        |j$                  j,                        d|j$                  j.                  dt        |j$                  j0                        dt        |j$                  j2                        d|j$                  j4                  }t6        j8                  j;                  |j                        }| j=                  || j>                  j@                  |      }tC        | |d|j                        S )a;   Adds specified sync item for the client. It's always easier to use methods defined directly in the media
            objects, e.g. :func:`~plexapi.video.Video.sync`, :func:`~plexapi.audio.Audio.sync`.

            Parameters:
                client (:class:`~plexapi.myplex.MyPlexDevice`): a client for which you need to add SyncItem to.
                clientId (str): an identifier of a client for which you need to add SyncItem to.
                sync_item (:class:`~plexapi.sync.SyncItem`): prepared SyncItem object with all fields set.

            If both `client` and `clientId` provided the client would be preferred.
            If neither `client` nor `clientId` provided the clientId would be set to current clients's identifier.

            Returns:
                :class:`~plexapi.sync.SyncItem`: an instance of created syncItem.

            Raises:
                :exc:`~plexapi.exceptions.BadRequest`: When client with provided clientId wasn't found.
                :exc:`~plexapi.exceptions.BadRequest`: Provided client doesn't provides `sync-target`.
        z"Unable to find client by clientId=sync-targetz,Received client doesn't provides sync-targetzSyncItem[title]zSyncItem[rootTitle]zSyncItem[metadataType]zSyncItem[machineIdentifier]zSyncItem[contentType]zSyncItem[Policy][scope]zSyncItem[Policy][unwatched]zSyncItem[Policy][value]valuer   zSyncItem[Location][uri]z#SyncItem[MediaSettings][audioBoost]z(SyncItem[MediaSettings][maxVideoBitrate]z%SyncItem[MediaSettings][musicBitrate]z%SyncItem[MediaSettings][photoQuality]z(SyncItem[MediaSettings][photoResolution]z%SyncItem[MediaSettings][subtitleSize]z%SyncItem[MediaSettings][videoQuality]z(SyncItem[MediaSettings][videoResolution]r  rA   r  N)r   )"r   r   r   r   providesrm   	rootTitlemetadataTyper   contentTypepolicyscopere  r   	unwatchedhasattrr  locationmediaSettings
audioBoostmaxVideoBitratemusicBitratephotoQualityphotoResolutionsubtitleSizevideoQualityvideoResolutionr   rC   r  rB   r)   rE   r   )r0   	sync_itemr  r   r   r  r   r8   s           r;   synczMyPlexAccount.sync4  sp   & h(H,,.**h6#F )
  #EhZ!PQQ/KLL
y
!9#6#6
 %i&<&<
 *9+F+F	

 $Y%:%:
 &y'7'7'='=
 *3s93C3C3M3M/N+O
 &sWYM]M]_fEg9+;+;+A+Amn'o
 &y'9'9
 23y7N7N7Y7Y3Z
 7I<S<S<c<c8d
 4S9P9P9]9]5^
 4S9P9P9]9]5^
 7	8O8O8_8_
 4S9P9P9]9]5^
  4S9P9P9]9]5^!
" 7	8O8O8_8_#
( ll!!6+B+B!Czz#dmm&8&8zHdD6;R;RSSr<   c           
      v   | j                   j                  d| j                         t              }|j                  dvrit        j                  |j                        d   }|j                  j                  dd      }t        d|j                   d| d|j                   d	|       |j                         d
   S )z Returns a str, a new "claim-token", which you can use to register your new Plex Server instance to your
            account.
            See: https://hub.docker.com/r/plexinc/pms-docker/, https://www.plex.tv/claim/
        z$https://plex.tv/api/claim/token.jsonr   r   r   r   r   r   r   r   r3   )r)   r%   r   r   r   r   r   r   r   r   r   )r0   r   r   r   s       r;   
claimTokenzMyPlexAccount.claimTokeno  s    
 ==$$%KUYUbUbUdnu$v6yy!5!56q9Hmm++D#6Gq!5!5 6b
!HLL>QST[S\]^^}}w''r<   c                     | j                         D cg c]!  }|j                  dk(  s|j                  s |# }}g }|D ]5  }|j                         }|j	                  |j                  ||d             7 |S c c}w )a   Get Play History for all library sections on all servers for the owner.

            Parameters:
                maxresults (int): Only return the specified number of results (optional).
                mindate (datetime): Min datetime to return results from.
        r  rF  
maxresultsmindate	accountID)r   r  ownedconnectextendhistory)r0   r  r  r   rZ  histr  conns           r;   r  zMyPlexAccount.history{  sv     #nn.U.!**2HQWW1.UF>>#DKK
GWXYZ   Vs   A9A9A9c                     | j                   j                  | j                        }| j                  |      }| j	                  |t
        d      S )zm Returns a list of user account Online Media Sources settings :class:`~plexapi.myplex.AccountOptOut`
        userUUIDr  )rh  r   )OPTOUTSr  ro   rB   ri  AccountOptOut)r0   r   r   s      r;   onlineMediaSourcesz MyPlexAccount.onlineMediaSources  sA     ll!!499!5zz#~~dH~EEr<   c                 `    | j                  | j                   d      }| j                  |      S )zG Returns a list of VOD Hub items :class:`~plexapi.library.Hub`
        /hubs)rB   VODri  r  s     r;   videoOnDemandzMyPlexAccount.videoOnDemand  s,     zzTXXJe,-~~d##r<   c                 `    | j                  | j                   d      }| j                  |      S )zI Returns a list of tidal Hub items :class:`~plexapi.library.Hub`
        r  )rB   MUSICri  r  s     r;   tidalzMyPlexAccount.tidal  s,     zzTZZL./~~d##r<   c                    ddd}|sd}|r||d<   |rt        j                  |      |d<   |j                  |       | j                   d| t        j                  |       } | j
                  | j                  ||      fi |S )a   Returns a list of :class:`~plexapi.video.Movie` and :class:`~plexapi.video.Show` items in the user's watchlist.
            Note: The objects returned are from Plex's online metadata. To get the matching item on a Plex server,
            search for the media using the guid.

            Parameters:
                filter (str, optional): 'available' or 'released' to only return items that are available or released,
                    otherwise return all items.
                sort (str, optional): In the format ``field:dir``. Available fields are ``watchlistedAt`` (Added At),
                    ``titleSort`` (Title), ``originallyAvailableAt`` (Release Date), or ``rating`` (Critic Rating).
                    ``dir`` can be ``asc`` or ``desc``.
                libtype (str, optional): 'movie' or 'show' to only return movies or shows, otherwise return all items.
                maxresults (int, optional): Only return the specified number of results.
                **kwargs (dict): Additional custom filters to apply to the search results.


            Example:

                .. code-block:: python

                    # Watchlist for released movies sorted by critic rating in descending order
                    watchlist = account.watchlist(filter='released', sort='rating:desc', libtype='movie')
                    item = watchlist[0]  # First item in the watchlist

                    # Search for the item on a Plex server
                    result = plex.library.search(guid=item.guid, libtype=item.type)

        rF  )includeCollectionsincludeExternalMediaallsorttypez/library/sections/watchlist/)r  )r   
searchTyper   DISCOVERrO  _toOnlineMetadata
fetchItems)r0   filterr  libtyper  r   r  rC   s           r;   	watchlistzMyPlexAccount.watchlist  s    : #$$%

 F!F6N"--g6F6Nf;F8ENNSYDZC[\%t%%doocjo&Q\U[\\r<   c                 J    t        | j                  |      j                        S )z Returns True if the item is on the user's watchlist.

            Parameters:
                item (:class:`~plexapi.video.Movie` or :class:`~plexapi.video.Show`): Item to check
                    if it is on the user's watchlist.
        )r   	userStatewatchlistedAt)r0   items     r;   onWatchlistzMyPlexAccount.onWatchlist  s     DNN4(6677r<   c                 8   t        |t              s|g}|D ]  }| j                  |      rt        d|j                   d      |j
                  j                  dd      d   }| j                  | j                   d| | j                  j                          | S )a   Add media items to the user's watchlist

            Parameters:
                items (List): List of :class:`~plexapi.video.Movie` or :class:`~plexapi.video.Show`
                    objects to be added to the watchlist.

            Raises:
                :exc:`~plexapi.exceptions.BadRequest`: When trying to add invalid or existing
                    media to the watchlist.
        "z" is already on the watchlistr5  rF  z"/actions/addToWatchlist?ratingKey=rH   r  listr  r   rm   guidrsplitrB   r  r)   r<  r0   r|  r  	ratingKeys       r;   addToWatchlistzMyPlexAccount.addToWatchlist  s     %&GED% 1TZZL0M!NOO		((a04IJJ$--(J9+V_c_l_l_p_pJq	 
 r<   c                 8   t        |t              s|g}|D ]  }| j                  |      st        d|j                   d      |j
                  j                  dd      d   }| j                  | j                   d| | j                  j                          | S )a   Remove media items from the user's watchlist

            Parameters:
                items (List): List of :class:`~plexapi.video.Movie` or :class:`~plexapi.video.Show`
                    objects to be added to the watchlist.

            Raises:
                :exc:`~plexapi.exceptions.BadRequest`: When trying to remove invalid or non-existing
                    media to the watchlist.
        r  z" is not on the watchlistr5  rF  r  z'/actions/removeFromWatchlist?ratingKey=rH   r  r  s       r;   removeFromWatchlistz!MyPlexAccount.removeFromWatchlist  s     %&GED##D) 1TZZL0I!JKK		((a04IJJ$--(OPY{[dhdqdqduduJv	 
 r<   c                     |j                   j                  dd      d   }| j                  | j                   d| d      }| j	                  |t
              S )z Returns a :class:`~plexapi.myplex.UserState` object for the specified item.

            Parameters:
                item (:class:`~plexapi.video.Movie` or :class:`~plexapi.video.Show`): Item to return the user state.
        r5  rF  r  z/library/metadata/z
/userStaterg  )r  r  rB   METADATAfindItem	UserState)r0   r  r  r8   s       r;   r  zMyPlexAccount.userState  sR     II$$S!,R0	zzT]]O+=i[
ST}}Ty}11r<   c                 p    | j                  |      }|j                  rt        |j                  dkD        S dS )ar   Return True if the item is played on Discover.

            Parameters:
                item (:class:`~plexapi.video.Movie`,
                :class:`~plexapi.video.Show`, :class:`~plexapi.video.Season` or
                :class:`~plexapi.video.Episode`): Object from searchDiscover().
                Can be also result from Plex Movie or Plex TV Series agent.
        r   F)r  	viewCountr   )r0   r  r  s      r;   isPlayedzMyPlexAccount.isPlayed  s5     NN4(	090C0CtI''!+,NNr<   c                     | j                    d}|j                  j                  dd      d   }|dd}| j                  ||       | S )ao   Mark the Plex object as played on Discover.

            Parameters:
                item (:class:`~plexapi.video.Movie`,
                :class:`~plexapi.video.Show`, :class:`~plexapi.video.Season` or
                :class:`~plexapi.video.Episode`): Object from searchDiscover().
                Can be also result from Plex Movie or Plex TV Series agent.
        z/actions/scrobbler5  rF  r  com.plexapp.plugins.libraryrC   r   r6  r  r  r  rB   r0   r  rC   r  r  s        r;   
markPlayedzMyPlexAccount.markPlayed  sP     01II$$S!,R0	"2OP

3v
&r<   c                     | j                    d}|j                  j                  dd      d   }|dd}| j                  ||       | S )aq   Mark the Plex object as unplayed on Discover.

            Parameters:
                item (:class:`~plexapi.video.Movie`,
                :class:`~plexapi.video.Show`, :class:`~plexapi.video.Season` or
                :class:`~plexapi.video.Episode`): Object from searchDiscover().
                Can be also result from Plex Movie or Plex TV Series agent.
        z/actions/unscrobbler5  rF  r  r  r  r6  r  r  s        r;   markUnplayedzMyPlexAccount.markUnplayed!  sP     23II$$S!,R0	"2OP

3v
&r<   c                    ddd}|j                  |d      }ddi}||||dd}| j                  | j                   d	||
      }|d   j                  dg       }	t        d |	D        g       }
g }|
D ]l  }|d   }|d   }|dk(  rd}n	|dk(  rd}ndj	                  d |j                         D              }d| d| d}|j                  | j                  |             n | j                  |      S )a   Search for movies and TV shows in Discover.
            Returns a list of :class:`~plexapi.video.Movie` and :class:`~plexapi.video.Show` objects.

            Parameters:
                query (str): Search query.
                limit (int, optional): Limit to the specified number of results. Default 30.
                libtype (str, optional): 'movie' or 'show' to only return movies or shows, otherwise return all items.
                providers (str, optional): 'discover' for default behavior
                    or 'discover,PLEXAVOD' to also include the Plex ad-suported video service
                    or 'discover,PLEXAVOD,PLEXTVOD' to also include the Plex video rental service
        moviestv)movieshowz	movies,tvAcceptr   rF  )rB   limitsearchTypessearchProvidersincludeMetadataz/library/search)r   r  MediaContainerSearchResultsc              3   j   K   | ]+  }|j                  d       dk(  s|j                  dg        - yw)r`   externalSearchResultN)r%   )r   ra  s     r;   r   z/MyPlexAccount.searchDiscover.<locals>.<genexpr>L  s1     k1QRQVQVW[Q\`jQjQUU>26s   33Metadatar  r  Videor  	Directoryr   c              3   j   K   | ]+  \  }}| d t        j                  t        |             d - yw)z="z" N)htmlescapere  )r   kvs      r;   r   z/MyPlexAccount.searchDiscover.<locals>.<genexpr>X  s2     YHX1qcDKKA$7#8;HXs   13<r   z/>)	r%   rB   r  r   r}  r|  rp  _manuallyLoadXMLr  )r0   rB   r  r  	providerslibtypesr   r  r8   searchResultssearchResultresultsresultmetadatar  tagattrsxmls                     r;   searchDiscoverzMyPlexAccount.searchDiscover0  s)    &t4,,w4 (
 "( 
 zzT]]O?;WU[z\-.22?BGkkmop"Fj)HF#Dw!GGYHXYYEcU!E7"%CNN40056 # %%g..r<   c                 f    ddi}| j                  | j                  |      }|j                  d      S )z Returns True or False if syncing of watch state and ratings
            is enabled or disabled, respectively, for the account.
        r   r   r  consent)rB   VIEWSTATESYNCr%   )r0   r   r8   s      r;   viewStateSynczMyPlexAccount.viewStateSync^  s6    
 /0zz$,,gz>xx	""r<   c                 &    | j                  d       y)z< Enable syncing of watch state and ratings for the account. TN_updateViewStateSyncrK   s    r;   enableViewStateSyncz!MyPlexAccount.enableViewStateSyncg  s    !!$'r<   c                 &    | j                  d       y)z= Disable syncing of watch state and ratings for the account. FNr$  rK   s    r;   disableViewStateSyncz"MyPlexAccount.disableViewStateSynck  s    !!%(r<   c                 p    d|i}| j                  | j                  | j                  j                  |       y)z Enable or disable syncing of watch state and ratings for the account.

            Parameters:
                consent (bool): True to enable, False to disable.
        r   r  N)rB   r!  r)   r<  )r0   r   r  s      r;   r%  z"MyPlexAccount._updateViewStateSynco  s0     W%

4%%dmm.?.?
Or<   c                 |    ddd}d|i}| j                  | j                  | j                  j                  ||       y)z Link a device to the account using a pin code.

            Parameters:
                pin (str): The 4 digit link pin code.
        z!application/x-www-form-urlencodedzPlex SSO)r   X-Plex-Productr6   )r   r8   N)rB   LINKr)   r<  )r0   rf   r   r8   s       r;   linkzMyPlexAccount.linkx  s?     @(
 }

499dmm//t
Lr<   c           	         t        | j                  | j                  | j                        }t	        t        |j                  dd                  }t        |t              s|g}|D ]  }||_	        t        |j                        }t        t        |j                              }||d<   |j                  dd       t        |j                   |j"                  |j$                  t'        |      |j(                  f      |_         |S )z= Convert a list of media objects to online metadata objects. )r4   includeUserStateTincludeFieldsN)r   r  r&   r)   r   r   popr  r  _serverr   _details_keyr]  r   rB   r	   schemenetlocpathr   fragment)r0   objsr   r  r/  objr   rB   s           r;   r  zMyPlexAccount._toOnlineMetadata  s     DMM4;;NtFJJ/A4$HIJ$%6DC CK 3++,C399-.E(8E$%IIot,)3::szz388YW\M]_b_k_k*lmC  r<   c                 $    | j                  d      S )z! Returns your public IP address. zhttps://plex.tv/:/ip)rB   rK   s    r;   publicIPzMyPlexAccount.publicIP  s    zz011r<   c                 H    d|i}| j                  d|      }t        | |      S )z Returns a :class:`~plexapi.myplex.GeoLocation` object with geolocation information
            for an IP address using Plex's GeoIP database.

            Parameters:
                ip_address (str): IP address to lookup.
        
ip_addresszhttps://plex.tv/api/v2/geoipr6  )rB   GeoLocation)r0   r=  r  r8   s       r;   geoipzMyPlexAccount.geoip  s.     
+zz8zH4&&r<   )NNNNNNTNNNNN)NFFFNNNr   )NFNNNNNN)TTNNNN)   Ndiscover)_r   
__module____qualname____doc__r  r7  r  r'  r[  ro  r.  r2  rC  rD   rI   r  r  r,  r!  r   r  r  r  r  rC   r/   r-   rL   r   r   r   r   r   propertyr   r   r   rB   r   r   r   r   r   r   r   r   r  r$  r*  r0  r3  r9  r>  r@  rD  rG  rR  rV  rc  r  r&  rM  rk  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?  __classcell__r:   s   @r;   r"   r"   $   sX   GP LL0ICNKLWM;K=L8HMO2F4G5HHG-DAM(D
(C,E2H2H
'CB!E:x S S Y Y K K  34
8;
	:=

!gl \a`d&QP ^cbf/1b bgfj3Dj	5	5E8A+B
B2 5  qu_cC2J:$4<"". &
&

F$&9Tv
(F$$+]Z8**2
O,/\ # #()PM.2	'r<   r"   c                   B    e Zd ZdZdZdZd Zed        Zd Z	d Z
d
d	Zy)r  ai   This object represents non-signed in users such as friends and linked
        accounts. NOTE: This should not be confused with the :class:`~plexapi.myplex.MyPlexAccount`
        which is your specific account. The raw xml for the data presented here
        can be found at: https://plex.tv/api/users/

        Attributes:
            TAG (str): 'User'
            key (str): 'https://plex.tv/api/users/'
            allowCameraUpload (bool): True if this user can upload images.
            allowChannels (bool): True if this user has access to channels.
            allowSync (bool): True if this user can sync.
            email (str): User's email address (user@gmail.com).
            filterAll (str): Unknown.
            filterMovies (str): Unknown.
            filterMusic (str): Unknown.
            filterPhotos (str): Unknown.
            filterTelevision (str): Unknown.
            home (bool): Unknown.
            id (int): User's Plex account ID.
            protected (False): Unknown (possibly SSL enabled?).
            recommendationsPlaylistId (str): Unknown.
            restricted (str): Unknown.
            servers (List<:class:`~plexapi.myplex.<MyPlexServerShare`>)): Servers shared with the user.
            thumb (str): Link to the users avatar.
            title (str): Seems to be an alias for username.
            username (str): User's username.
    Userzhttps://plex.tv/api/users/c                    | j                   | j                  k(  | _        t        j                  t
        |j                  j                  d            | _        t        j                  t
        |j                  j                  d            | _	        t        j                  t
        |j                  j                  d            | _
        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d	      | _        t        j                  t
        |j                  j                  d
            | _        t        j                  t$        |j                  j                  d            | _        t        j                  t
        |j                  j                  d            | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  dd      | _        |j                  j                  dd      | _        | j4                  D ]  }| j&                  |_         y)rN   r  r  r  rW   	filterAllr  r  filterPhotosr  r]   r`   rg   recommendationsPlaylistIdri   rl   rm   r   r1   N)	_initpathrC   rK  r   r   r   r~   r%   r  r  r  rW   rN  r  r  rO  r  r]   r   r`   rg   rP  ri   rl   rm   r1   rZ  r  r0   r8   r  s      r;   r   zMyPlexUser._loadData  s   nn0!&D$++//BU2V!W"ZZdkkooo.NOD$++//+*FG[[__W-
5 KKOON;;;??=9 KKOON; $0B CJJtT[[__V%<=	**S$++//$"78D$++//+*FG)-9T)U&++//,7[[__W-
[[__Wb1

B7llF#wwF #r<   c                 B    | j                  | j                  t              S r   ri  r   MyPlexServerSharerK   s    r;   rZ  zMyPlexUser.servers      ~~djj*;<<r<   c                    	 | j                   j                  | j                   j                  j                  |            D ]_  }t	        j
                  t        |j                  j                  d            | j                  k(  sD|j                  j                  d      c S  y # t        $ r# t        j                  d| j                         Y y w xY w)Nr  userIDaccessTokenz!Failed to get access token for %s)r2  rB   r  r  r   r   r   r~   r%   r`   	Exceptionr   	exceptionrm   )r0   r   r  s      r;   	get_tokenzMyPlexUser.get_token  s    	K**4<<+D+D+K+KVg+K+hi::c4;;??8#<=H;;??=99 j  	KMM=tzzJ	Ks   BB$ B$ "B$ $)CCc                     | j                   D ]1  }|j                         |j                  j                         k(  s/|c S  t        d|       )z Returns the :class:`~plexapi.myplex.MyPlexServerShare` that matches the name specified.

            Parameters:
                name (str): Name of the server to return.
        zUnable to find server )rZ  r   r   r   )r0   r   r  s      r;   r  zMyPlexUser.server  sG     llFzz|v{{0022 # /v677r<   Nc                 p    g }| j                   D ]$  }|j                  |j                  ||             & |S )z Get all Play History for a user in all shared servers.
            Parameters:
                maxresults (int): Only return the specified number of results (optional).
                mindate (datetime): Min datetime to return results from.
        )r  r  )rZ  r  r  )r0   r  r  r  r  s        r;   r  zMyPlexUser.history  s5     llFKK*gNO #r<   rA  )r   rE  rF  rG  TAGrC   r   r   rZ  r\  r  r  r   r<   r;   r  r    s<    6 C
&C'. = =K
8	r<   r  c                   2    e Zd ZdZdZdZdZd Zed        Z	y)rL  a   This object represents pending friend invites.

        Attributes:
            TAG (str): 'Invite'
            createdAt (datetime): Datetime the user was invited.
            email (str): User's email address (user@gmail.com).
            friend (bool): True or False if the user is invited as a friend.
            friendlyName (str): The user's friendly name.
            home (bool): True or False if the user is invited to a Plex Home.
            id (int): User's Plex account ID.
            server (bool): True or False if the user is invited to any servers.
            servers (List<:class:`~plexapi.myplex.<MyPlexServerShare`>)): Servers shared with the user.
            thumb (str): Link to the users avatar.
            username (str): User's username.
    Invitez$https://plex.tv/api/invites/requestsz%https://plex.tv/api/invites/requestedc                 p   t        j                  |j                  j                  d            | _        |j                  j                  d      | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _	        t        j                  t        |j                  j                  d            | _
        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _        |j                  j                  d	d
      | _        | j                   D ]  }| j                  |_         y)rN   	createdAtrW   rK  rZ   r]   r`   r  rl   r1   r   N)r   r   r~   r%   rc  rW   r   r   rK  rZ   r]   r   r`   r  rl   r1   rZ  r  rR  s      r;   r   zMyPlexInvite._loadData  s
   ))$++//+*FG[[__W-
jjt{{x'@A KKOON;JJtT[[__V%<=	**S$++//$"78jjt{{x'@A[[__W-

B7llF#wwF #r<   c                 B    | j                  | j                  t              S r   rT  rK   s    r;   rZ  zMyPlexInvite.servers(  rV  r<   N)
r   rE  rF  rG  r_  rN  rU  r   r   rZ  r   r<   r;   rL  rL    s1     C5H7I' = =r<   rL  c                   "    e Zd ZdZd Zd ZddZy)Sectiona   This refers to a shared section. The raw xml for the data presented here
        can be found at: https://plex.tv/api/servers/{machineId}/shared_servers

        Attributes:
            TAG (str): section
            id (int): The shared section ID
            key (int): The shared library section key
            shared (bool): If this section is shared with the user
            title (str): Title of the section
            type (str): movie, tvshow, artist

    c                    t        j                  t        |j                  j	                  d            | _        t        j                  t        |j                  j	                  d            | _        t        j                  t        |j                  j	                  dd            | _        |j                  j	                  d      | _	        |j                  j	                  d      | _
        | j
                  | _        | j                  | _        y)rN   r`   rC   sharedr   rm   r  N)r   r   r   r~   r%   r`   rC   r   rh  rm   r  	sectionIdrv  r  s     r;   r   zSection._loadData<  s    **S$++//$"78::c4;;??5#9:jjt{{x'EF[[__W-
KKOOF+	((r<   Nc                     | j                   j                   j                  | j                   j                        j                         }|j	                  ||| j                   j
                  | j                        S )a   Get all Play History for a user for this section in this shared server.
            Parameters:
                maxresults (int): Only return the specified number of results (optional).
                mindate (datetime): Min datetime to return results from.
        )r  r  r  librarySectionID)r2  r   r   r  r  r  rv  r0   r  r  r  s       r;   r  zSection.historyF  sc     %%..t||/@/@AIIK~~W(,(>(>QUQ`Q`  b 	br<   rA  )r   rE  rF  rG  r_  r   r  r   r<   r;   rf  rf  -  s     C#br<   rf  c                   .    e Zd ZdZdZd Zd Zd ZddZy)	rU  at   Represents a single user's server reference. Used for library sharing.

        Attributes:
            id (int): id for this share
            serverId (str): what id plex uses for this.
            machineIdentifier (str): The servers machineIdentifier
            name (str): The servers name
            lastSeenAt (datetime): Last connected to the server?
            numLibraries (int): Total number of libraries
            allLibraries (bool): True if all libraries is shared with this user.
            owned (bool): 1 if the server is owned by the user
            pending (bool): True if the invite is pending.

    Serverc                    t        j                  t        |j                  j	                  d            | _        t        j                  t        |j                  j	                  d            | _        t        j                  t        |j                  j	                  d            | _        |j                  j	                  d      | _        |j                  j	                  d      | _	        t        j                  |j                  j	                  d            | _        t        j                  t        |j                  j	                  d            | _        t        j                  t        |j                  j	                  d            | _        t        j                  t        |j                  j	                  d	            | _        t        j                  t        |j                  j	                  d
            | _        y)rN   r`   r  rY  r   r   
lastSeenAtnumLibrariesallLibrariesr  pendingN)r   r   r   r~   r%   r`   r  rY  r   r   r   rp  rq  r   rr  r  rs  r  s     r;   r   zMyPlexServerShare._loadDatab  s&   **S$++//$"78C)EF

3
(CD!%1D!EKKOOF+	**4;;??<+HI!JJsDKKOON,KL!JJtT[[__^-LMZZdkkoog&>?
zz$	(BCr<   c                     | j                         D ]1  }|j                         |j                  j                         k(  s/|c S  t        d|       )z Returns the :class:`~plexapi.myplex.Section` that matches the name specified.

            Parameters:
                name (str): Name of the section to return.
        zUnable to find section )r  r   rm   r   )r0   r   ru  s      r;   ru  zMyPlexServerShare.sectiono  sI     }}Gzz|w}}2244 ' 0788r<   c                     t         j                  j                  | j                  | j                        }| j
                  j                  |      }| j                  |t        d      S )z_ Returns a list of all :class:`~plexapi.myplex.Section` objects shared with this user.
        rX  SharedServerr   )	r"   r[  r  r   r`   r2  rB   ri  rf  r0   r   r8   s      r;   r  zMyPlexServerShare.sections{  sU     ))004;Q;Q\`\c\c0d||!!#&~~dG.~AAr<   Nc                     | j                   j                  | j                        j                         }|j	                  ||| j
                        S )z Get all Play History for a user in this shared server.
            Parameters:
                maxresults (int): Only return the specified number of results (optional).
                mindate (datetime): Min datetime to return results from.
        r  )r2  r   r   r  r  r  rl  s       r;   r  zMyPlexServerShare.history  sA     &&tyy199;~~WPTP^P^~__r<   )i N)	r   rE  rF  rG  r_  r   ru  r  r  r   r<   r;   rU  rU  Q  s%     CD
9B`r<   rU  c                   \    e Zd ZdZdZdZg dZddgZd Ze	d        Z
	 	 	 dd
Z	 	 	 	 ddZy	)r   a	   This object represents resources connected to your Plex server that can provide
        content such as Plex Media Servers, iPhone or Android clients, etc. The raw xml
        for the data presented here can be found at:
        https://plex.tv/api/v2/resources?includeHttps=1&includeRelay=1

        Attributes:
            TAG (str): 'Device'
            key (str): 'https://plex.tv/api/v2/resources?includeHttps=1&includeRelay=1'
            accessToken (str): This resource's Plex access token.
            clientIdentifier (str): Unique ID for this resource.
            connections (list): List of :class:`~plexapi.myplex.ResourceConnection` objects
                for this resource.
            createdAt (datetime): Timestamp this resource first connected to your server.
            device (str): Best guess on the type of device this is (PS, iPhone, Linux, etc).
            dnsRebindingProtection (bool): True if the server had DNS rebinding protection.
            home (bool): Unknown
            httpsRequired (bool): True if the resource requires https.
            lastSeenAt (datetime): Timestamp this resource last connected.
            name (str): Descriptive name of this resource.
            natLoopbackSupported (bool): True if the resource supports NAT loopback.
            owned (bool): True if this resource is one of your own (you logged into it).
            ownerId (int): ID of the user that owns this resource (shared resources only).
            platform (str): OS the resource is running (Linux, Windows, Chrome, etc.)
            platformVersion (str): Version of the platform.
            presence (bool): True if the resource is online
            product (str): Plex product (Plex Media Server, Plex for iOS, Plex Web, etc.)
            productVersion (str): Version of the product.
            provides (str): List of services this resource provides (client, server,
                player, pubsub-player, etc.)
            publicAddressMatches (bool): True if the public IP address matches the client's public IP address.
            relay (bool): True if this resource has the Plex Relay enabled.
            sourceTitle (str): Username of the user that owns this resource (shared resources only).
            synced (bool): Unknown (possibly True if the resource has synced content?)
    r   z>https://plex.tv/api/v2/resources?includeHttps=1&includeRelay=1)localremoterelayhttpshttpc                    t        j                  |j                  j                  d            | _        |j                  j                  d      | _        t        j                  |j                  j                  d      d      | _        |j                  j                  d      | _	        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        t        j                  |j                  j                  d	      d      | _        |j                  j                  d
      | _        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        t        j                  t&        |j                  j                  dd            | _        |j                  j                  d      | _        |j                  j                  d      | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d      | _        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _        t        j                  t        |j                  j                  d            | _        y)rN   rY  r   rc  z%Y-%m-%dT%H:%M:%SZr   dnsRebindingProtectionr]   httpsRequiredrp  r   natLoopbackSupportedr  ownerIdr   platformplatformVersionpresenceproductproductVersionr  publicAddressMatchesr}  sourceTitlesyncedN)r   r$   r~   r%   rY  r   r   r   rc  r   r   r   r  r]   r  rp  r   r  r  r   r  r  r  r  r  r  r  r  r}  r  r  r  s     r;   r   zMyPlexResource._loadData  s`   $//0NO $0B C))$++//+*FH\]kkooh/&+jjt{{G_7`&a#JJtT[[__V%<=	"ZZdkkooo.NO**4;;??<+HJ^_KKOOF+	$)JJtT[[__E[5\$]!ZZdkkoog&>?
zz#t{{y!'DE
3#{{/@A

4)DE{{y1"kkoo.>?
3$)JJtT[[__E[5\$]!ZZdkkoog&>?
;;??=9jjt{{x'@Ar<   c                 F    | j                  | j                  t        d      S )Nconnectionsrw  )ri  r   ResourceConnectionrK   s    r;   r  zMyPlexResource.connections  s    ~~djj*<=~QQr<   Nc           
         || j                   dd }|| j                  dd }|D ci c]  }||D ci c]  }|g  c} }}}| j                  D ]  }| j                  s| j                  r|j                  r)|j
                  rdn|j                  rdnd}||vrLd|v r!||   d   j                  |j                         d|v sv||   d   j                  |j                          |du r|j                  d       n|du r|j                  d       g }|D ]   }|D ]  }|j                  ||   |           " |S c c}w c c}}w )	a"   Returns a sorted list of the available connection addresses for this resource.
            Often times there is more than one address specified for a server or client.
            Default behavior will prioritize local connections before remote or relay and HTTPS before HTTP.

            Parameters:
                ssl (bool, optional): Set True to only connect to HTTPS connections. Set False to
                    only connect to HTTP connections. Set None (default) to connect to any
                    HTTP or HTTPS connection.
        Nr}  r{  r|  r  r~  TF)DEFAULT_LOCATION_ORDERDEFAULT_SCHEME_ORDERr  r  r{  r}  rp  httpuriurir  r  )	r0   ssl	locationsschemesr  r4  connections_dict
connectionr  s	            r;   preferred_connectionsz$MyPlexResource.preferred_connections  s[    33A6I?//2GYbcYbXH&Hfvrz&HHYbc**Jzz$**Z5E5E&0&6&67
HXHXW^f9,W$$X.v6==j>P>PQg%$X.w7>>z~~N + $;v.E\7>>'2!H!""#3H#=f#EF " " # 'Ics   
E
D=E=Ec                    || j                   dd }|| j                  dd }| j                  |||      }d| j                  v rt        nt
        }|D cg c]'  }||| j                  | j                  j                  |g) }}t        j                  dt        |             t        j                  t        |      }	t        d| j                   |	      S c c}w )a   Returns a new :class:`~plexapi.server.PlexServer` or :class:`~plexapi.client.PlexClient` object.
            Uses `MyPlexResource.preferred_connections()` to generate the priority order of connection addresses.
            After trying to connect to all available addresses for this resource and
            assuming at least one connection was successful, the PlexServer object is built and returned.

            Parameters:
                ssl (bool, optional): Set True to only connect to HTTPS connections. Set False to
                    only connect to HTTP connections. Set None (default) to connect to any
                    HTTP or HTTPS connection.
                timeout (int, optional): The timeout in seconds to attempt each connection.

            Raises:
                :exc:`~plexapi.exceptions.NotFound`: When unable to connect to any addresses for this resource.
        Nr  z!Testing %s resource connections..Resource)r  r  r  r  r   r   rY  r2  r)   r   r   r  r   threaded_connect_chooseConnectionr   )
r0   r  r5   r  r  r  rh  r   listargsr  s
             r;   r  zMyPlexResource.connect  s    * 33A6I?//2G00iI %5j:\gh\gUXS#t//1F1FP\gh		5s8}E..84 TYY@@ is   ,Cr@  rB  )r   rE  rF  rG  r_  rC   r  r  r   r   r  r  r  r   r<   r;   r   r     sh    !D C
JC :#V,B2 R R
 	%R !Ar<   r   c                       e Zd ZdZdZd Zy)r  a<   Represents a Resource Connection object found within the
        :class:`~plexapi.myplex.MyPlexResource` objects.

        Attributes:
            TAG (str): 'Connection'
            address (str): The connection IP address
            httpuri (str): Full HTTP URL
            ipv6 (bool): True if the address is IPv6
            local (bool): True if the address is local
            port (int): The connection port
            protocol (str): HTTP or HTTPS
            relay (bool): True if the address uses the Plex Relay
            uri (str): Full connetion URL
    r  c                    |j                   j                  d      | _        t        j                  t
        |j                   j                  d            | _        t        j                  t
        |j                   j                  d            | _        t        j                  t        |j                   j                  d            | _	        |j                   j                  d      | _
        t        j                  t
        |j                   j                  d            | _        |j                   j                  d      | _        d| j                   d	| j                   | _        y
)rN   addressIPv6r{  portprotocolr}  r  zhttp://:N)r~   r%   r  r   r   r   ipv6r{  r   r  r  r}  r  r  r  s     r;   r   zResourceConnection._loadData/  s    {{y1JJtT[[__V%<=	ZZdkkoog&>?
JJsDKKOOF$;<	
3ZZdkkoog&>?
;;??5) a		{;r<   Nr   rE  rF  rG  r_  r   r   r<   r;   r  r    s     C	<r<   r  c                   B    e Zd ZdZdZdZd Zed        Zd
dZ	d Z
d	 Zy)r   aA   This object represents resources connected to your Plex server that provide
        playback ability from your Plex Server, iPhone or Android clients, Plex Web,
        this API, etc. The raw xml for the data presented here can be found at:
        https://plex.tv/devices.xml

        Attributes:
            TAG (str): 'Device'
            key (str): 'https://plex.tv/devices.xml'
            clientIdentifier (str): Unique ID for this resource.
            connections (list): List of connection URIs for the device.
            device (str): Best guess on the type of device this is (Linux, iPad, AFTB, etc).
            id (str): MyPlex ID of the device.
            model (str): Model of the device (bueller, Linux, x86_64, etc.)
            name (str): Hostname of the device.
            platform (str): OS the resource is running (Linux, Windows, Chrome, etc.)
            platformVersion (str): Version of the platform.
            product (str): Plex product (Plex Media Server, Plex for iOS, Plex Web, etc.)
            productVersion (string): Version of the product.
            provides (str): List of services this resource provides (client, controller,
                sync-target, player, pubsub-player).
            publicAddress (str): Public IP address.
            screenDensity (str): Unknown
            screenResolution (str): Screen resolution (750x1334, 1242x2208, etc.)
            token (str): Plex authentication token for the device.
            vendor (str): Device vendor (ubuntu, etc).
            version (str): Unknown (1, 2, 1.3.3.3148-b38628e, 1.3.15, etc.)
    Devicezhttps://plex.tv/devices.xmlc                    |j                   j                  d      | _        |j                   j                  d      | _        |j                   j                  d      | _        |j                   j                  d      | _        |j                   j                  d      | _        |j                   j                  d      | _        |j                   j                  d      | _        |j                   j                  d      | _	        |j                   j                  d	      | _
        |j                   j                  d
      | _        |j                   j                  d      | _        |j                   j                  d      | _        |j                   j                  d      | _        t        j                   |j                   j                  d            | _        |j                   j                  d      | _        |j                   j                  d      | _        t)        j*                  |j                   j                  d            | _        t)        j*                  |j                   j                  d            | _        y)rN   r   publicAddressr  r  r  r  r   modelvendorr  r   versionr`   r3   screenResolutionscreenDensityrc  rp  N)r~   r%   r   r  r  r  r  r  r   r  r  r  r   r  r`   r   r$   r3   r  r  r   r   rc  rp  r  s     r;   r   zMyPlexDevice._loadDataZ  s   KKOOF+	![[___={{y1"kkoo.>?
3#{{/@Akkooh/[[__W-
kkooh/
3 $0B C{{y1++//$'))$++//'*BC
 $0B C![[___=))$++//+*FG**4;;??<+HIr<   c                 >    | j                  | j                  dd      S )Nr  
Connectionr  r   rK   s    r;   r  zMyPlexDevice.connectionso  s    ~~djj%l~CCr<   Nc                 X   d| j                   v rt        nt        }| j                  D cg c]'  }||| j                  | j
                  j                  |g) }}t        j                  dt        |             t        j                  t        |      }t        d| j                  |      S c c}w )a   Returns a new :class:`~plexapi.client.PlexClient` or :class:`~plexapi.server.PlexServer`
            Sometimes there is more than one address specified for a server or client.
            After trying to connect to all available addresses for this client and assuming
            at least one connection was successful, the PlexClient object is built and returned.

            Raises:
                :exc:`~plexapi.exceptions.NotFound`: When unable to connect to any addresses for this device.
        r  zTesting %s device connections..r  )r  r   r   r  r3   r2  r)   r   r   r  r   r  r  r  r   )r0   r5   rh  r   r  r  s         r;   r  zMyPlexDevice.connects  s     %5j:VZVfVfgVfsS#tzz4<<+@+@'JVfg		3S]C..84 499g>> hs   ,B'c                     d| j                    d}| j                  j                  || j                  j                  j                         y)z' Remove this device from your account. zhttps://plex.tv/devices/z.xmlN)r`   r2  rB   r)   rJ   )r0   rC   s     r;   rJ   zMyPlexDevice.delete  s8    (	63 5 5 < <=r<   c                 l    d| j                   vrt        d      | j                  j                  |       S )z Returns an instance of :class:`~plexapi.sync.SyncList` for current device.

            Raises:
                :exc:`~plexapi.exceptions.BadRequest`: when the device doesn't provides `sync-target`.
        r  z?Requested syncList for device which do not provides sync-target)r  )r  r   r2  r  rK   s    r;   r  zMyPlexDevice.syncItems  s4     -^__||%%T%22r<   r   )r   rE  rF  rG  r_  rC   r   r   r  r  rJ   r  r   r<   r;   r   r   ;  s>    6 C
'CJ* D D?>
	3r<   r   c                   ~     e Zd ZdZdZdZd fd	Zed        ZddZ	ddZ
d Zd	 Zd
 Zd Zd Zd Zd ZddZ xZS )MyPlexPinLogina	  
        MyPlex PIN login class which supports getting a token for authenticating the client and
        providing an access token to create a :class:`~plexapi.myplex.MyPlexAccount` instance.
        The login can be done using the four character PIN which the user must enter at
        https://plex.tv/link or using Plex OAuth.

        This helper class supports a polling, threaded and callback approach.

        - The polling approach expects the developer to periodically check if the PIN login was
          successful using :func:`~plexapi.myplex.MyPlexPinLogin.checkLogin`.
        - The threaded approach expects the developer to call
          :func:`~plexapi.myplex.MyPlexPinLogin.run` and then at a later time call
          :func:`~plexapi.myplex.MyPlexPinLogin.waitForLogin` to wait for and check the result.
        - The callback approach is an extension of the threaded approach and expects the developer
          to pass the ``callback`` parameter to the call to :func:`~plexapi.myplex.MyPlexPinLogin.run`.
          The callback will be called when the thread waiting for the PIN login to succeed either
          finishes or expires. The parameter passed to the callback is the received authentication
          token or ``None`` if the login expired.

        Parameters:
            session (requests.Session, optional): Use your own session object if you want to
                cache the http responses from Plex.
            requestTimeout (int, optional): Timeout in seconds on initial connect to plex.tv (default config.TIMEOUT).
            headers (dict, optional): A dict of X-Plex headers to send with requests.
            oauth (bool, optional): True to use Plex OAuth instead of PIN login.

        Attributes:
            PINS (str): 'https://plex.tv/api/v2/pins'
            POLLINTERVAL (int): 1
            pin (str): Four character PIN to use for the login at https://plex.tv/link.
            finished (bool): Whether the pin login has finished or not.
            expired (bool): Whether the pin login has expired or not.
            token (str): Token retrieved after login.

        Example:

            .. code-block:: python

                from plexapi.myplex import MyPlexAccount, MyPlexPinLogin

                pinlogin = MyPlexPinLogin(oauth=True)
                pinlogin.run()
                print(f'Login to Plex at the following url:\n{pinlogin.oauthUrl()}')
                pinlogin.waitForLogin()
                token = pinlogin.token

                account = MyPlexAccount(token=token)

    zhttps://plex.tv/api/v2/pinsrF  c                    t         t        |           |xs t        j                         | _        |xs t        | _        || _        || _	        d | _
        d | _        d | _        d| _        d | _        d | _        d| _        d| _        d | _        y )NF)r.   r  r/   r'   r(   r)   r   _requestTimeout_customHeaders_oauth_loginTimeout	_callback_thread_abortrr  _codefinishedexpiredr3   )r0   r4   requestTimeoutr   oauthr:   s        r;   r/   zMyPlexPinLogin.__init__  s    nd,.58#3#3#5-8%!

r<   c                 P    | j                   rt        d      | j                         S zf Return the four character PIN used for linking a device at
            https://plex.tv/link.
        z#Cannot use PIN for Plex OAuth login)r  r   _getCoderK   s    r;   rf   zMyPlexPinLogin.pin  s#    
 ;;BCC}}r<   c           	          | j                   st        d      | j                         }|d   |d   |d   |d   |d   |d   |d   | j                         d	}|r||d
<   dt	        |       S ) Return the Plex OAuth url for login.

            Parameters:
                forwardUrl (str, optional): The url to redirect the client to after login.
        z;Must use "MyPlexPinLogin(oauth=True)" for Plex OAuth login.X-Plex-Client-Identifierr+  X-Plex-VersionX-Plex-PlatformX-Plex-Platform-VersionX-Plex-DeviceX-Plex-Device-NameclientIDzcontext[device][product]zcontext[device][version]zcontext[device][platform]z context[device][platformVersion]zcontext[device][device]zcontext[device][deviceName]r6   
forwardUrlhttps://app.plex.tv/auth/#!?)r  r   r   r  r   r0   r  r   r  s       r;   oauthUrlzMyPlexPinLogin.oauthUrl  s     {{Z[[--/ :;(/0@(A(/0@(A)01B)C078Q0R'.'?+23G+HMMO	
 #-F< -i.?-@AAr<   c                 P   | j                   r| j                  st        d      | j                  rt        d      | j	                          || _        || _        d| _        d| _        t        j                  | j                  d      | _         | j                   j                          y)   Starts the thread which monitors the PIN login state.

            Parameters:
                callback (Callable[str], optional): Callback called with the received authentication token.
                timeout (int, optional): Timeout in seconds to wait for user login. Default 120 seconds.

            Raises:
                :exc:`RuntimeError`: If the thread is already running.
                :exc:`RuntimeError`: If the PIN login for the current PIN has expired.
        z(MyPlexPinLogin thread is already runningzMyPlexPinLogin has expiredFzplexapi.myplex.MyPlexPinLogintargetr   N)r  r  RuntimeErrorr  r  r  r  r  	threadingThread
_pollLoginstartr0   callbackr5   s      r;   runzMyPlexPinLogin.run  s     <<IJJ<<;<<$! ''tEder<   c                     | j                   r| j                  ry| j                   j                          | j                  s| j                  syy)z Waits for the PIN login to succeed or expire.

            Returns:
                bool: ``True`` if the PIN login succeeded or ``False`` otherwise.
        FT)r  r  r}  r  r3   rK   s    r;   waitForLoginzMyPlexPinLogin.waitForLogin  s7     ||t{{<<tzzr<   c                 x    | j                   r| j                  ryd| _        | j                   j                          y)z2 Stops the thread monitoring the PIN login state. NTr  r  r}  rK   s    r;   stopzMyPlexPinLogin.stop$  )    ||t{{r<   c                 x    | j                   ry	 | j                         S # t        $ r d| _        d| _        Y yw xY w)z2 Returns ``True`` if the PIN login has succeeded. FTr  _checkLoginrZ  r  r  rK   s    r;   
checkLoginzMyPlexPinLogin.checkLogin,  @    <<	!##%% 	!DL DM		!    99c                 ^   | j                   r| j                   S | j                  }| j                  rddi}nd }| j                  || j                  j
                  |      }|y |j                  j                  d      | _        |j                  j                  d      | _         | j                   S )NstrongTr6  r`   r6   )	r  PINSr  _queryr)   rE   r~   r%   rr  )r0   r   r  r   s       r;   r  zMyPlexPinLogin._getCode9  s    ::::ii;;%FF;;sDMM$6$6v;F??&&t,__((0
zzr<   c                     | j                   sy| j                  ry| j                   d| j                    }| j                  |      }|y|j                  j                  d      }|sy|| _        d| _        y)NFTr5  rO   )rr  r3   r  r  r~   r%   r  )r0   r   r   r3   s       r;   r  zMyPlexPinLogin._checkLoginM  sp    xx::1TXXJ';;s###K0
r<   c                 @   	 t        j                          }| j                  s| j                  r$t        j                          |z
  | j                  k  rq	 | j                         }|rn]t        j                  | j                         | j                  s2| j                  sLt        j                          |z
  | j                  k  rq| j                  r'| j                  r| j                  | j                         d| _
        y # t        $ r
 d| _        Y Mw xY w# d| _
        w xY wNT)r   r  r  r  rZ  r  sleepPOLLINTERVALr3   r  r  r0   r  r  s      r;   r  zMyPlexPinLogin._pollLogina  s    	!IIKEkk4+=+=$))+PUBUY]YkYkAk!--/F
 

4,,- kk4+=+=$))+PUBUY]YkYkAk zzdnntzz* DM ! #'DL !DM<   AD C> #:D $D 3D >DD DD 	Dc                     t        j                         }| j                  r|j                  | j                         |j                  |       |S )zF Returns dict containing base headers for all requests for pin login. r   r   r  r   r   s      r;   r   zMyPlexPinLogin._headersu  s<    ##%NN4../vr<   c           
         |xs | j                   j                  }t        j                  d|j                  j                         |       |xs | j                         } ||f|| j                  d|}|j                  sit        j                  |j                        d   }|j                  j                  dd      }t        d|j                   d| d|j                   d|       t        j                   |j                        S )	N%s %sr   r   r   r   r   r   r   )r)   r%   r   r   r   r   r   r  okr   r   r   r   r   r   r   r   r0   r   rA   r   r   r   r   r   s           r;   r  zMyPlexPinLogin._query}  s    ,4==,,		'6??002C8,T]]_#Ww8L8LWPVW{{yy!5!56q9Hmm++D#6Gq!5!5 6b
!HLL>QST[S\]^^##HMM22r<   )NNNFr   Nx   rA  )r   rE  rF  rG  r  r  r/   rH  rf   r  r  r  r  r  r  r  r  r   r  rI  rJ  s   @r;   r  r    sa    0b )DL$  B22((!(	3r<   r  c                        e Zd ZdZdZdZdZg dZ	 	 d" fd	Zd#dZ	e
d	        Ze
d
        Ze
d        Ze
d        Zd Zd$dZe
d        Zd Zd Zd Zd Zd Zd Zd%dZe
d        Zd&dZd'dZd Zd Zd Zd Zd Z d Z!d  Z"dd!Z# xZ$S )(MyPlexJWTLogina  
        MyPlex JWT login class which supports getting a JWT for authenticating the client and
        providing an access token to create a :class:`~plexapi.myplex.MyPlexAccount` instance.
        The login can be done using the four character PIN which the user must enter at
        https://plex.tv/link or using Plex OAuth.
        This class can also be used to refresh or verify an existing JWT.

        See: https://developer.plex.tv/pms/#section/API-Info/Authenticating-with-Plex

        Using this class requires the ``PyJWT`` with ``cryptography`` packages to be installed
        (``pyjwt[crypto]``).

        This helper class supports a polling, threaded and callback approach.

        - The polling approach expects the developer to periodically check if the PIN login was
          successful using :func:`~plexapi.myplex.MyPlexJWTLogin.checkLogin`.
        - The threaded approach expects the developer to call
          :func:`~plexapi.myplex.MyPlexJWTLogin.run` and then at a later time call
          :func:`~plexapi.myplex.MyPlexJWTLogin.waitForLogin` to wait for and check the result.
        - The callback approach is an extension of the threaded approach and expects the developer
          to pass the ``callback`` parameter to the call to :func:`~plexapi.myplex.MyPlexJWTLogin.run`.
          The callback will be called when the thread waiting for the PIN login to succeed either
          finishes or expires. The parameter passed to the callback is the received authentication
          token or ``None`` if the login expired.

        Parameters:
            session (requests.Session, optional): Use your own session object if you want to
                cache the http responses from Plex.
            requestTimeout (int, optional): Timeout in seconds on initial connect to plex.tv (default config.TIMEOUT).
            headers (dict, optional): A dict of X-Plex headers to send with requests.
            oauth (bool, optional): True to use Plex OAuth instead of PIN login.
            token (str, optional): Plex token only required to register the device initially if not using OAuth.
            jwtToken (str, optional): Existing Plex JWT to verify or refresh.
            keypair (tuple[str|bytes], optional): A tuple of the full file paths (str) to the ED25519 private and public
                key pair or the raw keys themselves (bytes) to use for signing the JWT.
                Use :func:`~plexapi.myplex.MyPlexJWTLogin.generateKeypair` to generate a new keypair if not provided.
            scope (list[str], optional): List of scopes to request in the new token.
                Default is all available scopes.

        Attributes:
            PINS (str): 'https://plex.tv/api/v2/pins'
            AUTH (str): 'https://clients.plex.tv/api/v2/auth'
            POLLINTERVAL (int): 1
            SCOPES (list): List of all available scopes to request for the JWT.
            pin (str): Four character PIN to use for the login at https://plex.tv/link.
            finished (bool): Whether the JWT login has finished or not.
            expired (bool): Whether the JWT login has expired or not.
            jwtToken (str): The Plex JWT received after login or refreshing.
            decodedJWT (dict): The decoded Plex JWT payload.

        Example:

            .. code-block:: python

                from plexapi.myplex import MyPlexAccount, MyPlexJWTLogin

                # Method 1: Generate a new Plex JWT using Plex OAuth
                jwtlogin = MyPlexJWTLogin(
                    oauth=True,
                    scopes=['username', 'email', 'friendly_name']
                )
                jwtlogin.generateKeypair(keyfiles=('private.key', 'public.key'))
                jwtlogin.run()
                print(f'Login to Plex at the following url:\n{jwtlogin.oauthUrl()}')
                jwtlogin.waitForLogin()
                jwtToken = jwtlogin.jwtToken

                account = MyPlexAccount(token=jwtToken)

                # Method 2: Generate a new Plex JWT using an existing Plex token and keypair
                jwtlogin = MyPlexJWTLogin(
                    token='2ffLuB84dqLswk9skLos',
                    keypair=('private.key', 'public.key'),
                    scopes=['username', 'email', 'friendly_name']
                )
                jwtlogin.registerDevice()
                jwtToken = jwtlogin.refreshJWT()

                account = MyPlexAccount(token=jwtToken)

                # Refresh an existing Plex JWT
                jwtlogin = MyPlexJWTLogin(
                    jwtToken=jwtToken,
                    keypair=('private.key', 'public.key'),
                    scopes=['username', 'email', 'friendly_name']
                )
                if not jwtlogin.verifyJWT():
                    jwtToken = jwtlogin.refreshJWT()

                account = MyPlexAccount(token=jwtToken)

    z#https://clients.plex.tv/api/v2/pinsz#https://clients.plex.tv/api/v2/authrF  )r1   rW   friendly_nameri   rS   ra   rA  c	                 *   t         t        |           |xs t        j                         | _        |xs t        | _        || _        || _	        |d   rt        j                  |d         nd | _        |d   rt        j                  |d         nd | _        |xs | j                  | _        d | _        || _        d | _        d | _        d | _        d| _        d | _        d | _        d| _        d| _        || _        t6        st9        j:                  d       y y )Nr   rF  Fz9PyJWT package is not installed, cannot use Plex JWT login)r.   r  r/   r'   r(   r)   r   r  r  r&   r   
openOrRead_privateKey
_publicKeySCOPES_scopes
_clientJWTr  r  r  r  r  rr  r  r  r  jwtTokenjwtr   r\  )
r0   r4   r  r   r  r3   r  keypairscopesr:   s
            r;   r/   zMyPlexJWTLogin.__init__  s    nd,.58#3#3#5-8%;B1:5++GAJ74:A!*%**71:6$,!
 KKST r<   c                 p   t         st        j                  d       yt        j                  j                         }|j                         }|j                  t        j                  j                  t        j                  j                  t        j                               }|j                  t        j                  j                  t        j                  j                        }|d   r|d   r|sOt        j                   j#                  |d         s"t        j                   j#                  |d         rt%        d      t'        |d   d      5 }t'        |d   d      5 }|j)                  |       |j)                  |       ddd       ddd       || _        || _        y# 1 sw Y    xY w# 1 sw Y   $xY w)	a   Generates a new ED25519 private/public keypair for signing the JWT and saves them to files.
            Requires the ``cryptography`` package to be installed.

            Parameters:
                keyfiles (tuple[str]): A tuple of the full file paths to write the private and public keypair to.
                overwrite (bool): Set to True to overwrite existing keypair files. Default is False.

            Raises:
                :exc:`FileExistsError`: when keypair files already exist and overwrite is False.
        zFCryptography package is not installed, cannot generate ED25519 keypairN)encodingr  encryption_algorithm)r  r  r   rF  zBKeypair files already exist, set overwrite=True to overwrite them.wb)cryptographyr   r\  r   Ed25519PrivateKeygenerate
public_keyprivate_bytesr
   EncodingRawPrivateFormatNoEncryptionpublic_bytesPublicFormatosr6  existsFileExistsErroropenwriter  r	  )	r0   keyfiles	overwrite
privateKey	publicKeyr  r	  privateFile
publicFiles	            r;   generateKeypairzMyPlexJWTLogin.generateKeypair  s[    KK`a..779
))+	 .."++// ..22!.!;!;!= / 

 ++"++// --11 , 


 A;8A;"''..!"=PXYZP[A\%&jkkhqk4(Khqk49PT^!!+.  , :Q( '$ :Q9P((s$   F,#F F, F)	%F,,F5c                 ,    | j                         }|d   S )z1 Returns the client identifier from the headers. r  )r   )r0   r   s     r;   _clientIdentifierz MyPlexJWTLogin._clientIdentifier-  s     --/122r<   c                     | j                   r| j                  syt        j                  | j                   | j                  z         j	                         S )z: Returns the key ID (thumbprint) for the ED25519 keypair. N)r  r	  hashlibsha256	hexdigestrK   s    r;   _keyIDzMyPlexJWTLogin._keyID3  s=     t~~d..@AKKMMr<   c           
          t         j                  j                  ddt        j                  | j
                        t        j                  | j                        dd| j                  d      S )z@ Returns the private JWK (JSON Web Key) for the ED25519 keypair.OKPEd25519sigEdDSA)ktycrvr   dusealgkid)r  PyJWK	from_dictr   base64urlEncoder	  r  r2  rK   s    r;   _privateJWKzMyPlexJWTLogin._privateJWK:  sY     yy""&&t7&&t'7'78;;$
  	r<   c           	          t         j                  j                  ddt        j                  | j
                        dd| j                  d      S )z? Returns the public JWK (JSON Web Key) for the ED25519 keypair.r4  r5  r6  r7  )r8  r9  r   r;  r<  r=  )r  r>  r?  r   r@  r	  r2  rK   s    r;   
_publicJWKzMyPlexJWTLogin._publicJWKG  sE     yy""&&t7;;$
  	r<   c                    | j                         dj                  | j                        d| j                  t	        t        j                  t        j                        j                               t	        t        j                  t        j                        t        d      z   j                               d}d| j                  i}t        j                  || j                  d|      S )	z7 Returns the encoded client JWT using the private JWK. rk   plex.tvr   )minutes)noncer  audissiatexpr=  r7  )rC   	algorithmr   )_getPlexNoncer}  r  r-  r   r   nowr   utc	timestampr   r2  r  encoderA  )r0   rF   r   s      r;   _encodeClientJWTzMyPlexJWTLogin._encodeClientJWTS  s     '')XXdll+))x||HLL1;;=>X\\2Yq5IITTVW
 4;;
 zz  	
 	
r<   c                    | j                   dgd|id| j                  gdd}|st        j                  di |S g d|d   d<   t	        | j                               D ]8  }	 t        j                  ddt        j                  j                  |      i|c S  t        j                  d       t        j                  # t        j                  $ r Y ut        j                  $ r%}t        j                  d	t        |              d
}~ww xY w)aS   Returns the decoded Plex JWT with optional signature verification using the Plex public JWK.

            Parameters:
                verify_signature (bool): Whether to verify the JWT signature and required claims.
                    Defaults to True. Set to False to skip signature verification and required-claim enforcement.
        r7  verify_signaturerE  )r  
algorithmsoptionsaudienceissuer)rH  rI  rK  rJ  
thumbprintrV  requirerC   zInvalid Plex JWT: %sNzAPlex JWT signature could not be verified with any known Plex JWKsr   )r  r-  r  decodereversed_getPlexPublicJWKr>  r?  InvalidSignatureErrorInvalidTokenErrorr   r\  re  )r0   rT  r   plexJWKes        r;   decodePlexJWTzMyPlexJWTLogin.decodePlexJWTg  s     ==")*,<="D$:$:;
  ::''''Qy)$ 6 6 89G	zz 		++G4  : 	WX''' ,, (( 2CF;s    3B<<DD# DDc                 "    | j                         S )zZ Returns the decoded Plex JWT with signature verification and required-claim enforcement. )rb  rK   s    r;   
decodedJWTzMyPlexJWTLogin.decodedJWT  s     !!##r<   c                     | j                    d} | j                  di d| j                  i}d| j                  j                  i}| j                  || j                  j                  ||       y)z% Registers the public JWK with Plex. z/jwkr   jwk)rA   r   r   Nr   )AUTHr   r&   rC  	_jwk_datar  r)   rE   )r0   r   r   bodys       r;   _registerPlexDevicez"MyPlexJWTLogin._registerPlexDevice  s`    4 $--@>4;;"?@t001C 2 2G$Or<   c                 x    | j                    d}| j                  || j                  j                        }|d   S )z Gets a nonce from Plex. z/noncerH   rG  rg  r  r)   r%   rx  s      r;   rM  zMyPlexJWTLogin._getPlexNonce  s8    6"{{3t}}'8'8{9G}r<   c                     | j                    d}d| j                  i}| j                  || j                  j                  |      }|d   S )z* Exchanges the client JWT for a Plex JWT. z/tokenr  )rA   r   
auth_token)rg  r  r  r)   rE   )r0   r   ri  r8   s       r;   _exchangePlexJWTzMyPlexJWTLogin._exchangePlexJWT  sI    6"t'{{3t}}'9'9{EL!!r<   c                 x    | j                    d}| j                  || j                  j                        }|d   S )z Gets the Plex public JWKs. z/keysrH   keysrl  rx  s      r;   r]  z MyPlexJWTLogin._getPlexPublicJWK  s8    5!{{3t}}'8'8{9F|r<   c                     | j                   st        d      | j                  r| j                  st        d      | j	                          y)a*   Registers the device with Plex using the provided token and private/public keypair.
            This must be done once if OAuth was not used before the Plex JWT can be refreshed.

            Raises:
                :exc:`~plexapi.exceptions.BadRequest`: when token or keypair is missing.
        z*Plex token is required to register device.zqED25519 private and public keys are required to register device. Use generateKeypair() to generate a new keypair.N)r&   r   r  r	  rj  rK   s    r;   registerDevicezMyPlexJWTLogin.registerDevice  sI     {{IJJt P Q Q 	  "r<   c                     | j                   r| j                  st        d      | j                         | _        | j                         | _        | j                         r| j                  S t        d      )aT   Refreshes the Plex JWT using the existing private/public keypair.

            Returns:
                str: The new Plex JWT.

            Raises:
                :exc:`~plexapi.exceptions.BadRequest`: when keypair is missing.
                :exc:`~plexapi.exceptions.BadRequest`: when the newly obtained JWT cannot be verified.
        z<ED25519 private and public keys are required to refresh JWT.z$Failed to verify newly obtained JWT.)r  r	  r   rR  r  ro  r  	verifyJWTrK   s    r;   
refreshJWTzMyPlexJWTLogin.refreshJWT  s_     t[\\//1--/>>== ?@@r<   c                 n   	 | j                   }|d   | j                  k7  rt        j                  d       y|d   t	        t        j                  t        j                        t        |      z   j                               k  rt        j                  d| d       yy# t        j                  $ r Y yw xY w)	a$   Verifies the existing Plex JWT is valid and not expiring within the specified number of days.

            Parameters:
                refreshWithinDays (int): Number of days before expiration to consider
                    the JWT invalid and in need of refresh. Default is 1 day.
        rY  z,Existing JWT was signed with a different keyFrK  )daysz Existing JWT is expiring within z day(s)T)rd  r2  r   r\  r   r   rN  r   rO  r   rP  r  r_  )r0   refreshWithinDaysrd  s      r;   ru  zMyPlexJWTLogin.verifyJWT  s    
	J ,'4;;6JKE"S(,,x||*DyVgGh*h)s)s)u%vv>?P>QQXYZ $$ 		s   B B43B4c                 H    | j                   rt        d      | j                  S r  )r  r   r  rK   s    r;   rf   zMyPlexJWTLogin.pin  s!    
 ;;BCCzzr<   c           	          | j                   st        d      | j                         }|d   |d   |d   |d   |d   |d   |d   | j                  d	}|r||d
<   dt	        |       S )r  z;Must use "MyPlexJWTLogin(oauth=True)" for Plex OAuth login.r  r+  r  r  r  r  r  r  r  r  )r  r   r   r  r   r  s       r;   r  zMyPlexJWTLogin.oauthUrl  s     {{Z[[--/ :;(/0@(A(/0@(A)01B)C078Q0R'.'?+23G+HJJ	
 #-F< -i.?-@AAr<   c                 z   | j                   r| j                  st        d      | j                  rt        d      | j	                          | j                         | _        || _        || _        d| _        d| _	        t        j                  | j                  d      | _         | j                   j                          y)r  z(MyPlexJWTLogin thread is already runningzMyPlexJWTLogin has expiredFzplexapi.myplex.MyPlexJWTLoginr  N)r  r  r  r  r  rR  r  r  r  r  r  r  r  r  r  s      r;   r  zMyPlexJWTLogin.run	  s     <<IJJ<<;<<//1$! ''tEder<   c                     | j                   r| j                  ry| j                   j                          | j                  s| j                  syy)z Waits for the user login to succeed or expire.

            Returns:
                bool: ``True`` if the user login succeeded or ``False`` otherwise.
        FT)r  r  r}  r  r  rK   s    r;   r  zMyPlexJWTLogin.waitForLogin	  s7     ||t{{<<t}}r<   c                 x    | j                   r| j                  ryd| _        | j                   j                          y)z3 Stops the thread monitoring the user login state. NTr  rK   s    r;   r  zMyPlexJWTLogin.stop+	  r  r<   c                 x    | j                   ry	 | j                         S # t        $ r d| _        d| _        Y yw xY w)z3 Returns ``True`` if the user login has succeeded. FTr  rK   s    r;   r  zMyPlexJWTLogin.checkLogin3	  r  r  c                 \   | j                   }| j                  r| j                  j                  dd}nd| j                  j                  i}| j	                  || j
                  j                  |      }|y |j                  d      | _        |j                  d      | _	        | j                  S )NT)rf  r  rf  )r   r`   r6   )
r  r  rC  rh  r  r)   rE   r%   rr  r  )r0   r   ri  r   s       r;   r  zMyPlexJWTLogin._getCode@	  s    ii;;00D t00D ;;sDMM$6$6T;B<<%\\&)
zzr<   c                     | j                   sy| j                  ry| j                   d| j                    }d| j                  i}| j	                  ||      }|y|j                  d      }|sy|| _        d| _        y)NFTr5  	deviceJWTr6  rO   )rr  r  r  r  r  r%   r  )r0   r   r  r   r3   s        r;   r  zMyPlexJWTLogin._checkLoginV	  s|    xx==1TXXJ't/;;s6;2[)r<   c                 @   	 t        j                          }| j                  s| j                  r$t        j                          |z
  | j                  k  rq	 | j                         }|rn]t        j                  | j                         | j                  s2| j                  sLt        j                          |z
  | j                  k  rq| j                  r'| j                  r| j                  | j                         d| _
        y # t        $ r
 d| _        Y Mw xY w# d| _
        w xY wr  )r   r  r  r  rZ  r  r  r  r  r  r  r  s      r;   r  zMyPlexJWTLogin._pollLogink	  s    	!IIKEkk4+=+=$))+PUBUY]YkYkAk!--/F
 

4,,- kk4+=+=$))+PUBUY]YkYkAk }}t}}- DM ! #'DL !DMr  c                     t        j                         }| j                  r|j                  | j                         |j                  |       d|d<   |S )zK Returns dict containing base headers for all requests for Plex JWT login. r   r   r  r   s      r;   r   zMyPlexJWTLogin._headers	  sF    ##%NN4../v.r<   c           
         |xs | j                   j                  }t        j                  d|j                  j                         |       |xs | j                         } ||f|| j                  d|}|j                  sit        j                  |j                        d   }|j                  j                  dd      }t        d|j                   d| d|j                   d|       d	|j                  j                  d
d      v r%t!        |j"                        r|j%                         S t'        j(                  |j                        S )Nr  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  contentr   r   r   r   s           r;   r  zMyPlexJWTLogin._query	  s   ,4==,,		'6??002C8,T]]_#Ww8L8LWPVW{{yy!5!56q9Hmm++D#6Gq!5!5 6b
!HLL>QST[S\]^^!1!1!5!5nb!IIcRZRbRbNc==?"##HMM22r<   )NNNFNNrA  N)rA  F)T)rF  r   r  )%r   rE  rF  rG  r  rg  r  r
  r/   r+  rH  r-  r2  rA  rC  rR  rb  rd  rj  rM  ro  r]  rs  rv  ru  rf   r  r  r  r  r  r  r  r  r   r  rI  rJ  s   @r;   r  r    s   [x 1D0DLZFNSIM8$%L 3 3
 N N 
 
 	 	
(!(F $ $P"# A&(  B24,*!(3r<   r  c                 x   t        j                          }	  | ||||      }	t        t        j                          |z
        }
|||	|
f||<   t        r|r|j                          yyy# t        $ rJ}t        t        j                          |z
        }
t        j                  d||       ||d|
f||<   Y d}~yd}~ww xY w)a   Connects to the specified cls with url and token. Stores the connection
        information to results[i] in a threadsafe way.

        Arguments:
            cls: the class which is responsible for establishing connection, basically it's
                 :class:`~plexapi.client.PlexClient` or :class:`~plexapi.server.PlexServer`
            url (str): url which should be passed as `baseurl` argument to cls.__init__()
            session (requests.Session): session which sould be passed as `session` argument to cls.__init()
            token (str): authentication token which should be passed as `baseurl` argument to cls.__init__()
            timeout (int): timeout which should be passed as `baseurl` argument to cls.__init__()
            results (list): pre-filled list for results
            i (int): index of current job, should be less than len(results)
            job_is_done_event (:class:`~threading.Event`): is X_PLEX_ENABLE_FAST_CONNECT is True then the
                  event would be set as soon the connection is established
    )baseurlr3   r4   r5   z%s: %sN)r   r   r   setrZ  r   error)rh  r   r3   r4   r5   r  ijob_is_done_event	starttimer   runtimeerrs               r;   r  r  	  s      		I	1SwPdiikI-.5&'2
%*;!!# +<% 1diikI-.		(C%5$0
1s   AA& &	B9/A B44B9c           	      J   |D ]'  \  }}}}|rdnd}t        j                  d| ||||       ) |D cg c]  }|s|d   |d    }}|r7t        j                  d| |d   j                  |d   j                         |d   S t	        d| j                          d|       c c}w )	zF Chooses the first (best) connection from the given _connect results. OKERRz*%s connection %s (%ss): %s?X-Plex-Token=%s   z$Connecting to %s: %s?X-Plex-Token=%sr   zUnable to connect to z: )r   r   _baseurlr&   r   r   )	ctyper   r  r   r3   r  r  okerrrs	            r;   r  r  	  s     (/#UFGE		>ugWZ\ab (/ %?Wad.>qtWG?		8%ATATV]^_V`VgVghqz
*5;;=/D6B
CC	 @s   B B B c                   :    e Zd ZdZdZh dZd Zd Zd Zd Z	d Z
y	)
r  a   Represents a single AccountOptOut
        'https://plex.tv/api/v2/user/{userUUID}/settings/opt_outs'

        Attributes:
            TAG (str): optOut
            key (str): Online Media Source key
            value (str): Online Media Source opt_in, opt_out, or opt_out_managed
    r  >   opt_inopt_outopt_out_managedc                     |j                   j                  d      | _        |j                   j                  d      | _        y)rN   rC   r  N)r~   r%   rC   r  r  s     r;   r   zAccountOptOut._loadData	  s*    ;;??5)[[__W-
r<   c                 l   || j                   vrt        | d| j                          | j                  j                  j	                  | j                  j
                        }| j                  |d}| j                  j                  || j                  j                  j                  |       || _
        y)z Sets the Online Media Sources option.

            Parameters:
                option (str): see CHOICES

            Raises:
                :exc:`~plexapi.exceptions.NotFound`: ``option`` str not found in CHOICES.
        z! not found in available choices: r  )rC   r  r  N)CHOICESr   r2  r  r  ro   rC   rB   r)   rE   r  )r0   optionr   r  s       r;   _updateOptOutzAccountOptOut._updateOptOut	  s     %fX%Ft||nUVVll""))4<<3D3D)EF33t||'<'<'A'A&Q
r<   c                 &    | j                  d       y)z, Sets the Online Media Source to "Enabled". r  Nr  rK   s    r;   optInzAccountOptOut.optIn	  s    8$r<   c                 &    | j                  d       y)z- Sets the Online Media Source to "Disabled". r  Nr  rK   s    r;   r  zAccountOptOut.optOut	  s    9%r<   c                 t    | j                   dk(  rt        | j                    d      | j                  d       y)z Sets the Online Media Source to "Disabled for Managed Users".

            Raises:
                :exc:`~plexapi.exceptions.BadRequest`: When trying to opt out music.
        ztv.plex.provider.musicz3 does not have the option to opt out managed users.r  N)rC   r   r  rK   s    r;   optOutManagedzAccountOptOut.optOutManaged	  s7     88//z)\]^^,-r<   N)r   rE  rF  rG  r_  r  r   r  r  r  r  r   r<   r;   r  r  	  s,     C6G.
 %&.r<   r  c                        e Zd ZdZd Zd Zd Zy)r  a   Represents a single UserState

        Attributes:
            TAG (str): UserState
            lastViewedAt (datetime): Datetime the item was last played.
            ratingKey (str): Unique key identifying the item.
            type (str): The media type of the item.
            viewCount (int): Count of times the item was played.
            viewedLeafCount (int): Number of items marked as played in the show/season.
            viewOffset (int): Time offset in milliseconds from the start of the content
            viewState (bool): True or False if the item has been played.
            watchlistedAt (datetime): Datetime the item was added to the watchlist.
    c                 P    d| j                   j                   d| j                   dS )Nr  r  >)r:   r   r  rK   s    r;   __repr__zUserState.__repr__
  s'    4>>**+1T^^,<A>>r<   c                    t        j                  |j                  j                  d            | _        |j                  j                  d      | _        |j                  j                  d      | _        t        j                  t        |j                  j                  dd            | _	        t        j                  t        |j                  j                  dd            | _
        t        j                  t        |j                  j                  dd            | _        |j                  j                  d      d	k(  | _        t        j                  |j                  j                  d
            | _        y)rN   lastViewedAtr  r  r  r   viewedLeafCount
viewOffset	viewStatecompleter  N)r   r   r~   r%   r  r  r  r   r   r  r  r  r  r  r  s     r;   r   zUserState._loadData
  s    !,,T[[__^-LM5KKOOF+	Ca)HI$zz#t{{?PRS/TU**S$++//,*JK5C"--dkkooo.NOr<   N)r   rE  rF  rG  r_  r  r   r   r<   r;   r  r  	  s     C?	Pr<   r  c                       e Zd ZdZdZd Zy)r>  az   Represents a signle IP address geolocation

        Attributes:
            TAG (str): location
            city (str): City name
            code (str): Country code
            continentCode (str): Continent code
            coordinates (Tuple<float>): Latitude and longitude
            country (str): Country name
            europeanUnionMember (bool): True if the country is a member of the European Union
            inPrivacyRestrictedCountry (bool): True if the country is privacy restricted
            postalCode (str): Postal code
            subdivisions (str): Subdivision name
            timezone (str): Timezone
    r  c                 Z   |j                   j                  d      | _        |j                   j                  d      | _        |j                   j                  d      | _        t        d |j                   j                  d      xs dj                  d      D              | _        |j                   j                  d      | _        |j                   j                  d      | _	        |j                   j                  d	      | _
        |j                   j                  d
      | _        |j                   j                  d      }|dk(  rdnt        j                  t        |      | _        |j                   j                  d      }|dk(  rd| _        yt        j                  t        |      | _        y)rN   cityr6   continent_codec              3   P   K   | ]  }t        j                  t        |         y wr   )r   r   float)r   coords     r;   r   z(GeoLocation._loadData.<locals>.<genexpr>+
  s!      !f2dEJJue$2ds   $&coordinatesrk   rV   postal_codesubdivisions	time_zoneeuropean_union_memberUnknownFin_privacy_restricted_countryN)r~   r%   r  r6   continentCodetupler   r  rV   
postalCoder  r   r   r   r   europeanUnionMemberinPrivacyRestrictedCountry)r0   r8   r  r  s       r;   r   zGeoLocation._loadData&
  s@   KKOOF+	KKOOF+	![[__-=>  !f37;;??=3Q3XUX2_2_`c2d!f f{{y1++//-8 KKOON;4"kkoo.EF(I5E5::dL_;` 	  &*[[__5T%U"/9<E 	'BG**TSmBn 	'r<   Nr  r   r<   r;   r>  r>  
  s     Cpr<   r>  r   )Dr   r/  r  r   r  r   r   r   r   urllib.parser   r   r   r	   r'   r  cryptography.hazmat.primitivesr
   )cryptography.hazmat.primitives.asymmetricr   ImportErrorr  plexapir   r   r   r   r   r   r   r   plexapi.baser   r   plexapi.clientr   plexapi.exceptionsr   r   r   r   plexapi.libraryr   plexapi.serverr   plexapi.sonosr   plexapi.syncr   r   requests.status_codesr    r   r"   r  rL  rf  rU  r   r  r   r  r  r  r  r  r  r>  r   r<   r;   <module>r     sV      	   2 2 C C <A, , , 9 % T T * % ) + 1E'J E'P$W Wt$=: $=N!bj !bH8`
 8`vOAZ OAd< <:U3: U3ps3 s3lJ3 J3Z1:D1.J 1.hP
 P>$p* $pIP  L
  
Cs#   D, D9 ,D65D69EE