
    4Aj,                        d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlmZmZmZmZmZmZmZmZmZmZ  G d de          ZdS )	a  Analyze audio energy profile to find optimal playback offset.

Uses ffmpeg's ebur128 loudness meter to measure momentary loudness
at 100ms intervals, then identifies where the music "gets interesting"
(crosses a configurable energy threshold). Returns a recommended offset
in seconds plus the full energy profile.

Key use cases:
  - Skip quiet intros in ambient/cinematic music tracks
  - Find the peak energy section for a 30-second video from a 3-minute track
  - Determine if music needs looping (total duration vs video duration)
    )annotationsN)Path)Any)
BaseToolDeterminismExecutionModeResourceProfileRetryPolicy
ToolResultToolRuntimeToolStability
ToolStatusToolTierc                     e Zd ZdZdZej        ZdZdZ	e
j        Zej        Zej        Zej        ZdgZdZg dZg dZd	d
gddddddddddddZ eddddd          Z edg           Zd
gZg Z d%dZ!d&d!Z"d'd#Z#d$S )(AudioEnergyaudio_energyz0.1.0analysisffmpegzbinary:ffmpegznInstall ffmpeg:
  Windows: winget install ffmpeg
  macOS: brew install ffmpeg
  Linux: sudo apt install ffmpeg)find_music_offsetenergy_profilebest_windowloop_recommendation)z@finding where ambient music gets interesting (skip quiet intros)z5choosing the best offset for a music track in a videoz=determining if a music track needs looping for a longer videoobject
input_pathstringz(Path to audio file (mp3, wav, ogg, etc.))typedescriptionnumberzkDuration of the video this music will accompany. Used to recommend looping and find the best offset window.zMomentary loudness threshold in LUFS to consider music 'active' (default: -40). Higher = stricter. Typical: -50 for very quiet, -30 for energetic.)r   r   default)r   video_duration_secondsenergy_threshold_lufs)r   required
properties      r   F)	cpu_coresram_mbvram_mbdisk_mbnetwork_required)max_retriesretryable_errorsreturnr   c                Z    t          j        d          rt          j        S t          j        S )Nr   )shutilwhichr   	AVAILABLEUNAVAILABLE)selfs    J/app/working/workspaces/default/OpenMontage/tools/analysis/audio_energy.py
get_statuszAudioEnergy.get_statusc   s&    <!! 	(''%%    inputsdict[str, Any]floatc                    dS )N         )r4   r8   s     r5   estimate_costzAudioEnergy.estimate_costh   s    sr7   r   c                8
  + t          |d                   }|                                st          dd|           S t          j        d          }|st          dd          S |                    dd          }|                    d	          }t          j                    }t          j        d
          }|st          dd          S 	 t          j        |dddddt          |          gddd          }t          j        |j                  }	t          |	d         d                   }
n+# t          $ r}t          dd|           cY d }~S d }~ww xY w	 t          j        |dt          |          dddddgddd          }|j        }n&# t          j        $ r t          dd          cY S w xY wt#          j        d          }g }|                    d           D ]t}|                    |          }|r[t          |                    d!                    }t          |                    d"                    }|                    ||f           u|st          dd#          S t/          |d$         d%                   d!z   }g }t1          |          D ]_++fd&|D             }|r t3          |          t5          |          z  }nd'}|                    +t7          |d!          ||k    d(           `d)}|D ]!}|d*         rt          |d+                   } n"d, |D             }|r0t9          |d- .          }t          |d+                   }|d/         }nd)}d'}|}d0| d1| d2}|r||
k     rt/          |          } d3 |D             }!t5          |!          | k    rd4}"d%}#t1          t5          |!          | z
  d!z             D ]8}$|!|$|$| z            }%t3          |%          t5          |%          z  }&|&|"k    r|&}"|$}#9t          |#          }d5|  d6|# d7t7          |"d!           d2}d}'d }(|rj|
|z
  })|)|k     r_d}'t7          |)d!          t7          |d!          t7          ||)z
  d!          d8| d9t7          |)d!           d:t7          |d!           d;| d<	d=}(t          |          t7          |
d!          |t5          |          t3          d> |D                       |||d?|||'|(|d@}*t          d|*t7          t          j                    |z
  d"          A          S )BNr   FzFile not found: )successerrorr   zffmpeg not found on PATHr"   r   r!   ffprobezffprobe not found on PATHz-vquietz-print_formatjsonz-show_formatT
   )capture_outputtexttimeoutformatdurationzFailed to probe duration: z-iz-afebur128z-fnull-x   z!ebur128 analysis timed out (120s)z"t:\s*([\d.]+)\s+.*?M:\s*(-?[\d.]+)
r%      u9   Failed to parse ebur128 output — no loudness data foundr   c                L    g | ] \  }}|cxk    r	d z   k     n n|dk    |!S )r%   r=   ).0tmsecs      r5   
<listcomp>z'AudioEnergy.execute.<locals>.<listcomp>   sS       a!%%%%cAg%%%%%!d(( *2((r7   g      ^)time_secondsloudness_lufsactiver<   r[   rY   c                *    g | ]}|d          dk    |S )rZ   rS   r=   rT   ss     r5   rX   z'AudioEnergy.execute.<locals>.<listcomp>   s'    RRR/8JT8Q8Q18Q8Q8Qr7   c                    | d         S )NrZ   r=   )r^   s    r5   <lambda>z%AudioEnergy.execute.<locals>.<lambda>   s    !O:L r7   )keyrZ   zFirst active music at zs (threshold: z LUFS)c                :    g | ]}|d          dk    r|d          ndS )rZ   rS   ir=   r]   s     r5   rX   z'AudioEnergy.execute.<locals>.<listcomp>   sA        '(&84&?&?/""S  r7   g     8zBest zs window starts at zs (avg loudness: zMusic from offset zs provides only zs but video is z#s. Set loop=true and offsetSeconds=z in audio config.)music_available_from_offsetvideo_durationshortfall_secondsrecommendationc              3  *   K   | ]}|d          
dV  dS )r[   r%   Nr=   r]   s     r5   	<genexpr>z&AudioEnergy.execute.<locals>.<genexpr>  s+      %O%OA1X;%Oa%O%O%O%O%O%Or7   )threshold_lufstotal_secondsactive_secondsquiet_intro_secondspeak_loudness_at_secondspeak_loudness_lufs)fileaudio_duration_secondsr   recommended_offset_secondsoffset_reason
needs_loop	loop_infor   )r@   dataduration_seconds)r   existsr   r0   r1   gettime
subprocessrunstrrD   loadsstdoutr:   	ExceptionstderrTimeoutExpiredrecompilesplitsearchgroupappendintrangesumlenroundmax),r4   r8   r   r   ri   rd   startrB   probe_result
probe_dataaudio_durationeresultr   pattern
raw_pointslinematchrU   m_lufsmax_secr   points_in_secavg_lufsfirst_active_secsegactive_segmentspeak_segpeak_sec	peak_lufsrecommended_offsetrr   window_sizeloudness_valuesbest_avg
best_startiwindowavgrs   rt   available_from_offsetresult_datarW   s,                                              @r5   executezAudioEnergy.executek   s   &.//
  "" 	Te3Rj3R3RSSSSh'' 	Oe3MNNNN$;SAA$<==	
 ,y)) 	Pe3NOOOO	U%>T7OV"C
OO  $$  L L$788J":h#7
#CDDNN 	U 	U 	Ue3SPQ3S3STTTTTTTTT	U	X^D#j//9&#
  $$  F ]FF( 	X 	X 	Xe3VWWWWWW	X *BCC02
LL&& 	/ 	/DNN4((E /%++a..))u{{1~~..!!1f+... 	Q    jnQ'((1,/1>> 	 	C   (  M
  "}--M0B0BB!!! #!&x!3!3"^3# #     ! 	 	C8} #(^)<#=#= 
 SRnRRR 	?0L0LMMMHXn566H 1IIHI
 .2%5 2 2)2 2 2 	
  	n~==n--K '  O
 ?##{22!
s?33kAAEFF ' 'A,Q[-@AFf++F3CX~~#&%&
%*:%6%6"AK A AJ A A&+Ha&8&8A A A  
	 	$25G$G!$~55!
389NPQ3R3R&+NA&>&>).&)>>* *O-? O O !6::O O 33O O *<O O O 	$ 
OO&+NA&>&>"0!$^!4!4"%%O%O%O%O%O"O"O'7,4&/  +=*$",
 
$ "49;;#6::
 
 
 	
s1   A D- -
E7E
EE4F  F10F1N)r.   r   )r8   r9   r.   r:   )r8   r9   r.   r   )$__name__
__module____qualname__nameversionr   COREtier
capabilityproviderr   
PRODUCTION	stabilityr   SYNCexecution_moder   DETERMINISTICdeterminismr   LOCALruntimedependenciesinstall_instructionscapabilitiesbest_forinput_schemar	   resource_profiler
   retry_policyidempotency_key_fieldsside_effectsr6   r>   r   r=   r7   r5   r   r   &   sm       DG=DJH(I"'N+KG#$L	+   L  H !N !I 
 ! M' ' ! B & &
 
 L. 'CA   ;1rBBBL*^L& & & &
   D
 D
 D
 D
 D
 D
r7   r   )__doc__
__future__r   rD   r   r0   rz   ry   pathlibr   typingr   tools.base_toolr   r   r   r	   r
   r   r   r   r   r   r   r=   r7   r5   <module>r      s-    # " " " " "  				                                         I
 I
 I
 I
 I
( I
 I
 I
 I
 I
r7   