U
    ]0h7                     @   st   d 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 ddlmZmZ G dd	 d	e	ZG d
d de	ZdS )a  
Channel Audio Management Models

This module contains models for managing channel jingles, station IDs, and audio detection:
- Jingle: Audio content played during broadcasts
- JingleDetection: Detection logs for jingles in live streams

These models handle audio fingerprinting, scheduling, and automatic detection
of jingles in broadcast streams for compliance and monitoring purposes.
    )models)timezone)gettext_lazy)	BaseModel)Channel)jingle_upload_pathmd5_upload_pathc                       s  e Zd ZdZdedfdedfdedfded	fd
edfdedfdedfdedfdedfdedfdedfgZdedfdedfdedfdedfd ed!fd"ed#fd$ed%fd&ed'fd(ed)fd*ed+fd,ed-fd.ed/fd0ed1fgZd2ed3fd4ed5fd6ed7fd8ed9fd:ed;fd<ed=fgZej	e
ejd>ed?ed@dAdAdBZejdCedDedEdFZejdGeedHedIdJdKZejdGed.edLedMdJdNZejed6edOedPdQZejeedRedSdTZejeedUedVdJdJdWZejedXedYdJdJdZZejed[ed\dJdJdZZejedJed]ed^d_ZejdJed`edadbZ ejdJedcedddbZ!ej"dJedeedfdgZ#ej$dJdJedhedidjZ%ej$dJdJedkedldjZ&eje'dJedmednd_Z(ejdoedpedqdgZ)ej*dJdJedredsdjZ+G dtdu duZ,dvdw Z-dxdy Z.dzd{ Z/d|d} Z0 fd~dZ1dd Z2dd Z3 fddZ4dd Z5dd Z6dd Z7e8dd Z9e8dd Z:e8dd Z;e8dd Z<e8dd Z=e8dd Z>dd Z?  Z@S )Jinglea  
    Channel jingles and station IDs with enhanced placement types.
    
    Represents audio content that is played during broadcasts including
    station identifications, bumpers, promos, and transition sounds.
    Supports scheduling, audio fingerprinting, and usage tracking.
    
    Attributes:
        channel: Foreign key to the channel this jingle belongs to
        name: Human-readable name for the jingle
        jingle_type: Type of jingle (station_id, bumper, promo, etc.)
        placement_type: When the jingle should be played in the broadcast
        file: Audio file for the jingle
        duration: Length of the jingle in seconds
        audio_fingerprint: Audio fingerprint data for automatic detection
        frames_fingerprint: I-Frame fingerprint data for automatic detection
        is_active: Whether this jingle is currently active
        priority: Priority level for jingle selection
        start_date: Optional start date for jingle validity
        end_date: Optional end date for jingle validity
        time_slots: JSON field with time ranges when jingle can play
        play_count: Number of times this jingle has been played
        last_played: Timestamp of last play
    
    Related Models:
        - Channel: One-to-many relationship (channel can have multiple jingles)
        - JingleDetection: One-to-many relationship (jingle can have multiple detections)
    Z
station_idz
Station IDZbumperZBumperZpromoZPromoZ
transitionZ
TransitionZcommercial_breakzCommercial BreakZ
news_introz
News IntroZweather_introzWeather IntroZsports_introzSports IntroZmusic_introzMusic IntroZshow_openerzShow OpenerZshow_closerzShow CloserstartzStart of ShowendzEnd of Showcommercial_startzStart of Commercial Breakcommercial_endzEnd of Commercial BreakZprogram_transitionzProgram TransitionZsegment_transitionzSegment TransitionhourlyZHourlyZhalf_hourlyzHalf HourlyZquarter_hourlyzQuarter HourlyZtop_of_hourzTop of HourZbottom_of_hourzBottom of HourrandomzRandom PlacementmanualzManual Only   zVery Low   ZLow   ZNormal   ZHigh   z	Very High   Criticaljinglesr   zChannel this jingle belongs toF)	on_deleterelated_nameverbose_name	help_textblanknull   zJingle Namez#Human-readable name for this jingle)
max_lengthr   r      zJingle TypezType of jingle contentT)r    choicesr   r   db_indexzPlacement Typez2When this jingle should be played in the broadcast)r    r"   defaultr   r   r#   zPriority LevelzEPriority level for jingle selection (higher number = higher priority))r"   r$   r   r   z
Audio FilezHAudio file for this jingle (automatically organized by channel and date))	upload_tor   r   zMD5 Checksum Filez>MD5 checksum file (automatically generated after audio upload))r%   r   r   r   r   zDuration (seconds)z!Duration of the jingle in seconds)r   r   r   r   zFile Size (bytes)zSize of the audio file in byteszAudio Metadataz@Additional audio metadata (bitrate, sample rate, channels, etc.)r$   r   r   r   Audio Fingerprintz9Audio fingerprint data for automatic detection in streams)r   r   r   zFrames Fingerprintz:Frames fingerprint data for automatic detection in streamsz	Is Activez'Whether this jingle is currently active)r$   r   r   z
Start Datez7Optional start date for when this jingle becomes active)r   r   r   r   zEnd Datez7Optional end date for when this jingle becomes inactivez
Time Slotsz2JSON list of time ranges when this jingle can playr   z
Play Countz+Number of times this jingle has been playedzLast Playedz-Timestamp of when this jingle was last playedc                   @   s   e Zd ZedZedZdZdddgZej	ddgdej	d	dgdej	d
ddgdgZ
ejejddddejejddddgZdS )zJingle.Metar	   Jinglesr   channelz-created_atname	is_activefieldsplacement_typepriority
created_atr   )Zduration__gteZpositive_duration)checkr*   )Zfile_size__gteZpositive_file_sizeN)__name__
__module____qualname___r   verbose_name_pluraldb_tableorderingr   IndexindexesCheckConstraintQconstraints r>   r>   7/var/www/html/Focus/src/apps/channels/models/jingles.pyMeta   s"   


r@   c                 C   s    | j  d| jr| jj nd dS )Nz (z
No Channel))r*   r)   selfr>   r>   r?   __str__   s    zJingle.__str__c              
   C   s   | j s
dS t }| jr*| | jk r*dS | jrB| | jkrBdS | jr| }| jD ]r}zPtj	|d d }tj	|d d }||  kr|krn nW  dS W qV t
tfk
r   Y qVY qVX qVdS dS )aP  
        Check if jingle can be played at the current time.
        
        Returns:
            bool: True if jingle can be played now, False otherwise
            
        Checks:
            - Jingle is active
            - Current date is within start/end date range
            - Current time is within allowed time slots
        Fr
   z%H:%Mr   T)r+   r   now
start_datedateend_date
time_slotstimedatetimestrptimeKeyError
ValueError)rC   rE   Zcurrent_timeslot
start_timeend_timer>   r>   r?   can_play_now   s&    
zJingle.can_play_nowc                 C   s,   |  j d7  _ t | _| jddgd dS )z
        Mark jingle as played and update usage statistics.
        
        Updates:
            - Increments play_count
            - Sets last_played to current timestamp
        r   
play_countlast_playedupdate_fieldsN)rS   r   rE   rT   saverB   r>   r>   r?   mark_played   s    
zJingle.mark_playedc                 C   s   | j | j| j|  dS )z
        Get usage statistics for this jingle.
        
        Returns:
            dict: Dictionary containing usage statistics
        rS   rT   r+   rR   rY   rB   r>   r>   r?   get_usage_stats,  s
    zJingle.get_usage_statsc                    s   d}| j rHz tjj| j d}|j| jk}W qL tjk
rD   d}Y qLX nd}t j|| |r| jr|   | 	  t jdddgd dS )	zb
        Enhanced save method with automatic MD5 generation and file metadata extraction.
        F)pkTmd5_fileduration	file_sizerU   N)
r[   r(   objectsgetfileDoesNotExistsuperrW   _generate_md5_file_extract_file_metadata)rC   argskwargsZis_new_fileZold_instance	__class__r>   r?   rW   :  s    
zJingle.savec              
      s   j s
dS zt } j d t fdddD ]}|| q2| }tj	 j j
}| d| d}tj|d  d}t |}dd	lm}  jj|||d
dd W nR tk
r }	 z2ddl}
|
t}|d j dt|	  W 5 d}	~	X Y nX dS )zI
        Generate MD5 checksum file for the uploaded audio file.
        Nr   c                      s    j dS Ni   ra   readr>   rB   r>   r?   <lambda>a      z+Jingle._generate_md5_file.<locals>.<lambda>rn   z  
z.md5)ContentFileutf-8F)rW   z"Failed to generate MD5 for jingle : )ra   hashlibmd5seekiterupdate	hexdigestospathbasenamer*   splitextr   django.core.files.baserp   r\   rW   encode	Exceptionlogging	getLoggerr2   error	id_jinglestr)rC   md5_hashchunkZmd5_checksumZoriginal_filenameZmd5_contentZmd5_filenameZmd5_pathrp   er   loggerr>   rB   r?   rd   T  s,    


zJingle._generate_md5_filec                 C   s  | j s
dS ddl}|t}z
| j j| _d}zddlm} t| j drR| j j	n| j j j
}||}|r<t|dr<t|jdrt|jj| _d}|d	| j d
| j d i }t|jdr|jj|d< t|jdr|jj|d< nt|jdr|jj|d< t|jdr|jj|d< t|jdr6t|jj|d< || _W nV tk
r^   |d Y n8 tk
r } z|dt|  W 5 d}~X Y nX |szddlm}	 ddlm}
 |
ds|
dr(t| j dr| j j	n| j j j
}|	|}tt|d | _d}|d| j d
| j d n
|d W nV tk
rT   |d Y n8 tk
r } z|dt|  W 5 d}~X Y nX |sLzbddl }t| j dr| j j	n| j j j
}|j!|d}t|| _d}|d| j d
| j d W nV tk
r   |d Y n8 tk
rJ } z|dt|  W 5 d}~X Y nX |s"| j j
" #d r"zddl$}t| j dr| j j	n| j j j
}|%|d!D}|& }|' }t|| | _d}|d"| j d
| j d W 5 Q R X W n8 tk
r  } z|d#t|  W 5 d}~X Y nX |sАz(ddl(}ddl)}t| j drR| j j	n| j j j
}dd$d%d&d'd(d)|g}|j*|ddd*d+}|j+dkrP|,|j-}d}d,|krd-|d, krt.|d, d- }nXd.|kr t|d. dkr |d. D ]2}|/d/d0krd-|krt.|d- } q q|rPt|| _d}|d1| j d
| j d W n| |j0|j1t2|j3fk
r } z|d2t|  W 5 d}~X Y n8 tk
r } z|d3t|  W 5 d}~X Y nX |s|d4| j d5 d*| _| jr&| jdkr&|d6| j d7| j d8 d*| _W nt tk
r } zT|4d9| j d:t|  t| d;rl| jsrd| _t| d-r| jsd*| _W 5 d}~X Y nX dS )<z
        Extract comprehensive metadata from the audio file including duration, 
        bitrate, sample rate, channels, and other audio properties.
        Nr   F)Filerz   infolengthTz,Extracted metadata using mutagen for jingle z: duration=sbitratesample_rate
sampleratechannelsmodez8Mutagen not installed. Install with: pip install mutagenz)Failed to extract metadata with mutagen: )AudioSegment)whichZffprobeZffmpegi  z*Extracted metadata using pydub for jingle z>ffmpeg/ffprobe not found. Install ffmpeg for audio processing.z4pydub not installed. Install with: pip install pydubz'Failed to extract metadata with pydub: )rz   z,Extracted metadata using librosa for jingle z8librosa not installed. Install with: pip install librosaz)Failed to extract metadata with librosa: .wavrz)Extracted metadata using wave for jingle z&Failed to extract metadata with wave: z-vquietz-print_formatjsonz-show_formatz-show_streams   )capture_outputtexttimeoutformatr]   streamsZ
codec_typeaudioz,Extracted metadata using ffprobe for jingle z)Failed to extract metadata with ffprobe: zUnexpected error with ffprobe: z&Could not extract duration for jingle z). Setting default duration to 30 seconds.zInvalid duration z for jingle z. Setting to 30 seconds.z&Failed to extract metadata for jingle rr   r^   )5ra   r   r   r2   sizer^   Zmutagenr   hasattrrz   r*   r   intr   r]   r   r   r   r   r   r   r   metadataImportErrorwarningr   Zpydubr   Zpydub.utilsr   	from_filelenlibrosaZget_durationlowerendswithwaveopenZ
getnframesZgetframerate
subprocessr   run
returncodeloadsstdoutfloatr`   TimeoutExpiredCalledProcessErrorFileNotFoundErrorJSONDecodeErrorr   )rC   r   r   Zmetadata_extractedZMutagenFile	file_pathZ
audio_fileZadditional_metadatar   r   r   r   r   duration_secondsr   Zwav_fileframesr   r   r   cmdresultdatar]   streamr>   r>   r?   re   |  s    


& 
& 
 & *&   	

  $&
zJingle._extract_file_metadatac                    s`   t    | jr\ddddddg}tj| jjd  }||kr\tdt	d	
d
|idS )z#
        Model validation.
        z.mp3r   z.aacz.m4az.oggz.flacr   ra   z3Only audio files are allowed. Supported formats: {}z, N)rc   cleanra   ry   rz   r|   r*   r   ValidationErrorr5   r   join)rC   allowed_extensionsZfile_extensionrh   r>   r?   r   -  s    
 zJingle.cleanc                 C   s   | j r| j jS dS )z0
        Get the URL of the audio file.
        N)ra   urlrB   r>   r>   r?   get_file_url?  s    zJingle.get_file_urlc                 C   s   | j r| j jS dS )z7
        Get the URL of the MD5 checksum file.
        N)r\   r   rB   r>   r>   r?   get_md5_urlE  s    zJingle.get_md5_urlc                    s    j r jsdS zx jd  j d}| d }t } j d t fdddD ]}|	| q`|
 }| | kW S  tk
r   Y dS X dS )z
        Verify the integrity of the audio file using the stored MD5 checksum.
        
        Returns:
            bool: True if file integrity is verified, False otherwise
        Fr   rq   c                      s    j dS rj   rk   r>   rB   r>   r?   rm   ^  rn   z.Jingle.verify_file_integrity.<locals>.<lambda>rn   N)ra   r\   ru   rl   decodesplitrs   rt   rv   rw   rx   r   r   )rC   Zstored_md5_contentZ
stored_md5r   r   Zcurrent_md5r>   rB   r?   verify_file_integrityK  s    zJingle.verify_file_integrityc                 C   s0   | j s
dS | j d }| j d }|dd|dS )z2
        Get duration formatted as MM:SS.
        z00:00<   02d:)r]   )rC   minutessecondsr>   r>   r?   formatted_durationg  s
    

zJingle.formatted_durationc                 C   sN   | j s
dS t| j }dD ](}|dk r8|dd|   S |d }q|ddS )zC
        Get file size formatted in human-readable format.
        z0 B)BKBMBGBg      @z.1f z TB)r^   r   )rC   r   unitr>   r>   r?   formatted_file_sizes  s    

zJingle.formatted_file_sizec                 C   s   | j r| j dS dS )zGet bitrate from metadata.r   Nr   r`   rB   r>   r>   r?   r     s    zJingle.bitratec                 C   s   | j r| j dS dS )zGet sample rate from metadata.r   Nr   rB   r>   r>   r?   r     s    zJingle.sample_ratec                 C   s   | j r| j dS dS )z%Get number of channels from metadata.r   Nr   rB   r>   r>   r?   r     s    zJingle.channelsc                 C   s*   | j r&tj| j jd  ddS dS )z%Get audio format from file extension.r   . N)ra   ry   rz   r|   r*   upperreplacerB   r>   r>   r?   audio_format  s     zJingle.audio_formatc                 C   s~   | j | j| jd}| jrz| jr.| j d|d< | jrD| j d|d< | jrz| jdkrXdn| jdkrfd	n
| j d
}||d< |S )z
        Get formatted metadata for display purposes.
        
        Returns:
            dict: Formatted metadata information
        )r]   r^   r   z kbpsr   z Hzr   r   ZMonor   ZStereoz	 channelsr   )r   r   r   r   r   r   r   )rC   Zdisplay_infoZchannel_textr>   r>   r?   get_metadata_display  s    (zJingle.get_metadata_display)Ar2   r3   r4   __doc__r5   ZJINGLE_TYPESZPLACEMENT_TYPESZPRIORITY_LEVELSr   
ForeignKeyr   CASCADEr)   	CharFieldr*   Zjingle_typer.   PositiveSmallIntegerFieldr/   	FileFieldr   ra   r   r\   PositiveIntegerFieldr]   PositiveBigIntegerFieldr^   	JSONFielddictr   	TextFieldaudio_fingerprintframes_fingerprintBooleanFieldr+   	DateFieldrF   rH   listrI   rS   DateTimeFieldrT   r@   rD   rR   rX   rZ   rW   rd   re   r   r   r   r   propertyr   r   r   r   r   r   r   __classcell__r>   r>   rh   r?   r	      sR  































	&( 2





r	   c                   @   s  e Zd ZdZdedfdedfdedfgZded	fd
edfdedfdedfgZeje	ej
dededdZejeej
dededdZejdededdZejededdZejededdZejd d!ed"ed#d$Zejd%eded&ed'd(Zejed)ed*dZejd+eded,ed-d(Zejeded.ed/d0ZG d1d2 d2Zd3d4 Zed5d6 Zed7d8 Z ed9d: Z!d;d< Z"dDd>d?Z#dEd@dAZ$dBdC Z%d=S )FJingleDetectiona  
    Log of detected jingles in live streams.
    
    Records when jingles are automatically detected in broadcast streams
    using audio/video fingerprinting. Used for compliance monitoring,
    ad break detection, and broadcast analysis.
    
    Attributes:
        channel: Foreign key to the channel where detection occurred
        jingle: Foreign key to the detected jingle
        detected_at: Timestamp when detection was recorded
        start_timestamp: When the jingle started playing in the stream
        end_timestamp: When the jingle ended playing in the stream
        confidence_score: Detection confidence from 0.0 to 1.0
        detection_method: Method used for detection
        stream_position: Position in stream where jingle was detected
        status: Validation status of the detection
        metadata: Additional detection metadata
    
    Related Models:
        - Channel: Many-to-one relationship
        - Jingle: Many-to-one relationship
    detectedZDetected	confirmedZ	Confirmedfalse_positivezFalse Positiver   r'   r   zI-Frame FingerprintcombinedzCombined Audio/Framer   zManual Detectionjingle_detectionsr   z%Channel where the jingle was detected)r   r   r   r   Z
detectionsr	   zThe jingle that was detectedTzDetected Atz-When the detection was recorded in the system)auto_now_addr   r   z
Start Timez-When the jingle started playing in the stream)r   r   zEnd Timez+When the jingle ended playing in the streamr   r   zConfidence Scorez*Detection confidence score from 0.0 to 1.0)
max_digitsdecimal_placesr   r   2   zDetection MethodzMethod used for detection)r    r"   r$   r   r   zStream PositionzAPosition in stream where jingle was detected (seconds from start)r!   Statusz#Validation status of this detectionMetadataz,Additional detection metadata and parametersr&   c                   @   sn   e Zd ZdZdgZedZedZej	ddgdej	ddgdej	d	d
gdej	ddgdej	dgdgZ
dS )zJingleDetection.Metar   z-detected_atzJingle DetectionzJingle Detectionsr)   detected_atr,   jinglestart_timestampend_timestampstatusconfidence_scoreN)r2   r3   r4   r7   r8   r5   r   r6   r   r9   r:   r>   r>   r>   r?   r@     s   r@   c                 C   s   | j j d| jj d| j S )Nz detected on z at )r   r*   r)   r   rB   r>   r>   r?   rD   )  s    zJingleDetection.__str__c                 C   s    | j r| jr| j| j   S dS )z
        Get detection duration in seconds.
        
        Returns:
            float: Duration of the detected jingle in seconds
        r   )r   r   total_secondsrB   r>   r>   r?   r]   ,  s    zJingleDetection.durationc                 C   s
   | j dkS )z&Check if detection has been confirmed.r   r   rB   r>   r>   r?   is_confirmed8  s    zJingleDetection.is_confirmedc                 C   s
   | j dkS )z/Check if detection is marked as false positive.r   r  rB   r>   r>   r?   is_false_positive=  s    z!JingleDetection.is_false_positivec                 C   sv   | j jdkrnzJtjj| jd| j| jtjdd ddgd	 }|rT|j| j
  W S W n tjk
rl   Y nX | jjS )aO  
        Infer ad break duration based on jingle placement.
        
        For jingles that mark the start of commercial breaks, attempts to
        find the corresponding end-of-break jingle to calculate the actual
        ad break duration.
        
        Returns:
            int: Estimated ad break duration in seconds
        r   r   
   )r   r   r   )r)   Zjingle__placement_typeZstart_timestamp__gtZstart_timestamp__ltZ
status__in)r   r.   r   r_   filterr)   r   r   	timedeltafirstr   r   rb   Zmax_ad_duration)rC   Zend_detectionr>   r>   r?   infer_ad_break_durationB  s    
z'JingleDetection.infer_ad_break_durationNc                 C   sH   d| _ |r4d| jkr"t|| jd< t  | jd< | jddgd dS )z
        Mark detection as confirmed.
        
        Args:
            confirmed_by: Optional user who confirmed the detection
        r   confirmed_byZconfirmed_atr   r   rU   Nr   r   r   r   rE   	isoformatrW   )rC   r	  r>   r>   r?   confirm_detection`  s    
z!JingleDetection.confirm_detectionc                 C   sH   d| _ |r4d| jkr"t|| jd< t  | jd< | jddgd dS )z
        Mark detection as false positive.
        
        Args:
            marked_by: Optional user who marked this as false positive
        r   	marked_byZmarked_false_positive_atr   r   rU   Nr
  )rC   r  r>   r>   r?   mark_false_positiven  s    
z#JingleDetection.mark_false_positivec              	   C   s.   | j j| jj| jt| j| j| j| j| j	dS )z
        Get a summary of this detection.
        
        Returns:
            dict: Summary information about the detection
        )Zjingle_namechannel_namer]   Z
confidencemethodr   r   stream_position)
r   r*   r)   r]   r   r   detection_methodr   r   r  rB   r>   r>   r?   get_detection_summary|  s    z%JingleDetection.get_detection_summary)N)N)&r2   r3   r4   r   r5   ZDETECTION_STATUSZDETECTION_METHODSr   r   r   r   r)   r	   r   r   r   r   r   DecimalFieldr   r   r  r   r  r   r   r   r   r@   rD   r   r]   r  r  r  r  r  r  r>   r>   r>   r?   r     s   






			




r   N)r   	django.dbr   django.utilsr   django.utils.translationr   r5   apps.common.modelsr   apps.channels.models.channelsr   Zapps.channels.utilsr   r   r	   r   r>   r>   r>   r?   <module>   s        "