
    i-(                         S SK r S SKrS SKJr  S SKrSr\S-   r\S-   r\S-   r\SS.r	S	r
S
rSrSrSrSrSrSrSrSrSrSrSrSrSrSrSrSrSrSrSrSrSr S r!S!r"S"r#S#r$S$S%S&S'S(S)S*.r% " S+ S,\5      r& " S- S.\'5      r(g)/    N)
HTMLParserzhttps://www.youtube.com/zapi/lounge/bc/bindz)api/lounge/pairing/get_lounge_token_batchwatch_queue_ajaxz!application/x-www-form-urlencoded)OriginzContent-TypezX-YouTube-LoungeId-Tokenzreq{req_id}z,yt-uix-scroller-scroll-unit watch-queue-itemz"S","(.*?)"]z"c","(.*?)","_currentIndex_currentTime
_audioOnly_videoId_listId__sccountsetPlaylistclearPlaylistremoveVideoinsertVideoaddVideoaction_get_watch_queue_items
gsessionidloungeIdTokenCVERTYPERIDSIDVERAIDCIREMOTE_CONTROLaaaaaaaaaaaaaaaaaaaaaaaaaaPython   castzandroid-phone-13.14.55)deviceidnamezmdx-versionpairing_typeappc                   .   ^  \ rS rSrU 4S jrS rSrU =r$ )QueueHTMLParser1   c                 0   > / U l         [        TU ]	  5         g N)queue_itemssuper__init__)self	__class__s    N/home/sebas/venvs/catt/lib/python3.13/site-packages/casttube/YouTubeSession.pyr-   QueueHTMLParser.__init__2   s        c                     US:X  aQ  [        S U 5       5      nSUR                  5       ;   a*  US   [        :X  a  U R                  R	                  U5        g g g g )Nlic              3   ,   #    U  H
  u  pX4v   M     g 7fr*    ).0xys      r0   	<genexpr>2QueueHTMLParser.handle_starttag.<locals>.<genexpr>8   s     7qfs   class)dictkeysWATCH_QUEUE_ITEM_CLASSr+   append)r.   tagattrs
attributess       r0   handle_starttagQueueHTMLParser.handle_starttag6   sZ    $;777J*//++g&*@@$$++J7 A , r2   )r+   )__name__
__module____qualname____firstlineno__r-   rD   __static_attributes____classcell__)r/   s   @r0   r'   r'   1   s    8 8r2   r'   c                       \ rS rSrSrS r\S 5       rSS jrS r	S r
S rS	 rS
 rS rS rS rS rS rSS jrS rS rSS jrSrg)YouTubeSession>   z1The main logic to interact with YouTube cast api.c                 V    Xl         S U l        S U l        S U l        SU l        SU l        g )Nr   )
_screen_id_lounge_token_gsession_id_sid_rid
_req_count)r.   	screen_ids     r0   r-   YouTubeSession.__init__A   s,    #! 		r2   c                 J    U R                   (       a  U R                  (       a  gg)z,Returns True if session params are not None.TF)rR   rQ   r.   s    r0   
in_sessionYouTubeSession.in_sessionI   s     !3!3r2   c                 H    U R                  5         U R                  XU5        g)a   
Play video(video_id) now. This ignores the current play queue order.
:param video_id: YouTube video id(http://youtube.com/watch?v=video_id)
:param list_id: list id for playing playlist ...youtube.com/watch?v=VIDEO_ID&list=LIST_ID
:param start_time: starting time of the video in seconds
N)_start_session_initialize_queue)r.   video_idlist_id
start_times       r0   
play_videoYouTubeSession.play_videoQ   s      	x*=r2   c                 0    U R                  U[        5        g)zz
Add video(video_id) to the end of the play queue.
:param video_id: YouTube video id(http://youtube.com/watch?v=video_id)
N)_queue_action
ACTION_ADDr.   r_   s     r0   add_to_queueYouTubeSession.add_to_queue\   s    
 	8Z0r2   c                 0    U R                  U[        5        g)z
Play video(video_id) after the currently playing video.
:param video_id: YouTube video id(http://youtube.com/watch?v=video_id)
N)re   ACTION_INSERTrg   s     r0   	play_nextYouTubeSession.play_nextc       
 	8]3r2   c                 0    U R                  U[        5        g)zn
Remove video(videoId) from the queue.
:param video_id: YouTube video id(http://youtube.com/watch?v=video_id)
N)re   ACTION_REMOVErg   s     r0   remove_videoYouTubeSession.remove_videoj   rn   r2   c                 0    U R                  S[        5        g )N )re   ACTION_CLEARrY   s    r0   clear_playlistYouTubeSession.clear_playlistq   s    2|,r2   c                    [         U R                  [        SSS[        S[        U R
                  [        U R                  [        SSS[        S[        S0
nUR                  [        5        U R                  [        [        U R                  0S	US
9nUR                   nUR#                  SS5      n[$        R&                  " X3R)                  S5      S 5      nU VVs/ s H  u  pVUPM	     nnnU$ s  snnf )zi
Get data about the current active session using an xmlhttp request.
:return: List of session attributes
   v   rpcxmlhttpt      Theaderssession_requestparams
rt   [N)LOUNGEIDTOKENrQ   r   r   r   rS   
GSESSIONIDrR   r   r   r   update	BIND_DATA_do_postBIND_URLLOUNGE_ID_HEADERtextreplacejsonloadsfind)r.   
url_paramsresponseresponse_textresponse_listkrz   s          r0   get_session_dataYouTubeSession.get_session_datat   s    
 $T%7%7aaeUXZ^ZcZc $"3"3T9c1cSTVXZ[]
)$==4DdFXFX3Y15j ! J %--dB7

=1C1CC1H1I#JK'45}tq}5 6s   C-c                 z    U R                  5       nU H%  nUS   S:X  d  M  US   S   (       d  M  US   S   s  $    g)zG
Get the current queue playlist id.
:return: queue playlist id or None
r   
nowPlayingr   listIdN)r   )r.   session_datarz   s      r0   get_queue_playlist_id$YouTubeSession.get_queue_playlist_id   sH    
 ,,.At|#Q4>>Q4>)  r2   c                    U R                  5       nU(       d  0 $ [        SSU0nU R                  [        [        U R
                  0SUS9n[        5       nUR                  UR                  5       S   5        UR                  $ )z
Get the video id, video title and uploader username for videos currently in the queue.
:return: index, video id, title, username or {} if no active playlist id is found for the session
r   listFr   html)
r   ACTION_GET_QUEUE_ITEMSr   QUEUE_AJAX_URLr   rQ   r'   feedr   r+   )r.   queue_playlist_idr   r   parsers        r0   get_queue_videosYouTubeSession.get_queue_videos   s~    
 !668 I,a9JK
==:JDL^L^9_16z ! K "HMMOF+,!!!r2   c                 D    U R                  5         U R                  5         g r*   )_get_lounge_id_bindrY   s    r0   r]   YouTubeSession._start_session   s    

r2   c                     SU R                   0nU R                  [        US9nUR                  5       S   S   S   nX0l        g)zN
Get the lounge_token.
The token is used as a header in all session requests.

screen_ids)datascreensr   loungeTokenN)rP   r   LOUNGE_TOKEN_URLr   rQ   )r.   r   r   lounge_tokens       r0   r   YouTubeSession._get_lounge_id   sE    
 doo.==!1==}}y1!4]C)r2   c                    SU l         SU l        [        U R                   [        S[        S0n[
        U R                  0nU R                  [        [        UUS9n[        UR                  5      n[        R                  " [        U5      n[        R                  " [        U5      nUR!                  S5      U l        UR!                  S5      U l        g)z
Bind to the app and get SID, gsessionid session identifiers.
If the chromecast is already in another YouTube session you should get
the SID, gsessionid for that session.
SID, gsessionid are used as url params in all further session requests.
r   ry   r   )r   r   r   N)rT   rU   r   r   r   r   rQ   r   r   r   strcontentresearch	SID_REGEXGSESSION_ID_REGEXgrouprS   rR   )r.   r   r   r   r   sidr   s          r0   r   YouTubeSession._bind   s     	499c1dA6
#T%7%78==	7(2 ! 4h&&'ii	7+YY0':
IIaL	&,,Q/r2   c                 P   [         U[        [        [        U[        S[
        S[        U[        S0nU R                  U5      n[        U R                  [        U R                  [        U R                  [        S[         S0nU R#                  [$        U[&        U R(                  0SUS9  g)z?
Initialize a queue with a video and start playing that video.
falser   ry   Tr   r   r   r   N)LIST_IDACTIONACTION_SET_PLAYLISTCURRENT_TIMECURRENT_INDEX
AUDIO_ONLYVIDEO_IDCOUNT_format_session_paramsr   rS   r   rR   r   rT   r   r   r   r   r   rQ   )r.   r_   r`   ra   request_datar   s         r0   r^    YouTubeSession._initialize_queue   s       3$j%r"G (q$ 22<@499j$2C2C499c1dA7
h\<LdN`N`;a&*: 	 	?r2   c           
      |   U R                   (       d  U R                  5         OU R                  5         [        U[        U[
        S0nU R                  U5      n[        U R                  [        U R                  [        U R                  [        S[        S0nU R                  [         U["        U R$                  0SUS9  g)z{
Sends actions for an established queue.
:param video_id: id to perform the action on
:param action: the action to perform
r   ry   Tr   N)rZ   r]   r   r   r   r   r   r   rS   r   rR   r   rT   r   r   r   r   r   rQ   )r.   r_   actionr   r   s        r0   re   YouTubeSession._queue_action   s     ! JJL (q" 22<@499j$2C2CS$))UXZ[]acde
h\<LdN`N`;a&*: 	 	?r2   c                     [         R                  U R                  S9nUR                  5        VVs0 s H"  u  p4UR	                  S5      (       a  X#-   OUU_M$     snn$ s  snnf )N)req_id_)
REQ_PREFIXformatrU   items
startswith)r.   
param_dict	req_countr   rz   s        r0   r   %YouTubeSession._format_session_params   sU    %%T__%=	ISIYIYI[\I[c!2!2	1<I[\\\s   )ANc           	      n   U(       a  [        S0 [        [        40 UD6D6nO[        n[        R                  " XX#S9nUR                  S:X  d  UR                  S:X  a  U(       a  U R                  5         UR                  5         U(       a  U =R                  S-  sl        U =R                  S-  sl        U$ )a  
Calls requests.post with custom headers,
 increments RID(request id) on every post.
will raise if response is not 200
:param url:(str) request url
:param data: (dict) the POST body
:param params:(dict) POST url params
:param headers:(dict) Additional headers for the request
:param session_request:(bool) True to increment session
 request counter(req_count)
:return: POST response
)r   r   r   i  i  r   r6   )	r=   HEADERSrequestspoststatus_coder   raise_for_statusrU   rT   )r.   urlr   r   r   r   r   s          r0   r   YouTubeSession._do_post   s     6T'5W56GG==DP   C'8+?+?3+FOJJL!!#OOq O		Q	r2   )rR   rQ   rU   rT   rP   rS   )rt   0)NNNF)rF   rG   rH   rI   __doc__r-   propertyrZ   rb   rh   rl   rq   rv   r   r   r   r]   r   r   r^   re   r   r   rJ   r6   r2   r0   rM   rM   >   sj    <  	>144- 
"*0*?$?0]r2   rM   ))r   r   html.parserr   r   YOUTUBE_BASE_URLr   r   r   r   r   r   r?   r   r   r   r   r   r   r   r   r   r   ru   rp   rk   rf   r   r   r   r   r   r   r   r   r   r   r   r'   objectrM   r6   r2   r0   <module>r      s   	  " - 22#&QQ !$66%7Z
[- 
G " 	

	# 
7 
	'/KU]v>VX	
8j 
8MV Mr2   