§
    - Èj˜T  ã                   óF  — d Z ddl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
mZ ddlZ ee¦  «                             ¦   «         j        j        Zedz  Zedz  dz  Zedz  Zedz  Z ee¦  «        ej        vr$ej                             d ee¦  «        ¦  «         dd	lmZmZmZ ddlZdd
lmZm Z m!Z! ddl"Z#	 	 	 	 	 	 d&dede$de%dede%de%de&defd„Z'dede(fd„Z) G d„ d¦  «        Z* G d„ d¦  «        Z+ G d „ d!¦  «        Z, G d"„ d#¦  «        Z- G d$„ d%¦  «        Z.dS )'uP  
=============================================================================
THáº¬P NGÅ¨ NIÃŠN (THE FIFTEEN SPRINGS) - PRODUCTION PIPELINE TEST SUITE (M4)
=============================================================================
Module: tests/test_production_pipeline_m4.py
Milestone: M4 (Assembly, Exports & E2E Testing Strategy)
Standard: AGENTS.md, GEMINI.md, ORIGINAL_REQUEST.md Â§R3, PROJECT.md

Test Scope:
1. TestAudioPreservingConcat:
   - Concat across multiple shots preserves 100% AAC 48kHz stereo stream.
   - Video & audio track durations match, zero truncation or drift.
   - Mode A (boundary_smoothing 30ms micro-fade with curve=qsin) maintains zero timeline shrinkage.
2. TestEBUR128AudioMastering:
   - Two-pass loudnorm normalization achieves EBU R128 (-14.0 LUFS Â± 0.5, TP <= -1.0 dBTP).
   - master_scene_audio produces versioned masters with naming options (_master_audio_v* / _cinematic_master_v*).
   - resolve_audio_asset properly locates audio assets across standard directories.
3. TestSceneGatePreflightGating:
   - Pre-flight Scene Gate approval (score >= 0.8) proceeds to concat.
   - Scene Gate rejection (score < 0.8) halts master assembly and returns None.
4. TestMultiSceneFullEpisodeAssembly:
   - Stitches all 10 Scenes (ep01_scene01..ep01_scene10) into ep01_full_feature_master_v1.mp4.
   - Preserves sequential ordering and valid container structure.
   - resolve_scene_master respects priority (_master_audio_v* > _cinematic_master_v* > _master_v*).
   - assemble_episode_master dry-run and check modes operate accurately.
5. TestNegativeScenariosAndFaultTolerance:
   - Aborts on missing scene in 10-scene sequence.
   - Graceful fallback for silent video (-an) via aevalsrc=0 without crash.
   - Aborts cleanly when shots are missing for a scene.
=============================================================================
é    N)ÚPath)ÚpatchÚ	MagicMockÚ05_Production_PipelineÚ	04_AssetsÚvideosÚ
06_Exports)ÚAudioContinuityEngineÚ
get_ffmpegÚget_ffprobe)ÚVideoCriticVerdictÚSceneEvaluationÚShotEvaluationç      ð?é¸  Úblacké@  éð   TÚoutput_pathÚdurationÚfreqÚcolorÚwidthÚheightÚ	has_audioÚreturnc                 ó¦  — t          ¦   «         }| j                             dd¬¦  «         |ddddd|› d|› d	|› d
|› �g}|r0|                     dddd|› d|› �ddddddddddddddddg¦  «         n|                     g d¢¦  «         |                     dt	          |¦  «        t	          | ¦  «        g¦  «         t          j        |dd¬¦  «         | S )uI   Táº¡o video synthetic nháº¹ (320x240, h264+aac 48k stereo) trong < 200ms.T)ÚparentsÚexist_okú-yz-fÚlavfiú-izcolor=c=z:s=Úxz:d=zsine=frequency=z:sample_rate=48000:duration=ú-mapz0:vz1:aú-c:vÚlibx264ú-pix_fmtÚyuv420pú-c:aÚaacú-b:aÚ192kú-arÚ48000z-acÚ2)z-anr%   r&   r'   r(   z-t)Úcapture_outputÚcheck)r   ÚparentÚmkdirÚextendÚstrÚ
subprocessÚrun)	r   r   r   r   r   r   r   ÚffmpegÚcmds	            ú:C:\Projects\KieuStory\tests\test_production_pipeline_m4.pyÚcreate_synthetic_clipr;   >   s4  € õ ‰\Œ\€FØÔ×Ò T°DÐÑ9Ô9Ð9à�Øˆg�tÐO¨ÐOÐO°%ÐOÐO¸&ÐOÐOÀXÐOÐOð€Cð ð 
Ø�
Š
Ø�'˜4Ð!_°4Ð!_Ð!_ÐU]Ð!_Ð!_Ø�E˜6 5Ø�I˜z¨9Ø�E˜6 6¨5°'¸5À#ð	
ñ 	ô 	ð 	ð 	ð 	�
Š
ð 
ð 
ð 
ñ 	ô 	ð 	ð ‡J‚J�•c˜(‘m”m¥S¨Ñ%5Ô%5Ð6Ñ7Ô7Ð7Ý„N�3 t°4Ð8Ñ8Ô8Ð8ØÐó    Ú
media_pathc                 ó¤  — t          ¦   «         }|ddddddt          | ¦  «        g}t          j        |ddd¬¦  «        }t	          j        |j        ¦  «        }|                     d	g ¦  «        }|                     d
i ¦  «        }t          d„ |D ¦   «         d¦  «        }t          d„ |D ¦   «         d¦  «        }t          |                     dd¦  «        ¦  «        |du|r|                     d¦  «        nd|r7t          |                     d|                     dd¦  «        ¦  «        ¦  «        nd|du|r|                     d¦  «        nd|r#t          |                     dd¦  «        ¦  «        nd|r#t          |                     dd¦  «        ¦  «        nd|r7t          |                     d|                     dd¦  «        ¦  «        ¦  «        nddœ	S )u:   TrÃ­ch xuáº¥t chi tiáº¿t metadata audio/video qua ffprobe.z-vÚerrorz-show_entrieszJstream=codec_type,codec_name,sample_rate,channels,duration:format=durationz-ofÚjsonT)r0   Útextr1   ÚstreamsÚformatc              3   óL   K  — | ]}|                      d ¦  «        dk    ¯|V — Œ dS )Ú
codec_typeÚvideoN©Úget©Ú.0Úss     r:   ú	<genexpr>z#probe_media_file.<locals>.<genexpr>k   ó8   è è € ÐJÐJ˜1¨1¯5ª5°Ñ+>Ô+>À'Ò+IÐ+I�QÐ+IÐ+IÐ+IÐ+IÐJÐJr<   Nc              3   óL   K  — | ]}|                      d ¦  «        dk    ¯|V — Œ dS )rE   ÚaudioNrG   rI   s     r:   rL   z#probe_media_file.<locals>.<genexpr>l   rM   r<   r   g        Ú
codec_nameÚsample_rater   Úchannels)	Úformat_durationÚ	has_videoÚvideo_codecÚvideo_durationr   Úaudio_codecrQ   rR   Úaudio_duration)r   r5   r6   r7   r@   ÚloadsÚstdoutrH   ÚnextÚfloatÚint)	r=   Úffprober9   ÚresÚdatarB   ÚfmtÚv_streamÚa_streams	            r:   Úprobe_media_filerd   ^   sÏ  € å‰mŒm€Gà��wØÐeØˆv•s˜:‘”ð€Cõ
 Œ.˜¨T¸ÀDÐ
IÑ
IÔ
I€CÝŒ:�c”jÑ!Ô!€DØ�hŠh�y "Ñ%Ô%€GØ
�(Š(�8˜RÑ
 Ô
 €CåÐJÐJ ÐJÑJÔJÈDÑQÔQ€HÝÐJÐJ ÐJÑJÔJÈDÑQÔQ€Hõ ! §¢¨°SÑ!9Ô!9Ñ:Ô:Ø TÐ)Ø5=ÐG�x—|’| LÑ1Ô1Ð1À4ØW_Ðh�% §¢¨Z¸¿ºÀÈSÑ9QÔ9QÑ RÔ RÑSÔSÐSÐehØ TÐ)Ø5=ÐG�x—|’| LÑ1Ô1Ð1À4Ø>FÐM•s˜8Ÿ<š<¨°qÑ9Ô9Ñ:Ô:Ð:ÈAØ8@ÐG•C˜Ÿš Z°Ñ3Ô3Ñ4Ô4Ð4ÀaØW_Ðh�% §¢¨Z¸¿ºÀÈSÑ9QÔ9QÑ RÔ RÑSÔSÐSÐehð
ð 
ð 
r<   c                   ó$   — e Zd ZdZd„ Zd„ Zd„ ZdS )ÚTestAudioPreservingConcatuN   Kiá»ƒm thá»­ ghÃ©p ná»‘i filter_complex concat báº£o toÃ n 100% Ã¢m thanh AAC.c                 ó~  ‡— ˆfd„t          d¦  «        D ¦   «         }‰dz  }t          ¦   «         }g }g }t          |¦  «        D ]E\  }}|                     dt	          |¦  «        g¦  «         |                     d|› d|› d�¦  «         ŒFd                     |¦  «        › d	t          |¦  «        › d
�}	|dg|¢d‘|	‘d‘d‘d‘d‘d‘d‘d‘d‘d‘d‘d‘d‘t	          |¦  «        ‘}
t          j	        |
dd¬¦  «        }|j
        dk    sJ ‚|                     ¦   «         sJ ‚t          |¦  «        }|d         du sJ ‚|d         dk    sJ ‚|d         du sJ ‚|d         dk    sJ ‚|d          d!k    sJ ‚|d"         d#k    sJ ‚t          |d$         d%z
  ¦  «        d&k     sJ ‚t          |d'         |d(         z
  ¦  «        d)k     sJ ‚d*S )+uV   XÃ¡c nháº­n ghÃ©p ná»‘i N shot sinh ra master cÃ³ Ä‘áº§y Ä‘á»§ audio AAC stereo 48kHz.c           	      óN   •— g | ]!}t          ‰d |› d�z  dd|dz   z  ¬¦  «        ‘Œ"S )Úshot_ú.mp4ç      à?i�  é   ©r   r   ©r;   ©rJ   ÚiÚtmp_paths     €r:   ú
<listcomp>z_TestAudioPreservingConcat.test_01_filter_complex_concat_preserves_aac_audio.<locals>.<listcomp>„   sR   ø€ ð 
ð 
ð 
àõ " (¨_°Q¨_¨_¨_Ñ"<ÀsÐQTÐXYÐ\]ÑX]ÑQ^Ð_Ñ_Ô_ð
ð 
ð 
r<   é   zscene_master.mp4r"   ú[ú:v:0][ú:a:0]Ú ú	concat=n=ú:v=1:a=1[v][a]r    ú-filter_complexr$   ú[v]ú[a]r%   r&   r)   r*   r+   r,   r-   r.   T©r0   rA   r   rT   rU   Úh264r   rW   rQ   é€»  rR   é   rS   g      ø?ç333333Ã?rX   rV   çš™™™™™¹?N)Úranger   Ú	enumerater4   r5   ÚappendÚjoinÚlenr6   r7   Ú
returncodeÚexistsrd   Úabs)Úselfrq   ÚclipsÚ
out_masterr8   ÚinputsÚfilter_partsrp   ÚcÚconcat_filterr9   r_   Úmetas    `           r:   Ú1test_01_filter_complex_concat_preserves_aac_audiozKTestAudioPreservingConcat.test_01_filter_complex_concat_preserves_aac_audio‚   s´  ø€ ð
ð 
ð 
ð 
å˜1‘X”Xð
ñ 
ô 
ˆð Ð 2Ñ2ˆ
å‘”ˆØˆØˆÝ˜eÑ$Ô$ð 	7ð 	7‰DˆAˆqØ�MŠM˜4¥ Q¡¤˜.Ñ)Ô)Ð)Ø×ÒÐ 5 AÐ 5Ð 5¨QÐ 5Ð 5Ð 5Ñ6Ô6Ð6Ð6ØŸ7š7 <Ñ0Ô0ÐUÐU½3¸u¹:¼:ÐUÐUÐUˆð �Dð
Ø!ð
àð
à,ð
ð ð
ð ð
ð "ð
ð $)ð
ð ð	
ð ð	
ð  &ð	
ð (-ð	
ð /5ð	
ð 7=ð	
ð ?Dð	
ð FMð	
õ
 �
‰OŒOð
ˆõ Œn˜S°¸DÐAÑAÔAˆØŒ~ Ò"Ð"Ð"Ð"Ø× Ò Ñ"Ô"Ð"Ð"Ð"å 
Ñ+Ô+ˆØ�KÔ  DÐ(Ð(Ð(Ð(Ø�MÔ" fÒ,Ð,Ð,Ð,Ø�KÔ  DÐ(Ð(Ð(Ð(Ø�MÔ" eÒ+Ð+Ð+Ð+Ø�MÔ" eÒ+Ð+Ð+Ð+Ø�JÔ 1Ò$Ð$Ð$Ð$Ý�4Ð)Ô*¨SÑ0Ñ1Ô1°DÒ8Ð8Ð8Ð8Ý�4Ð(Ô)¨DÐ1AÔ,BÑBÑCÔCÀcÒIÐIÐIÐIÐIÐIr<   c                 ól  ‡— ˆfd„t          d¦  «        D ¦   «         }‰dz  }t          ¦   «         }|                     d„ |D ¦   «         t          |¦  «        ddd¬¦  «        }|du sJ ‚|                     ¦   «         sJ ‚t          |¦  «        }|d	         du sJ ‚|d
         dk    sJ ‚|d         dk    sJ ‚dS )uZ   XÃ¡c nháº­n AudioContinuityEngine.stitch_with_audio_crossfade xuáº¥t file cÃ³ AAC chuáº©n.c           	      óN   •— g | ]!}t          ‰d |› d�z  dd|dz  z   ¬¦  «        ‘Œ"S )Úshot_cross_rj   çš™™™™™é?iô  éd   rm   rn   ro   s     €r:   rr   zhTestAudioPreservingConcat.test_01_stitch_with_audio_crossfade_preserves_audio_stream.<locals>.<listcomp>©   sV   ø€ ð 
ð 
ð 
àõ " (Ð-B¸1Ð-BÐ-BÐ-BÑ"BÈSÐWZÐ]^ÐadÑ]dÑWdÐeÑeÔeð
ð 
ð 
r<   r€   zcrossfade_master.mp4c                 ó,   — g | ]}t          |¦  «        ‘ŒS © ©r5   ©rJ   r�   s     r:   rr   zhTestAudioPreservingConcat.test_01_stitch_with_audio_crossfade_preserves_audio_stream.<locals>.<listcomp>±   ó   € Ð#Ð#Ð#˜�S�‰VŒVÐ#Ð#Ð#r<   çš™™™™™É?TÚ
acrossfade©Úcrossfade_durÚnormalize_lufsÚmoder   rW   r*   rQ   r   N)rƒ   r
   Ústitch_with_audio_crossfader5   r‰   rd   ©r‹   rq   rŒ   r�   ÚengineÚokr’   s    `     r:   Ú:test_01_stitch_with_audio_crossfade_preserves_audio_streamzTTestAudioPreservingConcat.test_01_stitch_with_audio_crossfade_preserves_audio_stream§   s  ø€ ð
ð 
ð 
ð 
å˜1‘X”Xð
ñ 
ô 
ˆð Ð 6Ñ6ˆ
å&Ñ(Ô(ˆØ×/Ò/Ø#Ð#˜UÐ#Ñ#Ô#Ý�
‰OŒOØØØð 0ñ 
ô 
ˆð �TˆzˆzˆzˆzØ× Ò Ñ"Ô"Ð"Ð"Ð"å 
Ñ+Ô+ˆØ�KÔ  DÐ(Ð(Ð(Ð(Ø�MÔ" eÒ+Ð+Ð+Ð+Ø�MÔ" eÒ+Ð+Ð+Ð+Ð+Ð+r<   c                 ó”  ‡— ˆfd„t          d¦  «        D ¦   «         }‰dz  }t          ¦   «         }|                     d„ |D ¦   «         t          |¦  «        ddd¬¦  «        }|d	u sJ ‚|                     ¦   «         sJ ‚t          |¦  «        }t          |d
         dz
  ¦  «        dk     sJ ‚t          |d         dz
  ¦  «        dk     sJ ‚dS )uf   XÃ¡c nháº­n Mode A (boundary_smoothing) giá»¯ nguyÃªn 100% thá»�i lÆ°á»£ng tá»•ng, zero duration loss.c           	      óN   •— g | ]!}t          ‰d |› d�z  dd|dz  z   ¬¦  «        ‘Œ"S )Úshot_smooth_rj   g333333ã?i^  éP   rm   rn   ro   s     €r:   rr   zZTestAudioPreservingConcat.test_01_mode_a_boundary_smoothing_zero_drift.<locals>.<listcomp>Â   sV   ø€ ð 
ð 
ð 
àõ " (Ð-C¸AÐ-CÐ-CÐ-CÑ"CÈcÐX[Ð^_ÐbdÑ^dÑXdÐeÑeÔeð
ð 
ð 
r<   rs   zsmooth_master.mp4c                 ó,   — g | ]}t          |¦  «        ‘ŒS rš   r›   rœ   s     r:   rr   zZTestAudioPreservingConcat.test_01_mode_a_boundary_smoothing_zero_drift.<locals>.<listcomp>Ê   r�   r<   rž   FÚboundary_smoothingr    TrS   gÍÌÌÌÌÌü?r�   rX   N)rƒ   r
   r¤   r5   r‰   rd   rŠ   r¥   s    `     r:   Ú,test_01_mode_a_boundary_smoothing_zero_driftzFTestAudioPreservingConcat.test_01_mode_a_boundary_smoothing_zero_drift¿   s  ø€ ð
ð 
ð 
ð 
å˜1‘X”Xð
ñ 
ô 
ˆð Ð 3Ñ3ˆ
å&Ñ(Ô(ˆØ×/Ò/Ø#Ð#˜UÐ#Ñ#Ô#Ý�
‰OŒOØØ Ø%ð 0ñ 
ô 
ˆð �TˆzˆzˆzˆzØ× Ò Ñ"Ô"Ð"Ð"Ð"å 
Ñ+Ô+ˆå�4Ð)Ô*¨SÑ0Ñ1Ô1°DÒ8Ð8Ð8Ð8Ý�4Ð(Ô)¨CÑ/Ñ0Ô0°4Ò7Ð7Ð7Ð7Ð7Ð7r<   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r“   r¨   r¯   rš   r<   r:   rf   rf      sK   € € € € € ØXÐXð#Jð #Jð #JðJ,ð ,ð ,ð08ð 8ð 8ð 8ð 8r<   rf   c                   ó$   — e Zd ZdZd„ Zd„ Zd„ ZdS )ÚTestEBUR128AudioMasteringuI   Kiá»ƒm thá»­ chuáº©n hÃ³a Ã¢m lÆ°á»£ng EBU R128 (-14 LUFS, TP -1.0 dBTP).c                 ót  — t          |dz  dd¬¦  «        }|dz  }t          ¦   «         }|                     t          |¦  «        t          |¦  «        ddd¬	¦  «        }|du sJ ‚|                     ¦   «         sJ ‚|                     t          |¦  «        dd¬
¦  «        }|€J ‚|                     dd¦  «        rJ ‚t          |                     dd¦  «        ¦  «        }t          |                     dd¦  «        ¦  «        }t          |dz
  ¦  «        dk    sJ d|› d�¦   «         ‚|dk    sJ d|› d�¦   «         ‚dS )uK   XÃ¡c nháº­n Two-Pass Linear Normalization Ä‘áº¡t chuáº©n -14.0 LUFS Â± 0.5.zloud_input.mp4g      @r   rm   znormalized_output.mp4g      ,Àg      ð¿T)Útarget_lufsÚ	target_tpÚtwo_pass)r·   r¸   NÚ	is_silentFÚinput_ig     ÀXÀÚinput_tpg     ÀX@rk   u'   Integrated Loudness lá»‡ch quÃ¡ má»©c: z LUFSu   True Peak vÆ°á»£t ngÆ°á»¡ng: z dBTP)	r;   r
   Únormalize_loudnessr5   r‰   Úmeasure_loudnessrH   r\   rŠ   )	r‹   rq   Úin_clipÚout_clipr¦   r§   Úmeasr»   r¼   s	            r:   Ú3test_02_normalize_loudness_achieves_ebu_r128_targetzMTestEBUR128AudioMastering.test_02_normalize_loudness_achieves_ebu_r128_targetà   s^  € å'¨Ð3CÑ(CÈcÐX[Ð\Ñ\Ô\ˆØÐ5Ñ5ˆå&Ñ(Ô(ˆØ×&Ò&¥s¨7¡|¤|µS¸±]´]ÐPUÐaeÐptÐ&ÑuÔuˆØ�TˆzˆzˆzˆzØ�ŠÑ Ô Ð Ð Ð à×&Ò&¥s¨8¡}¤}À%ÐSWÐ&ÑXÔXˆØÐÐÐØ—8’8˜K¨Ñ/Ô/Ð/Ð/Ð/å˜Ÿš ¨EÑ2Ô2Ñ3Ô3ˆÝ˜Ÿš *¨dÑ3Ô3Ñ4Ô4ˆå�7˜eÑ$Ñ%Ô%¨Ò,Ð,Ð,Ð.fÐX_Ð.fÐ.fÐ.fÑ,Ô,Ð,Ø˜4ÒÐÐÐ!PÀÐ!PÐ!PÐ!PÑÔÐÐÐr<   c                 ó4  — d}|dz  }|dz  }|                      d¬¦  «         |                      d¬¦  «         ||› d�z  }t          |d¬¦  «         t          d	|¦  «        5  t          d
|¦  «        5  t          j        |d¬¦  «        }|du sJ ‚||› d�z  }|                     ¦   «         sJ ‚t          j        |d¬¦  «        }|du sJ ‚||› d�z  }	|	                     ¦   «         sJ ‚	 ddd¦  «         n# 1 swxY w Y   ddd¦  «         dS # 1 swxY w Y   dS )uN   XÃ¡c nháº­n master_scene_audio xuáº¥t Ä‘Ãºng file vÃ  Ã¡p dá»¥ng naming_style.Úep01_test_scener   ÚexportsT©r   ú_master_v1.mp4r   ©r   ú"production_orchestrator.VIDEOS_DIRú#production_orchestrator.EXPORTS_DIRÚmaster_audio)Únaming_styleú_master_audio_v1.mp4Úcinematic_masterú_cinematic_master_v1.mp4N)r3   r;   r   ÚpoÚmaster_scene_audior‰   )
r‹   rq   Ú
test_sceneÚmock_videos_dirÚmock_exports_dirÚmock_raw_masterÚsuccessÚexpected_outÚsuccess2Úexpected_cinematics
             r:   Ú1test_02_master_scene_audio_exports_versioned_filezKTestEBUR128AudioMastering.test_02_master_scene_audio_exports_versioned_fileô   só  € à&ˆ
Ø" XÑ-ˆØ# iÑ/ÐØ×Ò dÐÑ+Ô+Ð+Ø×Ò tÐÑ,Ô,Ð,à)¨zÐ,IÐ,IÐ,IÑIˆÝ˜o¸Ð<Ñ<Ô<Ð<åÐ7¸ÑIÔIð 	/ð 	/ÝÐ8Ð:JÑKÔKð	/ð 	/õ Ô+¨JÀ^ÐTÑTÔTˆGØ˜d�?�?�?�?Ø+°Ð.QÐ.QÐ.QÑQˆLØ×&Ò&Ñ(Ô(Ð(Ð(Ð(õ Ô,¨ZÐFXÐYÑYÔYˆHØ˜tÐ#Ð#Ð#Ð#Ø!1°zÐ4[Ð4[Ð4[Ñ![ÐØ%×,Ò,Ñ.Ô.Ð.Ð.Ð.Ð.ð	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øøøð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øøøð 	/ð 	/ð 	/ð 	/ð 	/ð 	/s7   Á"DÁ3A5C5Ã)DÃ5C9	Ã9DÃ<C9	Ã=DÄDÄDc                 ó˜  — |dz  }|                      d¦  «         t          j        t          |¦  «        ¦  «        }||                     ¦   «         k    sJ ‚t          j        d¦  «        �J ‚t          j        d¦  «        �J ‚t
          dz  dz  }|                     ¦   «         r%t          j        d¦  «        }|€J ‚|j        dk    sJ ‚dS dS )uU   XÃ¡c nháº­n resolve_audio_asset tÃ¬m tháº¥y file trÃªn cÃ¡c thÆ° má»¥c audio chuáº©n.zmy_test_bgm.m4aÚdummyNrw   rO   zscene01_bgm.m4a)Ú
write_textrÐ   Úresolve_audio_assetr5   ÚresolveÚ
ASSETS_DIRr‰   Úname)r‹   rq   Údummy_audioÚresolvedÚrepo_bgmr_   s         r:   Ú&test_02_resolve_audio_asset_resolutionz@TestEBUR128AudioMastering.test_02_resolve_audio_asset_resolution  sì   € àÐ!2Ñ2ˆØ×Ò˜wÑ'Ô'Ð'õ Ô)­#¨kÑ*:Ô*:Ñ;Ô;ˆØ˜;×.Ò.Ñ0Ô0Ò0Ð0Ð0Ð0õ Ô% dÑ+Ô+Ð3Ð3Ð3ÝÔ% bÑ)Ô)Ð1Ð1Ð1õ  Ñ'Ð*;Ñ;ˆØ�?Š?ÑÔð 	1ÝÔ(Ð):Ñ;Ô;ˆCØ�?�?�?Ø”8Ð0Ò0Ð0Ð0Ð0ð	1ð 	1ð 1Ð0r<   N)r°   r±   r²   r³   rÂ   rÚ   rå   rš   r<   r:   rµ   rµ   Ý   sJ   € € € € € ØSÐSðQð Qð Qð(/ð /ð /ð21ð 1ð 1ð 1ð 1r<   rµ   c                   ó   — e Zd ZdZd„ Zd„ ZdS )ÚTestSceneGatePreflightGatinguZ   Kiá»ƒm thá»­ cá»•ng kiá»ƒm duyá»‡t Táº§ng 2 (Scene Gate) trÆ°á»›c khi xuáº¥t Scene Master.c           	      ó  — t          dddt          dd¬¦  «        d¬¦  «        }t          d|¬	¦  «        5  t          j        d
ddg¦  «        }|j        du sJ ‚|j        dk    sJ ‚|j        dk    sJ ‚	 ddd¦  «         dS # 1 swxY w Y   dS )uR   Khi Scene Gate phÃª duyá»‡t (score >= 0.8), quy trÃ¬nh concat Ä‘Æ°á»£c thá»±c thi.gq=
×£pí?TÚAPPROVEgffffffî?©Újunction_smoothnessÚscorez"Junctions smooth, eyelines aligned©Úoverall_scoreÚapprovedÚsuggested_actionÚ
scene_evalÚcritique_notesú+production_orchestrator.evaluate_scene_gate©Úreturn_valueÚep01_scene01ú	shot1.mp4ú	shot2.mp4r—   N©r   r   r   rÐ   Úevaluate_scene_gaterï   rî   rð   )r‹   Úapproved_verdictÚverdicts      r:   Ú)test_03_scene_gate_approved_allows_concatzFTestSceneGatePreflightGating.test_03_scene_gate_approved_allows_concat)  s  € å-ØØØ&Ý&¸4ÀtÐLÑLÔLØ?ð
ñ 
ô 
Ðõ Ð@ÐO_Ð`Ñ`Ô`ð 	9ð 	9ÝÔ,¨^¸kÈ;Ð=WÑXÔXˆGØÔ# tÐ+Ð+Ð+Ð+ØÔ(¨CÒ/Ð/Ð/Ð/ØÔ+¨yÒ8Ð8Ð8Ð8Ð8ð		9ð 	9ð 	9ñ 	9ô 	9ð 	9ð 	9ð 	9ð 	9ð 	9ð 	9ð 	9øøøð 	9ð 	9ð 	9ð 	9ð 	9ð 	9ó   µ=B Â BÂBc           	      ó  — t          dddt          dd¬¦  «        d¬¦  «        }t          d|¬	¦  «        5  t          j        d
ddg¦  «        }|j        du sJ ‚|j        dk     sJ ‚|j        dk    sJ ‚	 ddd¦  «         dS # 1 swxY w Y   dS )ud   Khi Scene Gate tá»« chá»‘i (score < 0.8), quy trÃ¬nh concat bá»‹ cháº·n Ä‘á»©ng vÃ  tráº£ vá»� None.g×£p=
×ã?FÚRETAKE_SHOTrk   rê   z0Severe 180-degree axis jump at shot 2 transitionrí   ró   rô   rö   r÷   rø   r—   Nrù   )r‹   Úrejected_verdictrü   s      r:   Ú(test_03_scene_gate_rejected_halts_concatzETestSceneGatePreflightGating.test_03_scene_gate_rejected_halts_concat8  s  € å-ØØØ*Ý&¸3ÀdÐKÑKÔKØMð
ñ 
ô 
Ðõ Ð@ÐO_Ð`Ñ`Ô`ð 	=ð 	=ÝÔ,¨^¸kÈ;Ð=WÑXÔXˆGØÔ# uÐ,Ð,Ð,Ð,ØÔ(¨3Ò.Ð.Ð.Ð.ØÔ+¨}Ò<Ð<Ð<Ð<Ð<ð		=ð 	=ð 	=ñ 	=ô 	=ð 	=ð 	=ð 	=ð 	=ð 	=ð 	=ð 	=øøøð 	=ð 	=ð 	=ð 	=ð 	=ð 	=rþ   N)r°   r±   r²   r³   rý   r  rš   r<   r:   rç   rç   &  s8   € € € € € ØdÐdð9ð 9ð 9ð=ð =ð =ð =ð =r<   rç   c                   ó*   — e Zd ZdZd„ Zd„ Zd„ Zd„ ZdS )Ú!TestMultiSceneFullEpisodeAssemblyuR   Kiá»ƒm thá»­ ghÃ©p ná»‘i hoÃ n chá»‰nh 10 PhÃ¢n cáº£nh thÃ nh Ep01 Feature Master.c                 ó¢  — g }t          dd¦  «        D ]I}d|d›�}||› d�z  }t          |dd|dz  z   |d	z  d
k    rdnd¬¦  «         |                     |¦  «         ŒJ|dz  }t          ¦   «         }g }g }	t	          |¦  «        D ]E\  }}
|                     dt          |
¦  «        g¦  «         |	                     d|› d|› d�¦  «         ŒFd                     |	¦  «        › dt          |¦  «        › d�}|dg|¢d‘|‘d‘d‘d‘d‘d‘d‘d‘d‘d‘d ‘d!‘d"‘d#‘d$‘t          |¦  «        ‘}t          j
        |d%d%¬&¦  «        }|j        d
k    sJ ‚|                     ¦   «         sJ ‚t          |¦  «        }|d'         d%u sJ ‚|d(         d%u sJ ‚|d)         d k    sJ ‚|d*         d+k    sJ ‚|d,         d	k    sJ ‚t          |d-         d.z
  ¦  «        d/k     sJ ‚d0S )1uR   Táº¡o 10 synthetic scene masters vÃ  ghÃ©p thÃ nh ep01_full_feature_master_v1.mp4.rl   é   Ú
ep01_sceneÚ02drÍ   rž   i,  é(   r€   r   Úbluer   )r   r   r   zep01_full_feature_master_v1.mp4r"   rt   ru   rv   rw   rx   ry   r    rz   r$   r{   r|   r%   r&   z-crfÚ18r)   r*   r+   r,   r-   r.   Tr}   rT   r   rW   rQ   r   rR   rS   g       @g      Ð?N)rƒ   r;   r…   r   r„   r4   r5   r†   r‡   r6   r7   rˆ   r‰   rd   rŠ   )r‹   rq   Úscene_filesrp   Úsc_idÚsc_pathÚout_feature_masterr8   rŽ   r�   Úscr‘   r9   r_   r’   s                  r:   Ú)test_04_assemble_10_scenes_feature_masterzKTestMultiSceneFullEpisodeAssembly.test_04_assemble_10_scenes_feature_masterO  së  € àˆÝ�q˜"‘”ð 	(ð 	(ˆAØ( Ð(Ð(Ð(ˆEØ EÐ!?Ð!?Ð!?Ñ?ˆGÝ! '°C¸cÀAÈÁF¹lÐ\]Ð`aÑ\aÐefÒ\fÐ\fÐRXÐRXÐlsÐtÑtÔtÐtØ×Ò˜wÑ'Ô'Ð'Ð'à%Ð(IÑIÐõ ‘”ˆØˆØˆÝ˜{Ñ+Ô+ð 	7ð 	7‰EˆAˆrØ�MŠM˜4¥ R¡¤˜/Ñ*Ô*Ð*Ø×ÒÐ 5 AÐ 5Ð 5¨QÐ 5Ð 5Ð 5Ñ6Ô6Ð6Ð6ØŸ7š7 <Ñ0Ô0Ð[Ð[½3¸{Ñ;KÔ;KÐ[Ð[Ð[ˆð �Dð
Ø!ð
àð
à,ð
ð ð
ð ð
ð "ð
ð $)ð
ð ð	
ð ð	
ð  &ð	
ð (,ð	
ð
 ð
ð
 ð
ð
 "ð
ð
 $*ð
ð
 ,1ð
ð
 3:ð
õ Ð"Ñ#Ô#ð
ˆõ Œn˜S°¸DÐAÑAÔAˆØŒ~ Ò"Ð"Ð"Ð"Ø!×(Ò(Ñ*Ô*Ð*Ð*Ð*åÐ 2Ñ3Ô3ˆØ�KÔ  DÐ(Ð(Ð(Ð(Ø�KÔ  DÐ(Ð(Ð(Ð(Ø�MÔ" eÒ+Ð+Ð+Ð+Ø�MÔ" eÒ+Ð+Ð+Ð+Ø�JÔ 1Ò$Ð$Ð$Ð$å�4Ð)Ô*¨SÑ0Ñ1Ô1°DÒ8Ð8Ð8Ð8Ð8Ð8r<   c                 óö   — d„ t          dd¦  «        D ¦   «         }t          |¦  «        dk    sJ ‚|D ]F}|› d�}|› d�}|                     d¦  «        sJ ‚|                     d¦  «        sJ ‚d|v sJ ‚d	|v sJ ‚ŒGd
S )uP   Kiá»ƒm tra quy chuáº©n Ä‘áº·t tÃªn vÃ  Ä‘Æ°á»�ng dáº«n cho 10 scenes cá»§a Ep01.c                 ó   — g | ]}d |d›�‘Œ	S )r  r  rš   )rJ   rp   s     r:   rr   zjTestMultiSceneFullEpisodeAssembly.test_04_export_naming_and_path_hierarchy_conformance.<locals>.<listcomp>z  s$   € ÐDÐDÐD°!Ð- aÐ-Ð-Ð-ÐDÐDÐDr<   rl   r  é
   rÇ   rÍ   r  Ú
_master_v1Ú_master_audio_v1N)rƒ   r‡   Ú
startswith)r‹   Útarget_scenesr  Úraw_master_nameÚaudio_master_names        r:   Ú4test_04_export_naming_and_path_hierarchy_conformancezVTestMultiSceneFullEpisodeAssembly.test_04_export_naming_and_path_hierarchy_conformancex  sÆ   € àDÐDµu¸QÀ±|´|ÐDÑDÔDˆÝ�=Ñ!Ô! RÒ'Ð'Ð'Ð'àð 	;ð 	;ˆBØ!#Ð3Ð3Ð3ˆOØ#%Ð ;Ð ;Ð ;ÐØ"×-Ò-¨lÑ;Ô;Ð;Ð;Ð;Ø$×/Ò/°Ñ=Ô=Ð=Ð=Ð=Ø ?Ð2Ð2Ð2Ð2Ø%Ð):Ð:Ð:Ð:Ð:Ð:ð	;ð 	;r<   c                 ó¨  — |dz  }|dz  }|                      d¬¦  «         |                      d¬¦  «         d}||› d�z  }||› d�z  }||› d�z  }t          |d	¬
¦  «         t          |d	¬
¦  «         t          |d	¬
¦  «         t          d|¦  «        5  t          d|¦  «        5  t          j        |¦  «        }||k    sJ ‚|                     ¦   «          t          j        |¦  «        }	|	|k    sJ ‚|                     ¦   «          t          j        |¦  «        }
|
|k    sJ ‚	 ddd¦  «         n# 1 swxY w Y   ddd¦  «         dS # 1 swxY w Y   dS )ua   XÃ¡c nháº­n resolve_scene_master Æ°u tiÃªn: _master_audio_v* > _cinematic_master_v* > _master_v*.rÅ   r   TrÆ   Úep01_scene02rÇ   rÏ   rÍ   rž   rÈ   rÊ   rÉ   N)r3   r;   r   rÐ   Úresolve_scene_masterÚunlink)r‹   rq   Úmock_exportsÚmock_videosr  Úf_regÚf_cinÚf_audr_   Úres2Úres3s              r:   Ú+test_04_resolve_scene_master_prioritizationzMTestMultiSceneFullEpisodeAssembly.test_04_resolve_scene_master_prioritization…  s  € à )Ñ+ˆØ Ñ)ˆØ×Ò 4ÐÑ(Ô(Ð(Ø×Ò $ÐÑ'Ô'Ð'àˆØ Ð3Ð3Ð3Ñ3ˆØ "Ð>Ð>Ð>Ñ>ˆØ "Ð:Ð:Ð:Ñ:ˆå˜e¨cÐ2Ñ2Ô2Ð2Ý˜e¨cÐ2Ñ2Ô2Ð2Ý˜e¨cÐ2Ñ2Ô2Ð2åÐ8¸,ÑGÔGð 	!ð 	!ÝÐ7¸ÑEÔEð	!ð 	!õ Ô)¨"Ñ-Ô-ˆCØ˜%’<�<�<�<ð �LŠL‰NŒNˆNÝÔ*¨2Ñ.Ô.ˆDØ˜5’=�=�=�=ð �LŠL‰NŒNˆNÝÔ*¨2Ñ.Ô.ˆDØ˜5’=�=�=�=�=ð	!ð 	!ð 	!ñ 	!ô 	!ð 	!ð 	!ð 	!ð 	!ð 	!ð 	!øøøð 	!ð 	!ð 	!ð 	!ð 	!ð 	!ð 	!ñ 	!ô 	!ð 	!ð 	!ð 	!ð 	!ð 	!ð 	!ð 	!øøøð 	!ð 	!ð 	!ð 	!ð 	!ð 	!s7   ÂEÂ%A=D/Ä#EÄ/D3	Ä3EÄ6D3	Ä7EÅEÅEc                 ó  — |dz  }|                      d¬¦  «         t          d|¦  «        5  t          j        dd¬¦  «        }|€J ‚d|j        v sJ ‚t          j        dd¬	¦  «        }|�J ‚	 ddd¦  «         dS # 1 swxY w Y   dS )
uF   XÃ¡c nháº­n assemble_episode_master há»— trá»£ dry_run vÃ  check_only.rÅ   TrÆ   rÊ   Úep01)Ú
episode_idÚdry_runNÚep01_full_feature_master)r*  Ú
check_only)r3   r   rÐ   Úassemble_episode_masterrá   )r‹   rq   r   r_   Ú	res_checks        r:   Ú1test_04_assemble_episode_master_dry_run_and_checkzSTestMultiSceneFullEpisodeAssembly.test_04_assemble_episode_master_dry_run_and_check¥  sð   € à )Ñ+ˆØ×Ò 4ÐÑ(Ô(Ð(åÐ8¸,ÑGÔGð 	%ð 	%åÔ,¸ÈÐMÑMÔMˆCØ�?�?�?Ø-°´Ð9Ð9Ð9Ð9õ Ô2¸fÐQUÐVÑVÔVˆIØÐ$Ð$Ð$Ð$ð	%ð 	%ð 	%ñ 	%ô 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%øøøð 	%ð 	%ð 	%ð 	%ð 	%ð 	%s   ¬A A:Á:A>ÂA>N)r°   r±   r²   r³   r  r  r'  r0  rš   r<   r:   r  r  L  sX   € € € € € Ø\Ð\ð'9ð '9ð '9ðR;ð ;ð ;ð!ð !ð !ð@%ð %ð %ð %ð %r<   r  c                   ó$   — e Zd ZdZd„ Zd„ Zd„ ZdS )Ú&TestNegativeScenariosAndFaultToleranceu\   Kiá»ƒm thá»­ cÃ¡c tÃ¬nh huá»‘ng ngoáº¡i lá»‡, thiáº¿u file, file cÃ¢m vÃ  tá»± phá»¥c há»“i.c                 ó@  — |dz  }|                      d¬¦  «         t          dd¦  «        D ]&}|dk    rŒ	d|d›�}t          ||› d	�z  d
¬¦  «         Œ't          d|¦  «        5  t	          j        d¬¦  «        }|�
J d¦   «         ‚	 ddd¦  «         dS # 1 swxY w Y   dS )uh   Khi thiáº¿u 1 cáº£nh trong chuá»—i 10 cáº£nh, assembler tá»« chá»‘i xuáº¥t xÆ°á»Ÿng vÃ  tráº£ vá»� None.rÅ   TrÆ   rl   r  é   r  r  rÍ   rž   rÈ   rÊ   r)  )r*  Nu4   Pháº£i abort vÃ  tráº£ vá»� None khi thiáº¿u cáº£nh!)r3   rƒ   r;   r   rÐ   r.  )r‹   rq   r   rp   r  r_   s         r:   Ú%test_05_missing_scene_aborts_assemblyzLTestNegativeScenariosAndFaultTolerance.test_05_missing_scene_aborts_assembly¼  s-  € à )Ñ+ˆØ×Ò 4ÐÑ(Ô(Ð(õ �q˜"‘”ð 	\ð 	\ˆAØ�AŠvˆvØØ%˜aÐ%Ð%Ð%ˆBÝ! ,°BÐ1LÐ1LÐ1LÑ"LÐWZÐ[Ñ[Ô[Ð[Ð[åÐ8¸,ÑGÔGð 	Wð 	WÝÔ,¸Ð?Ñ?Ô?ˆCØ�;�;Ð V‘;”;�;�;ð	Wð 	Wð 	Wñ 	Wô 	Wð 	Wð 	Wð 	Wð 	Wð 	Wð 	Wð 	Wøøøð 	Wð 	Wð 	Wð 	Wð 	Wð 	Ws   Á#"BÂBÂBc                 ó¬  — t          |dz  ddd¬¦  «        }t          |dz  dd¬¦  «        }|d	z  }t          ¦   «         }|                     t          |¦  «        t          |¦  «        gt          |¦  «        d
d¬¦  «        }|du sJ ‚|                     ¦   «         sJ ‚t          |¦  «        }|d         du sJ ‚|d         dk    sJ ‚|d         dk    sJ ‚dS )uh   Shot bá»‹ cÃ¢m (-an) tá»± Ä‘á»™ng Ä‘Æ°á»£c chÃ¨n silence fallback (aevalsrc=0) khÃ´ng lÃ m crash FFmpeg.zwith_audio.mp4rk   r   T)r   r   r   z
silent.mp4F)r   r   zfallback_master.mp4r‚   r®   )r¡   r£   r   rW   r*   rQ   r   N)r;   r
   r¤   r5   r‰   rd   )r‹   rq   Úclip_with_audioÚclip_silentr�   r¦   r§   r’   s           r:   Ú%test_05_missing_audio_stream_fallbackzLTestNegativeScenariosAndFaultTolerance.test_05_missing_audio_stream_fallbackÌ  s  € å/°Ð;KÑ0KÐVYÐ`cÐosÐtÑtÔtˆÝ+¨H°|Ñ,CÈcÐ]bÐcÑcÔcˆØÐ 5Ñ5ˆ
å&Ñ(Ô(ˆØ×/Ò/Ý�Ñ!Ô!¥3 {Ñ#3Ô#3Ð4Ý�
‰OŒOØØ%ð	 0ñ 
ô 
ˆð �TˆzˆzˆzˆzØ× Ò Ñ"Ô"Ð"Ð"Ð"å 
Ñ+Ô+ˆØ�KÔ  DÐ(Ð(Ð(Ð(Ø�MÔ" eÒ+Ð+Ð+Ð+Ø�MÔ" eÒ+Ð+Ð+Ð+Ð+Ð+r<   c                 óä   — t          ddi fg¬¦  «        5  t          dd¬¦  «        5  t          j        d¦  «        }|�J ‚	 ddd¦  «         n# 1 swxY w Y   ddd¦  «         dS # 1 swxY w Y   dS )u\   Khi thiáº¿u video cho shot trong scene, concat_scene_shots dá»«ng láº¡i vÃ  tráº£ vá»� None.z+production_orchestrator.get_shots_for_sceneÚep01_scene99_shot01rô   z+production_orchestrator.find_rendered_videoNÚep01_scene99)r   rÐ   Úconcat_scene_shots)r‹   rq   Úresults      r:   Ú.test_05_concat_scene_shots_missing_shot_abortszUTestNegativeScenariosAndFaultTolerance.test_05_concat_scene_shots_missing_shot_abortsá  s  € åÐ@ÐQfÐhjÐPkÐOlÐmÑmÔmð 	"ð 	"ÝÐ@ÈtÐTÑTÔTð	"ð 	"åÔ*¨>Ñ:Ô:ˆFØ�>�>�>�>ð	"ð 	"ð 	"ñ 	"ô 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"øøøð 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"ñ 	"ô 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"ð 	"øøøð 	"ð 	"ð 	"ð 	"ð 	"ð 	"s4   •A%§AÁA%ÁA	ÁA%ÁA	ÁA%Á%A)Á,A)N)r°   r±   r²   r³   r5  r9  r?  rš   r<   r:   r2  r2  ¹  sJ   € € € € € ØfÐfðWð Wð Wð ,ð ,ð ,ð*"ð "ð "ð "ð "r<   r2  )r   r   r   r   r   T)/r³   ÚosÚsysÚrer@   Úshutilr6   Úpathlibr   Úunittest.mockr   r   ÚpytestÚ__file__rß   r2   ÚPROJECT_ROOTÚPIPELINE_DIRÚ
VIDEOS_DIRÚEXPORTS_DIRrà   r5   ÚpathÚinsertÚaudio_continuity_enginer
   r   r   Úproduction_orchestratorrÐ   Úantigravity_critic_gater   r   r   Úassemble_ep01_featureÚ	assemblerr\   r]   Úboolr;   Údictrd   rf   rµ   rç   r  r2  rš   r<   r:   ú<module>rU     sá  ððð ð@ 
€	€	€	Ø 
€
€
€
Ø 	€	€	€	Ø €€€Ø €€€Ø Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø *Ð *Ð *Ð *Ð *Ð *Ð *Ð *Ø €€€àˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5€ØÐ6Ñ6€Ø˜KÑ'¨(Ñ2€
Ø˜\Ñ)€Ø˜KÑ'€
à€3€|ÑÔ˜CœHÐ$Ð$Ø„H‡O‚O�A�s�s˜<Ñ(Ô(Ñ)Ô)Ð)à RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RÐ RØ $Ð $Ð $Ð $Ø WÐ WÐ WÐ WÐ WÐ WÐ WÐ WÐ WÐ WØ )Ð )Ð )Ð )ð ØØØØØðð Øðàðð ðð ð	ð
 ðð ðð ðð 
ðð ð ð ð@ ð ¨$ð ð ð ð ðBW8ð W8ð W8ð W8ð W8ñ W8ô W8ð W8ð|B1ð B1ð B1ð B1ð B1ñ B1ô B1ð B1ðR=ð =ð =ð =ð =ñ =ô =ð =ðLf%ð f%ð f%ð f%ð f%ñ f%ô f%ð f%ðZ-"ð -"ð -"ð -"ð -"ñ -"ô -"ð -"ð -"ð -"r<   