
    gt_                         d dl Z d dlmZ d dlmZ d dlmZmZ d dlm	Z	m
Z
 d dlmZmZ d dlmZmZmZ d dlmZmZ d d	lmZmZmZmZ d d
lmZ e
j8                   G d deeeeee             Zy)    N)groupby)Path)
quote_plusunquote)mediautils)PlayablePlexPartialObject)
BadRequestNotFoundUnsupported)LibrarySectionMusicSection)SmartFilterMixinArtMixinPosterMixinPlaylistEditMixins)
deprecatedc                   ~   e Zd ZdZd ZdZd Zd Zd Zd Z	d Z
ed        Zed	        Zed
        Zed        Zed        Zd Zd Zd Zd Zd Zd Zd Z ed      d        Zd Zd&dZd'dZd Z ed      d(d       Zd Ze d        Z!e d)d       Z"e d         Z#e 	 	 d*d!       Z$d" Z%	 	 d+d#Z&d&d$Z'ed%        Z(y),Playlista   Represents a single Playlist.

        Attributes:
            TAG (str): 'Playlist'
            TYPE (str): 'playlist'
            addedAt (datetime): Datetime the playlist was added to the server.
            allowSync (bool): True if you allow syncing playlists.
            composite (str): URL to composite image (/playlist/<ratingKey>/composite/<compositeid>)
            content (str): The filter URI string for smart playlists.
            duration (int): Duration of the playlist in milliseconds.
            durationInSeconds (int): Duration of the playlist in seconds.
            fields (List<:class:`~plexapi.media.Field`>): List of field objects.
            guid (str): Plex GUID for the playlist (com.plexapp.agents.none://XXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX).
            icon (str): Icon URI string for smart playlists.
            key (str): API URL (/playlist/<ratingkey>).
            leafCount (int): Number of items in the playlist view.
            librarySectionID (int): Library section identifier (radio only)
            librarySectionKey (str): Library section key (radio only)
            librarySectionTitle (str): Library section title (radio only)
            playlistType (str): 'audio', 'video', or 'photo'
            radio (bool): If this playlist represents a radio station
            ratingKey (int): Unique key identifying the playlist.
            smart (bool): True if the playlist is a smart playlist.
            summary (str): Summary of the playlist.
            title (str): Name of the playlist.
            titleSort (str): Title to use when sorting (defaults to title).
            type (str): 'playlist'
            updatedAt (datetime): Datetime the playlist was updated.
    playlistc                 *   t        j                  | |       t        j                  |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                  |t         j"                        | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d	d
      j+                  dd
      | _        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _        |j                  j                  d      | _        |j                  j                  d      | _        t        j                  t        |j                  j                  dd            | _        t        j                  t        |j                  j                  d            | _        t        j                  t        |j                  j                  d            | _        |j                  j                  d      | _        |j                  j                  d      | _         |j                  j                  d| j@                        | _!        |j                  j                  d      | _"        t        j                  |j                  j                  d            | _#        d| _$        d| _%        d| _&        y)z/ Load attribute values from Plex XML response. addedAt	allowSync	compositecontentdurationdurationInSecondsguidiconkey /items	leafCountlibrarySectionIDlibrarySectionKeylibrarySectionTitleplaylistTyperadior   	ratingKeysmartsummarytitle	titleSorttype	updatedAtN)'r	   	_loadDatar   
toDatetimeattribgetr   castboolr   r   r   intr   r   	findItemsr   Fieldfieldsr   r    replacer!   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   _items_section_filters)selfdatas     %/opt/Tautulli/lib/plexapi/playlist.pyr1   zPlaylist._loadData6   s   4&''	(BCD$++//+*FG5{{y1

3
(CD!&CAT1U!VnnT5;;7KKOOF+	KKOOF+	;;??5"-55hCC)EF %

3@R0S T!%1D!E#';;??3H#I  KKOON;ZZdkkoogq&AB
C)EFZZdkkoog&>?
{{y1[[__W-
djjAKKOOF+	))$++//+*FG    c                 4    t        | j                               S N)lenitemsr?   s    rA   __len__zPlaylist.__len__T   s    4::<  rB   c              #   >   K   | j                         D ]  }|  y wrD   rF   r?   items     rA   __iter__zPlaylist.__iter__W   s      JJL 	DJ	s   c                 H    t        fd| j                         D              S )Nc              3   P   K   | ]  }|j                   j                   k(    y wrD   r!   ).0iothers     rA   	<genexpr>z(Playlist.__contains__.<locals>.<genexpr>\   s     <!155EII%<s   #&)anyrF   )r?   rS   s    `rA   __contains__zPlaylist.__contains__[   s    <tzz|<<<rB   c                 (    | j                         |   S rD   rJ   )r?   r!   s     rA   __getitem__zPlaylist.__getitem__^   s    zz|C  rB   c                     | j                   S )z Alias to self.composite. )r   rG   s    rA   thumbzPlaylist.thumba   s     ~~rB   c                 f    | j                   ry| j                  ry| j                  ryt        d      )zH Returns the type of metadata in the playlist (movie, track, or photo). movietrackphotozUnexpected playlist type)isVideoisAudioisPhotor   rG   s    rA   metadataTypezPlaylist.metadataTypef   s.     <<\\\\899rB   c                      | j                   dk(  S )z+ Returns True if this is a video playlist. videor(   rG   s    rA   r_   zPlaylist.isVideor          G++rB   c                      | j                   dk(  S )z, Returns True if this is an audio playlist. audiore   rG   s    rA   r`   zPlaylist.isAudiow   rf   rB   c                      | j                   dk(  S )z+ Returns True if this is a photo playlist. r^   re   rG   s    rA   ra   zPlaylist.isPhoto|   rf   rB   c                     | j                         D ])  }|j                  |j                  k(  s|j                  c S  t        d|j                   d      )zF Match an item to a playlist item and return the item playlistItemID. Item with title "" not found in the playlist)rF   r*   playlistItemIDr   r-   )r?   rL   _items      rA   _getPlaylistItemIDzPlaylist._getPlaylistItemID   sO    ZZ\ 	,E$..0+++	, *4::,6QRSSrB   c                     | j                   r,| j                   | j                  | j                        | _        | j                  S )z Returns the search filter dict for smart playlist.
            The filter dict be passed back into :func:`~plexapi.library.LibrarySection.search`
            to get the list of items.
        )r+   r>   _parseFiltersr   rG   s    rA   filterszPlaylist.filters   s4    
 ::$--/ ..t||<DM}}rB   c                    | j                   st        d      | j                  t        j                  dt        | j                  xs d            }|rPt        |j                  d            }| j                  j                  j                  |      | _        | j                  S | j                         r2| j                         d   j                         | _        | j                  S t        d      | j                  S )aI   Returns the :class:`~plexapi.library.LibrarySection` this smart playlist belongs to.

            Raises:
                :class:`plexapi.exceptions.BadRequest`: When trying to get the section for a regular playlist.
                :class:`plexapi.exceptions.Unsupported`: When unable to determine the library section.
        z4Regular playlists are not associated with a library.z/library/sections/(\d+)/allr"      r   z'Unable to determine the library section)r+   r   r=   researchr   r   r7   group_serverlibrarysectionByIDrF   sectionr   )r?   match
sectionKeys      rA   r{   zPlaylist.section   s     zzSTT== II<gdllFXVX>YZE Q0
 $ 4 4 @ @ L}}$ zz| $

Q 7 7 9}}$GHH}}rB   c                     | j                         D ]1  }|j                  j                         |j                         k(  s/|c S  t        d| d      )a   Returns the item in the playlist that matches the specified title.

            Parameters:
                title (str): Title of the item to return.

            Raises:
                :class:`plexapi.exceptions.NotFound`: When the item is not found in the playlist.
        rk   rl   )rF   r-   lowerr   )r?   r-   rL   s      rA   rL   zPlaylist.item   sQ     JJL 	Dzz!U[[]2	 *5'1LMNNrB   c                    | j                   rg S | j                  | j                   d}| j                  |      }i }|D ]v  }|j                  s|j                  j                  d      d   }||vr;	 | j                  j                         j                  |      j                         ||<   ||   |_        x || _        | j                  S # t        $ r d||<   Y /w xY w)z. Returns a list of all items in the playlist. Nr#   /   )r)   r<   r!   
fetchItems	sourceURIsplitrx   myPlexAccountresourceconnectr   )r?   r!   rF   _serversrL   serverIDs         rA   rF   zPlaylist.items   s    ::I;;XXJf%COOC(E H 	6>>#~~33C8;Hx/6151K1K1M1V1VW_1`1h1h1jHX. $,H#5DL	6  DK{{  ( 615HX.6s   2:CCCc                 $    | j                  |      S )z3 Alias to :func:`~plexapi.playlist.Playlist.item`. rL   )r?   r-   s     rA   r4   zPlaylist.get   s    yyrB   c                 ~   | j                   rt        d      |rt        |t        t        f      s|g}t        |d       D ]  \  }}g }|D ]d  }|j                  | j                  k7  r%t        d| j                   d|j                         |j                  t        |j                               f dj                  |      }|j                          d| }d|i}| j                   d	t        j                  |       }| j                   j#                  || j                   j$                  j&                  
        | S )av   Add items to the playlist.

            Parameters:
                items (List): List of :class:`~plexapi.audio.Audio`, :class:`~plexapi.video.Video`,
                    or :class:`~plexapi.photo.Photo` objects to be added to the playlist.

            Raises:
                :class:`plexapi.exceptions.BadRequest`: When trying to add items to a smart playlist.
        z%Cannot add items to a smart playlist.c                     | j                   S rD   )rx   r   s    rA   <lambda>z#Playlist.addItems.<locals>.<lambda>   s
    dll rB   rP   z2Can not mix media types when building a playlist: z and ,/library/metadata/urir#   method)r+   r   
isinstancelisttupler   listTyper(   appendstrr*   join_uriRootr!   r   joinArgsrx   query_sessionput)	r?   rF   serverr<   
ratingKeysrL   r   argsr!   s	            rA   addItemszPlaylist.addItems   s6    ::DEEED%=9GE &e1JK 	FNFFJ 7==D$5$55$'Y(,(9(9':%&P Q Q!!#dnn"56	7 *-J__&''9*FC3<DXXJfU^^D%9$:;CLLs4<<+@+@+D+DE	F  rB   zuse "removeItems" insteadc                 &    | j                  |       y rD   )removeItemsrK   s     rA   
removeItemzPlaylist.removeItem   s    rB   c                 2   | j                   rt        d      |rt        |t        t        f      s|g}|D ]_  }| j                  |      }| j                   d| }| j                  j                  || j                  j                  j                         a | S )a   Remove items from the playlist.

            Parameters:
                items (List): List of :class:`~plexapi.audio.Audio`, :class:`~plexapi.video.Video`,
                    or :class:`~plexapi.photo.Photo` objects to be removed from the playlist.

            Raises:
                :class:`plexapi.exceptions.BadRequest`: When trying to remove items from a smart playlist.
                :class:`plexapi.exceptions.NotFound`: When the item does not exist in the playlist.
        z*Cannot remove items from a smart playlist./items/r   )r+   r   r   r   r   ro   r!   rx   r   r   delete)r?   rF   rL   rm   r!   s        rA   r   zPlaylist.removeItems   s     ::IJJED%=9GE 	ID!44T:NXXJgn%56CLLs4<<+@+@+G+GH	I rB   Nc                 &   | j                   rt        d      | j                  |      }| j                   d| d}|r| j                  |      }|d| z  }| j                  j                  || j                  j                  j                         | S )a   Move an item to a new position in the playlist.

            Parameters:
                items (obj): :class:`~plexapi.audio.Audio`, :class:`~plexapi.video.Video`,
                    or :class:`~plexapi.photo.Photo` objects to be moved in the playlist.
                after (obj): :class:`~plexapi.audio.Audio`, :class:`~plexapi.video.Video`,
                    or :class:`~plexapi.photo.Photo` objects to move the item after in the playlist.

            Raises:
                :class:`plexapi.exceptions.BadRequest`: When trying to move items in a smart playlist.
                :class:`plexapi.exceptions.NotFound`: When the item or item after does not exist in the playlist.
        z&Cannot move items in a smart playlist.r   z/movez?after=r   )r+   r   ro   r!   rx   r   r   r   )r?   rL   afterrm   r!   afterPlaylistItemIDs         rA   moveItemzPlaylist.moveItem  s     ::EFF006
'.!17"&"9"9%"@W0122C3t||'<'<'@'@ArB   c                    | j                   st        d      | j                         } |j                  d||j                  ||d|}| j
                  j                          | }d|i}| j                   dt        j                  |       }	| j
                  j                  |	| j
                  j                  j                         | S )aS   Update the filters for a smart playlist.

            Parameters:
                limit (int): Limit the number of items in the playlist.
                sort (str or list, optional): A string of comma separated sort fields
                    or a list of sort fields in the format ``column:dir``.
                    See :func:`~plexapi.library.LibrarySection.search` for more info.
                filters (dict): A dictionary of advanced filters.
                    See :func:`~plexapi.library.LibrarySection.search` for more info.
                **kwargs (dict): Additional custom filters to apply to the search results.
                    See :func:`~plexapi.library.LibrarySection.search` for more info.

            Raises:
                :class:`plexapi.exceptions.BadRequest`: When trying update filters for a regular playlist.
        z-Cannot update filters for a regular playlist.sortlibtypelimitrr   r   r#   r    )r+   r   r{   _buildSearchKeyMETADATA_TYPErx   r   r!   r   r   r   r   r   )
r?   r   r   rr   kwargsr{   	searchKeyr   r   r!   s
             rA   updateFilterszPlaylist.updateFilters.  s      zzLMM,,.+G++ ^w44E7^V\^	&&())5s|
&!5 673t||'<'<'@'@ArB   c                 0   t        | j                  t              r| j                  j                  |       | S | j                   t        j                  |       }| j                  j                  || j                  j                  j                         | S )z Actually edit the playlist. r   )r   _editsdictupdater!   r   r   rx   r   r   r   )r?   r   r!   s      rA   _editzPlaylist._editK  so    dkk4(KKv&K
5>>&1233t||'<'<'@'@ArB   z)use "editTitle" and "editSummary" insteadc                 F    i }|r||d<   |r||d<    | j                   di |S )z Edit the playlist.

            Parameters:
                title (str, optional): The title of the playlist.
                summary (str, optional): The summary of the playlist.
        r-   r,   r   )r   )r?   r-   r,   r   s       rA   editzPlaylist.editU  s5     !DM%DOtzz!D!!rB   c                     | j                   j                  | j                  | j                   j                  j                         y)z Delete the playlist. r   N)rx   r   r!   r   r   rG   s    rA   r   zPlaylist.deleted  s-    488DLL,A,A,H,HIrB   c                    |st        d      |rt        |t        t        f      s|g}|d   j                  }g }|D ]@  }|j                  |k7  rt        d      |j                  t        |j                               B dj                  |      }|j                          d| }|||dd}dt        j                  |       }	|j                  |	|j                  j                        d   }
 | ||
|		      S )
z Create a regular playlist. z5Must include items to add when creating new playlist.r   z1Can not mix media types when building a playlist.r   r   r   r/   r-   r+   
/playlistsr   initpath)r   r   r   r   r   r   r   r*   r   r   r   r   r   r   post)clsr   r-   rF   r   r   rL   r   r   r!   r@   s              rA   _createzPlaylist._createh  s     TUUED%=9GE8$$
 	3D}}( !TUUc$..12	3
 XXj)
"##5j\BHuqI5>>$/01||C(<(<|=a@64#..rB   c                    t        |t              s|j                  j                  |      }|xs |j                  } |j
                  d||||d|}	|j                          |	 }
|
|j                  |dd}dt        j                  |       }|j                  ||j                  j                        d   } | |||      S )	z Create a smart playlist. r   rt   r   r   r   r   r   r   )r   r   ry   r{   r   r   r   CONTENT_TYPEr   r   r   r   r   )r   r   r-   r{   r   r   r   rr   r   r   r   r   r!   r@   s                 rA   _createSmartzPlaylist._createSmart  s     '>2nn,,W5G2W22+G++ PweWPHNP	"#I;/G$8$85STU5>>$/01||C(<(<|=a@64#..rB   c                    t        |t              s|j                  j                  |      }t        |t              st        d      |j                  |d}dt        j                  |       }|j                  ||j                  j                         	 |j                  |j                  |      d   j                  |      j                         S # t        $ r t        d      dw xY w)	z/ Create a playlist from uploading an m3u file. z<Can only create playlists from m3u files in a music library.)	sectionIDpathz/playlists/uploadr   )	sectionIdguid__endswithr   z(Failed to create playlist from m3u file.N)r   r   ry   r{   r   r   r!   r   r   r   r   r   	playlists	editTitlereload
IndexError)r   r   r-   r{   m3ufilepathr   r!   s          rA   _createFromM3UzPlaylist._createFromM3U  s     '>2nn,,W5G'<0[\\$[[+>!%.."6!78S!5!56	S##gkk+#VWXYccdijqqss 	SGHdR	Ss   <C C)c           	          |
r| j                  ||||
      S |r&|rt        d       | j                  |||||||	fi |S | j                  |||      S )a"	   Create a playlist.

            Parameters:
                server (:class:`~plexapi.server.PlexServer`): Server to create the playlist on.
                title (str): Title of the playlist.
                section (:class:`~plexapi.library.LibrarySection`, str): Smart playlists and m3u import only,
                    the library section to create the playlist in.
                items (List): Regular playlists only, list of :class:`~plexapi.audio.Audio`,
                    :class:`~plexapi.video.Video`, or :class:`~plexapi.photo.Photo` objects to be added to the playlist.
                smart (bool): True to create a smart playlist. Default False.
                limit (int): Smart playlists only, limit the number of items in the playlist.
                libtype (str): Smart playlists only, the specific type of content to filter
                    (movie, show, season, episode, artist, album, track, photoalbum, photo).
                sort (str or list, optional): Smart playlists only, a string of comma separated sort fields
                    or a list of sort fields in the format ``column:dir``.
                    See :func:`~plexapi.library.LibrarySection.search` for more info.
                filters (dict): Smart playlists only, a dictionary of advanced filters.
                    See :func:`~plexapi.library.LibrarySection.search` for more info.
                m3ufilepath (str): Music playlists only, the full file path to an m3u file to import.
                    Note: This will overwrite any playlist previously created from the same m3u file.
                **kwargs (dict): Smart playlists only, additional custom filters to apply to the
                    search results. See :func:`~plexapi.library.LibrarySection.search` for more info.

            Raises:
                :class:`plexapi.exceptions.BadRequest`: When no items are included to create the playlist.
                :class:`plexapi.exceptions.BadRequest`: When mixing media types in the playlist.
                :class:`plexapi.exceptions.BadRequest`: When attempting to import m3u file into non-music library.
                :class:`plexapi.exceptions.BadRequest`: When failed to import m3u file.

            Returns:
                :class:`~plexapi.playlist.Playlist`: A new instance of the created Playlist.
        z*Cannot create a smart playlist with items.)r   r   r   r   )r   r   r-   r{   rF   r+   r   r   r   rr   r   r   s               rA   createzPlaylist.create  sj    F %%feWkJJ !MNN#3##FE7E7DRYd]cdd;;vue44rB   c                     | j                   j                  |      }| j                  || j                  | j	                               S )z Copy playlist to another user account.

            Parameters:
                user (:class:`~plexapi.myplex.MyPlexUser` or str): `MyPlexUser` object, username,
                    email, or user id of the user to copy the playlist to.
        )r   r-   rF   )rx   
switchUserr   r-   rF   )r?   user
userServers      rA   
copyToUserzPlaylist.copyToUser  s7     \\,,T2
{{*DJJdjjl{SSrB   c	                    | j                   st        d      ddlm}	m}
m} | j                  j                         } |	| j                  d      }|r|n| j                  |_        | j                  |_	        | j                  |_        | j                  |_        | j                  j                  |_        dt        | j                         |_        |
j#                  ||      |_        | j&                  r|j)                  |      |_        nQ| j,                  r|j/                  |      |_        n.| j0                  r|j3                  |      |_        nt5        d      |j7                  |||      S )a   Add the playlist as a sync item for the specified device.
            See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions.

            Parameters:
                videoQuality (int): idx of quality of the video, one of VIDEO_QUALITY_* values defined in
                                    :mod:`~plexapi.sync` module. Used only when playlist contains video.
                photoResolution (str): maximum allowed resolution for synchronized photos, see PHOTO_QUALITY_* values in
                                       the module :mod:`~plexapi.sync`. Used only when playlist contains photos.
                audioBitrate (int): maximum bitrate for synchronized music, better use one of MUSIC_BITRATE_* values
                                    from the module :mod:`~plexapi.sync`. Used only when playlist contains audio.
                client (:class:`~plexapi.myplex.MyPlexDevice`): sync destination, see
                                                               :func:`~plexapi.myplex.MyPlexAccount.sync`.
                clientId (str): sync destination, see :func:`~plexapi.myplex.MyPlexAccount.sync`.
                limit (int): maximum count of items to sync, unlimited if `None`.
                unwatched (bool): if `True` watched videos wouldn't be synced.
                title (str): descriptive title for the new :class:`~plexapi.sync.SyncItem`, if empty the value would be
                             generated from metadata of current photo.

            Raises:
                :exc:`~plexapi.exceptions.BadRequest`: When playlist is not allowed to sync.
                :exc:`~plexapi.exceptions.Unsupported`: When playlist content is unsupported.

            Returns:
                :class:`~plexapi.sync.SyncItem`: A new instance of the created sync item.
        z#The playlist is not allowed to syncr   )SyncItemPolicyMediaSettingsNzplaylist:///zUnsupported playlist content)clientclientId)r   r   plexapi.syncr   r   r   rx   r   r-   	rootTitler(   contentTyperb   machineIdentifierr   r   locationr   policyr_   createVideomediaSettingsr`   createMusicra   createPhotor   sync)r?   videoQualityphotoResolutionaudioBitrater   r   r   	unwatchedr-   r   r   r   myplex	sync_items                 rA   r   zPlaylist.sync  s#   6 ~~BCC@@++-T\\40	#(%djj	"jj	 $ 1 1	!%!2!2	&*ll&D&D	#+Jtyy,A+BC	!==	:	<<&3&?&?&MI#\\&3&?&?&MI#\\&3&?&?&PI#<=={{9Vh{GGrB   c                 R    | j                   j                  |d| j                        S )z3 Get the Plex Web URL with the correct parameters. r   )baseendpointr!   )rx   _buildWebURLr!   )r?   r   s     rA   
_getWebURLzPlaylist._getWebURL  s"    ||((dZTXX(VVrB   c                     t        j                  | j                        }t        t	        d      dz  |d   z  |dd  dz        S )zL Returns the Plex Media Server data directory where the metadata is stored. Metadata	Playlistsr   rt   Nz.bundle)r   sha1hashr   r   r   )r?   	guid_hashs     rA   metadataDirectoryzPlaylist.metadataDirectory  sI     NN499-	4
#k1IaL@iPQPRm_T[C\\]]rB   rD   )NNN)NN)NNNN)NNFNNNNN)NNNNNNFN))__name__
__module____qualname____doc__TAGTYPEr1   rH   rM   rV   rX   propertyrZ   rb   r_   r`   ra   ro   rr   r{   rL   rF   r4   r   r   r   r   r   r   r   r   r   classmethodr   r   r   r   r   r   r   r   r   rB   rA   r   r      s   : CD<!=!   	: 	: , , , , , ,T6O0 !F +, -.4: ;<" ="J / /. / /  S S  PTBF)5 )5VT rv$(4HlW ^ ^rB   r   )ru   	itertoolsr   pathlibr   urllib.parser   r   plexapir   r   plexapi.baser	   r
   plexapi.exceptionsr   r   r   plexapi.libraryr   r   plexapi.mixinsr   r   r   r   plexapi.utilsr   registerPlexObjectr   r   rB   rA   <module>r     s^    	   ,   4 @ @ 8 V V $ F^xk	F^ F^rB   