o
    üt h8  ã                   @   sô   d dl Z d dlmZ d dlmZ d dlm  mZ d dlZd dl	Z	d dl
Z
dZdZdZdZe	je	jdd	 d
Zdd„ Zdedededefdd„Zefdededefdd„Zdededefdd„Zdd„ ZG dd„ deƒZG dd „ d eƒZd!d"„ ZdS )#é    N)ÚInlineProcessor)Ú	Extensionzdata-song-idz
data-startzdata-endZm4az%(levelname)s: %(message)s)ÚlevelÚformatz\[([^\]]+)\]\(audio:([^)]+)\)c                 C   sX   t | ƒ}|sdS t dd|¡}t dd|¡}|r(|d  ¡ s$|d dkr(d| }|p+dS )zIReplaces characters potentially problematic in HTML IDs. Accepts strings.Ú_ú:Ú-z[^a-zA-Z0-9_-]Ú r   )ÚstrÚreÚsubÚisdigit)ÚtextZstr_textZ	sanitized© r   ú7/Users/srajan/code/music-website/markdown_extensions.pyÚsanitize_for_id   s   r   Úsong_idÚstart_secondsÚend_secondsÚreturnc                 C   s4   t | ƒ}t t|ƒƒ}t t|ƒƒ}|› d|› d|› S )zzGenerates a unique and sanitized HTML ID for an audio element.
    Uses integer part of seconds for ID compatibility.
    r   )r   Úint)r   r   r   Zsong_id_sanitizedZstart_seconds_sanitizedZend_seconds_sanitizedr   r   r   Úgenerate_audio_element_id$   s   r   Ú
element_idr   c                 C   s   | › d|› S )zOGenerates the filename for an audio segment based on its element ID and format.Ú.r   )r   r   r   r   r   Úgenerate_audio_filename.   ó   r   ÚfilenameÚbase_dirc                 C   s   |  tjd¡› d| › }|S )z7Generates the src attribute value for an audio element.ú/)ÚreplaceÚosÚsep)r   r   Zweb_pathr   r   r   Úgenerate_audio_src2   s   r"   c              	   C   sê   | sdS t | ƒ d¡}d}zRt|ƒdkrt|d ƒ}n:t|ƒdkr2tt|d ƒd t|d ƒ ƒ}n#t|ƒdkrQtt|d ƒd	 t|d ƒd  t|d ƒ ƒ}ntd
ƒ‚|dk r]tdƒ‚|W S  ttfyt   t d| › d¡ Y dS w )zOConverts time string (e.g., 'H:MM:SS', 'M:SS', 'SS', 'SS.ms') to float seconds.Nr   g        é   r   é   é<   é   i  zInvalid time formatzTime cannot be negativezCould not parse time string: 'ú')	r
   ÚsplitÚlenÚfloatr   Ú
ValueErrorÚ	TypeErrorÚloggingÚwarning)Ztime_strÚpartsÚsecondsr   r   r   Útime_str_to_seconds9   s&   "2þr1   c                       s&   e Zd Zd‡ fdd„	Zdd„ Z‡  ZS )ÚAudioClipInlineProcessorNc                    s2   t ƒ  ||¡ || _|| _| jst d¡ d S d S )NzAAudioClipInlineProcessor initialized without relative_audio_path!)ÚsuperÚ__init__Úfallback_song_idÚrelative_audio_pathr-   Úerror)ÚselfÚpatternÚmdr5   r6   ©Ú	__class__r   r   r4   T   s   ÿz!AudioClipInlineProcessor.__init__c              	   C   s–  |  d¡}|  d¡}i }z$| d¡D ]}| ¡ }d|v r.| dd¡\}}| ¡ || ¡  ¡ < qW n  tyP   t d|› d¡ |  d¡| d¡| d¡f Y S w | 	d| j
¡}	| 	d	¡}
| 	d
¡}t|
ƒ}t|ƒ}t|	|
|gƒs¢dd„ |	|
|dœ ¡ D ƒ}t dd |¡› d|  d¡› d| j
› d¡ |  d¡| d¡| d¡fS |d u sª|d u rÊt d|  d¡› d|
› d|› d¡ |  d¡| d¡| d¡fS t|	||ƒ}t|ƒ}| jsñt d|  d¡› d¡ |  d¡| d¡| d¡fS t|| jd}t d¡}t |d¡}| dd¡ | d|¡ ||_t |d¡}| d|¡ | d |¡ | t|	¡ | tt|ƒ¡ | tt|ƒ¡ | d!d"¡ || d¡| d¡fS )#Nr#   r$   ú,Ú=z"Malformed audio parameters found: z. Skipping.r   r   ÚfromÚtoc                 S   s   g | ]\}}|s|‘qS r   r   )Ú.0ÚkÚvr   r   r   Ú
<listcomp>r   s    z8AudioClipInlineProcessor.handleMatch.<locals>.<listcomp>)r   r?   r@   zMissing audio parameters (z, z) in: z. Fallback was 'z'. Skipping generation.zInvalid time format in: z. Could not convert 'from' ('z') or 'to' ('z#') to seconds. Skipping generation.zCannot generate audio src for z - relative_audio_path not set.)r   ÚspanÚclasszplay-triggerzdata-audio-targetÚaudioÚidÚsrcZpreloadÚmetadata)Úgroupr(   ÚstripÚlowerr+   r-   r.   ÚstartÚendÚgetr5   r1   ÚallÚitemsÚjoinr   r   r6   r7   r"   ÚetreeÚElementÚ
SubElementÚsetr   ÚDATA_ATTR_SONG_IDÚDATA_ATTR_STARTr
   ÚDATA_ATTR_END)r8   ÚmÚdataZdisplay_textZparam_stringÚparamsÚpairÚkeyÚvaluer   Zstart_time_strZend_time_strr   r   Zmissing_paramsZaudio_element_idZaudio_filenameZ	audio_srcÚwrapperZtrigger_spanZaudio_elr   r   r   ÚhandleMatch[   s\   

€ü þ

,$
z$AudioClipInlineProcessor.handleMatch)NN)Ú__name__Ú
__module__Ú__qualname__r4   rb   Ú__classcell__r   r   r;   r   r2   S   s    r2   c                       s$   e Zd Z‡ fdd„Zdd„ Z‡  ZS )ÚAudioExtensionc                    s*   ddgddgdœ| _ tƒ jdi |¤Ž d S )Nr	   z3Default song ID to use if not specified in the linkzDRelative path from HTML output to audio files (e.g., 'assets/audio')©r5   r6   r   )Úconfigr3   r4   )r8   Úkwargsr;   r   r   r4   ™   s   þzAudioExtension.__init__c                 C   sF   |   d¡}|   d¡}|st d¡ d S |j tt|||ddd¡ d S )Nr5   r6   z?AudioExtension requires 'relative_audio_path' to be configured.rh   Z
audio_clipé¯   )Z	getConfigr-   r7   ZinlinePatternsÚregisterr2   ÚAUDIO_CLIP_RE)r8   r:   Zfallback_idZrelative_pathr   r   r   ÚextendMarkdown    s   


ýzAudioExtension.extendMarkdown)rc   rd   re   r4   rn   rf   r   r   r;   r   rg   ˜   s    rg   c                  K   s   t di | ¤ŽS )z  Facilitates extension loading. Nr   )rg   )rj   r   r   r   ÚmakeExtension­   r   ro   )ÚmarkdownZmarkdown.inlinepatternsr   Zmarkdown.extensionsr   Zxml.etree.ElementTreerT   ÚElementTreer   r-   r    rX   rY   rZ   ÚDEFAULT_AUDIO_FORMATÚbasicConfigÚWARNINGrm   r   r
   r*   r   r   r"   r1   r2   rg   ro   r   r   r   r   Ú<module>   s*   
E