U
    ‹pÙjÙ<  ã                   @   s0  d Z ddlZddlZddlZddlmZmZ ddlmZ ddlm	Z	 ddl
Z
ddlZddlmZmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ e d¡Zeeeeƒ d¡ƒƒZej  d¡dkZ!dd„ Z"dd„ Z#dd„ Z$dd„ Z%dd„ Z&dd„ Z'dd„ Z(dd„ Z)e*d œd!d"„Z+dd#œd$d%„Z,dS )&zsHermes LMP V5 runtime. Python 3.8+. No credentials stored here.
Safe by default: dry-run unless HERMES_LMP_LIVE=1.
é    N)ÚdatetimeÚ	timedelta)ÚPath)Úurlsplit)ÚasociarÚidentificar)Úvalidar_asociaciones)Úobtener_inventario)Úextraer_playlist)Ú
planificar)Ú
StateStorezAmerica/Tijuanazhermes_lmp_v5_live_state.jsonZHERMES_LMP_LIVEÚ1c                 C   sL   |   d¡}|stdƒ‚t t|ƒ dd¡¡}|jdkrBtj 	|¡}| 
t¡S )zRUTC oficial del EPG -> hora con zona; no reinterpretar localFullDate como Tijuana.ZutcFullDatez?Partido sin utcFullDate; no se autoriza inicio por hora ambiguaÚZz+00:00N)ÚgetÚ
ValueErrorr   ÚfromisoformatÚstrÚreplaceÚtzinfoÚpytzZUTCZlocalizeZ
astimezoneÚTZ)ÚgameZutcZdt© r   úhermes_lmp_v5_runtime.pyÚ_inicio_epg   s    

r   c              	      s¬   t j| d |d| | d dœdddd ˆ ¡igd	œd
d�}| ¡  | ¡ }dd„ | dg ¡D ƒ}t|ƒdkrrtdƒ‚‡ fdd„|d  dg ¡D ƒ}t|ƒtˆ ƒkr¨tdƒ‚|S )Nú/api/actionúapplication/jsonz/config/lmp©ÚAuthorizationzContent-TypeZOriginZRefererZ	getstatusÚlmpÚIdú,)ÚActionÚ
ProviderIdÚStreamsé   ©ÚheadersÚjsonÚtimeoutc                 S   s   g | ]}|  d ¡dkr|‘qS )r    r   ©r   )Ú.0Úpr   r   r   Ú
<listcomp>+   s      z_getstatus.<locals>.<listcomp>Z	Providersé   zProveedor LMP ambiguoc                    s$   i | ]}|  d ¡ˆ kr|d  |“qS )r    r*   ©r+   Úc©Úidsr   r   Ú
<dictcomp>.   s       z_getstatus.<locals>.<dictcomp>r   r$   z#Inventario y getstatus no coinciden)	ÚhttpxÚpostÚjoinÚraise_for_statusr(   r   ÚlenÚRuntimeErrorÚset)ÚhostÚauthr2   ÚresponseÚdataZ	providersÚfoundr   r1   r   Ú
_getstatus#   s(    
  þÿür@   c           
         sÊ   t j| d d|idd�}| ¡  dd„ t|jƒD ƒ}i }| ¡ D ]d\}}t|ƒ‰ ‡ fdd„|D ƒ}t|ƒdkrztd	| ƒ‚|d
 d }	t	|	ƒj
dkrœtdƒ‚|	||< q@tt| ¡ ƒƒt|ƒkrÆtdƒ‚|S )Nz/playlist.m3ur   é   )r'   r)   c                 S   s&   g | ]}|d    ¡  ¡  d¡r|‘qS )Únamez[lmp])ÚstripÚlowerÚ
startswithr/   r   r   r   r-   7   s    ÿz'_validated_playlist.<locals>.<listcomp>c                    s,   g | ]$}t ˆ ƒd krt|d ƒˆ kr|‘qS )é   rB   )r8   r   r/   ©Zteamsr   r   r-   <   s       r.   zM3U ambigua para r   Úurl)ZhttpZhttpszURL invalidazURLs duplicadas)r4   r   r7   r
   ÚtextÚitemsr   r8   r9   r   Zschemer:   Úvalues)
r;   r<   Ú	inventoryr=   ZchannelsÚresultÚcidrB   ÚmatchesrH   r   rG   r   Ú_validated_playlist4   s     
rP   c                 C   s`   |d| | d dœ}dD ]:}t j| d ||ddœdd	�}| ¡  |d
krt d¡ qt d¡ d S )Nr   z/config/lmp?type=liver   )ÚchannelsupdaterequestZchannelsupdateapplyr   r   )r"   r#   r%   r&   rQ   é   )r4   r5   r7   ÚtimeÚsleep)r;   r<   r'   Úactionr=   r   r   r   Ú_refresh_providerH   s     ÿ ÿrV   c                 C   st   t |  dd¡ƒt |  dd¡ƒ }}t|ƒdksJt|ƒdksJt||B ƒdkrNd S t|  dd¡ƒd d t||B ƒ¡ S )NZawayTeamShortÚ ZhomeTeamShortr.   rF   ZlocalFullDateú|)r   r   r8   r   r6   Úsorted)r   ZawayÚhomer   r   r   Ú_keyT   s    "(r[   c           	      C   s   | d }|D ]î}|d }|  d¡}| |dd dd g dddœ¡}|d |d	< | ¡ |d
< t|ƒ ¡ |d< |dkrº|  d¡dkr�| ¡ |d< d|d< d|d< |  dd¡d |d< | ¡ |d< q|dkrÜd|d< d|d< d |d< q|dkr|  d¡dkrd|d< qdS )NÚgamesÚ_hermes_keyÚ
gameStatusÚunknownr   F)ÚstatusÚfirst_final_seenÚfinal_confirmationsÚlast_final_seenÚstart_attemptsÚpreparedÚstop_confirmedÚ_hermes_channelÚ
channel_idÚ	last_seenÚstart_atrF   r`   Úfinalra   rb   r.   rc   ÚliveZ	scheduledT)r   Ú
setdefaultÚ	isoformatr   )	r>   re   Z
status_mapÚnowr\   r   ÚkeyÚcodeÚrecr   r   r   Ú_apply_observation[   s8    

  ý

rs   c                  C   sL   t j ¡ sdi iS t t jjdd�¡} t| tƒr@t|  d¡tƒsHt	dƒ‚| S )zDNever silently replace damaged persistent state with an empty state.r\   zutf-8)ÚencodingzEstado V5 invalido)
ÚSTATEÚpathÚexistsr(   ÚloadsZ	read_textÚ
isinstanceÚdictr   r   )r>   r   r   r   Ú_strict_snapshotz   s    
r{   c                 C   sê  t ƒ d }| ¡ D �]Ð\}}| d¡}| |¡}	|	dkr:q|	 d¡}
| dg ¡}|�r"|d  d¡dk�r"|d }z|t |d	 ¡  ¡ }W n& tttfk
r²   td
| ƒ‚Y nX |
dkræ||d fdd„}t	 
|¡ |d| ƒ n<|
dk�r"|dk�r"||d fdd„}t	 
|¡ |d| ƒ | d¡�p0i }| d¡dkrz|t |d	 ¡  ¡ }W n( tttfk
�r‚   td| ƒ‚Y nX |
dk�r²|fdd„}t	 
|¡ |d| ƒ q|
dkr|dkr|fdd„}t	 
|¡ |d| ƒ qdS )zJResolve uncertain commands from observed O11 state, not from HTTP outcome.r\   rh   NÚRunningrd   éÿÿÿÿr`   ©ÚreservedÚsentrS   z$Intento pendiente sin fecha valida: TÚattemptc                 S   s<   | d | d d }|  d¡|kr8|  d¡dkr8d|d< d S )Nr\   rd   r}   r�   r`   r~   Ú	confirmedr*   ©ÚdÚkZnumberÚar   r   r   Úconfirm•   s    z#_reconcile_pending.<locals>.confirmzHERMES V5 RECUPERADO START FéZ   c                 S   s<   | d | d d }|  d¡|kr8|  d¡dkr8d|d< d S )Nr\   rd   r}   r�   r`   r~   Úunconfirmedr*   rƒ   r   r   r   Úfail�   s    z _reconcile_pending.<locals>.failzHERMES V5 START NO CONFIRMADO Ústop_operationz!STOP pendiente sin fecha valida: c                 S   s<   | d | }|  d¡pi   d¡dkr8d|d d< d|d< d S )Nr\   r‹   r`   r~   r‚   Trf   r*   ©r„   r…   Úrr   r   r   Ústop_okª   s    z#_reconcile_pending.<locals>.stop_okzHERMES V5 RECUPERADO STOP c                 S   s4   | d | }|  d¡pi   d¡dkr0d|d d< d S )Nr\   r‹   r`   r~   r‰   r*   rŒ   r   r   r   Ú	stop_fail²   s    z%_reconcile_pending.<locals>.stop_failzHERMES V5 STOP NO CONFIRMADO )r{   rJ   r   r   r   Útotal_secondsr   Ú	TypeErrorÚKeyErrorru   Ú
actualizar)r;   r<   Ústatusesro   ÚlogÚsnapshotrp   rr   rN   ÚchannelÚrunningÚattemptsÚlastZager‡   rŠ   ÚstoprŽ   r�   r   r   r   Ú_reconcile_pending„   sH    








rœ   )r•   c          $   	      sn  t  t¡‰|d  d¡|d  }}| d  dg ¡| d  dg ¡ }t||ƒ}t||t|ƒƒ‰t||ƒ}i }	ˆ 	¡ ˆt
dd�  	¡ fD ]}
|	 t||||
 ¡ ƒ¡ q„g ‰|D ]L}t|ƒ‰ˆ|	krªt|ƒ}ˆ|	ˆ  |d	< |d
< t|ƒ ¡ |d< ˆ |¡ qªˆ�s
|dƒ dS t ‡‡‡fdd„¡ t||ˆˆ|ƒ tƒ d }d}ˆD �]¶}|d	 |d
  ‰}t|ƒ}|t
dd� ˆ  k�oŠ|t
dd� kn  }| d¡dk�oÌ|t
dd� ˆ  k�oÈ|t
dd� kn  }|�sÞ|�sÞ�qB|ˆ }| d¡�sB| d¡dk�r�qBt�r:|�s:t||ƒ t||ƒ}t||t|ƒƒ‰d}t|||ƒ}t||ƒ}| ˆ¡|k�rltdˆ ƒ‚|ˆ }t�r°|d  ¡ D ](\}}|t|| ƒk�r†|| |d< �q†t�rÞ||	ˆ< ||d
< t ˆ|f‡fdd„	¡ |dˆ t�rðd nd! ƒ �qBtƒ d }i }ˆD ]P}|d	 ‰|ˆ }| d"|d
 ¡}ˆ |i ¡}t|| d#¡| d$¡d%�|ˆ< �qtˆ|	|ˆƒ}|D �]ô}|d& |d'  ‰}|ˆ }|| d"¡k�sr| d¡�s²�qr|d( d)k�rê| d*g ¡}|�rê|d+  d¡d,k�rê�qr|d( d-k�r| d.¡�r�qr|d( d-k�rX| d/d0¡d1k �r,�qr| d2¡}|�rrˆt  |¡  ¡ d3k�rX�qrt�sz|d4|d(  d5 ˆ ƒ �qrd0d6lm } | d7¡‰ ˆ �r¸‡ ‡‡fd8d9„}t |¡�sÖ�qrn‡‡fd:d;„} t | ¡�sÖ�qr|||dd<� !|d( |¡ t|||gƒ|  d#¡}!|d( d)k}"|!|"k	�r6td=|d(  d5 | ƒ‚‡ ‡fd>d?„}#t |#¡ |d@|d(  d5 ˆ ƒ �qrdS )AzBOne synchronous scheduler tick; does not start a second scheduler.r;   ú/r<   r=   r\   Z	prevGamesr.   )Zdaysr]   rg   Z_hermes_start_atz1HERMES V5: sin asociaciones validas; sin accionesNc                    s   t | ˆˆˆ ƒS )N)rs   )r„   )ro   re   r”   r   r   Ú<lambda>Ð   ó    ztick.<locals>.<lambda>Fé
   ©ZminutesrA   r^   é   ©Zhoursre   r`   rk   Tz'Canal cambiado o ausente tras refresh: r   Zregionalc                    s   | d |   d|ˆ  ¡ dœ¡S )Nr\   T)re   rh   Zprepared_at)Úupdatern   )r„   r…   r0   ©ro   r   r   rž   ó   s   ÿz HERMES V5: preparacion validada z LIVEz DRYrh   r|   ZOnDemand)r˜   Zondemandr   r—   rU   Ústartrd   r}   r~   r›   rf   rb   r   rF   rc   é,  zHERMES V5 DRY ú )Ú
O11Actionsr�   c                    sF   | d ˆ }t | dg ¡ƒˆ d kr(dS |d  ˆ dˆ ¡ dœ¡ dS )Nr\   rd   r.   Fr   )r�   r`   rS   T)r8   r   Úappendrn   )r„   r�   )r�   rp   ro   r   r   Úreserve  s    ÿztick.<locals>.reservec                    sP   | d ˆ  }|  d¡rdS |  d¡p&i }|  d¡dkr:dS dˆ ¡ dœ|d< d	S )
Nr\   rf   Fr‹   r`   )r   r€   r‚   r   )r`   rS   T)r   rn   )r„   r�   Úop)rp   ro   r   r   Úreserve_stop   s    
ztick.<locals>.reserve_stop)Zdry_runzO11 no confirmo c                    sL   | d ˆ }ˆ r4|d d }|  d¡ˆ krHd|d< nd|d d< d|d	< d S )
Nr\   rd   r}   r�   r‚   r`   r‹   Trf   r*   )r„   r�   r†   )r�   rp   r   r   r‡   1  s    
ztick.<locals>.confirmzHERMES V5 CONFIRMADO )"r   ro   r   Úrstripr   r	   r@   Úlistr   Údater   r¤   r   rn   r[   rz   r   rª   ru   r“   rœ   r{   ÚLIVErV   rP   r9   rJ   r   r   r   r�   Zhermes_lmp_actionsr©   Zejecutar)$ZepgZpanelZstream_databaser•   r;   r<   r\   rL   rO   ZvalidZdayr   Úgr–   Zrefreshed_this_tickrN   r¦   Z
en_ventanaZrecuperacion_liverr   ZlinksZcurrentÚslugZdbZstatesr—   ZactionsrU   r™   rš   r©   r«   r­   ZverifiedZdesiredr‡   r   )r�   rp   ro   re   r”   r   Útickº   sÊ    
 



.,ÿ



"


	
	
r´   r¥   c          	      C   s2  t sdS |pt t¡}� zütƒ }|d  ¡ D ]ä\}}| | d¡dd… krLq,| d¡s,| d¡sbq,| d¡}|srq,t |¡}|j	dkrˆq,||t
d	d
� k rœq,||t
dd� kr¶ W dS | d¡dkr,| d¡}|r,t |¡}|j	dk	r,d||  ¡   k�rdkr,n q, W dS q,W n tk
�r,   Y dS X dS )z3Fail closed for LMP only when live mode is enabled.Tr\   rX   r.   Nrf   re   rj   r    r¡   r¢   r£   r`   rl   ri   r   r§   F)r±   r   ro   r   r{   rJ   Úsplitr   r   r   r   r�   Ú	Exception)	r³   ro   r>   rp   r   Z
start_textr¦   ÚseenZobservedr   r   r   Úaccess_allowed>  s:    




,r¸   )-Ú__doc__Úosr(   rS   r   r   Úpathlibr   Zurllib.parser   r4   r   Zhermes_lmp_matcherr   r   Zhermes_lmp_guardr   Zhermes_lmp_inventoryr	   Zhermes_lmp_playlistr
   Zhermes_lmp_coordinatorr   Zhermes_lmp_v5_stater   Útimezoner   r   Ú__file__Z	with_nameru   Úenvironr   r±   r   r@   rP   rV   r[   rs   r{   rœ   Úprintr´   r¸   r   r   r   r   Ú<module>   s8   

6 