
    i-                    N   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mZ ddl	m
Z
 ddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd	Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd
Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZy)u   ffmpeg 기반 MP4 렌더링 모듈.

IDS Phase 5 — 모션 카드뉴스 (HTML→MP4)
- 5가지 모션 효과 지원 (fade, slide, zoom, dissolve, sequence)
- libx264 / yuv420p — SNS 호환 출력
- BGM 믹싱 지원
    )annotationsN)Path)Optionalc                    t         j                  j                  dd      } | r$t        |       j	                         rt        |       S d}t        |      j	                         r|S t        j                  d      }|r|S t        d      )u   ffmpeg 실행 파일 경로를 반환합니다.

    우선순위: 환경변수 FFMPEG_BIN → /home/jay/.local/bin/ffmpeg → PATH lookup
    
FFMPEG_BIN z/home/jay/.local/bin/ffmpegffmpegup   ffmpeg를 찾을 수 없습니다. FFMPEG_BIN 환경변수를 설정하거나 PATH에 ffmpeg를 추가하세요.)	osenvirongetr   existsstrshutilwhichFileNotFoundError)env_bin	local_binpath_bins      7/home/jay/workspace/skills/motion-cardnews-ko/render.py_get_ffmpeg_binr      su    
 jjnn\2.G4='')7|-II||H%H
z     c                `   |\  }}t        |       }	t        d|dz        }
||
z
  }|dg}| D ]  }|dddt        |      dt        |      gz  }! |r|dt        |      gz  }g }t        |	      D ]/  }|j	                  d| d	| d
| d| d
| d|
 d| d|
 d| d       1 dj                  d t        |	      D              }|j	                  | d|	 d       dj                  |      }|d|ddgz  }|r|d|	 ddgz  }|dd||	z  dz
   dgz  }|dd d!d"d#t        |      d$d%t        |      g	z  }|S )&u0   fade 효과 ffmpeg 명령어를 구성합니다.      ?333333?-y-loop1-t-i[	:v]scale=:*:force_original_aspect_ratio=decrease,pad=z/:(ow-iw)/2:(oh-ih)/2,setsar=1,fade=t=in:st=0:d=z,fade=t=out:st=z:d=[v]r   c              3  (   K   | ]
  }d | d  ywr$   r%   N .0is     r   	<genexpr>z"_build_fade_cmd.<locals>.<genexpr>H        11r!AY1   	concat=n=:v=1:a=0[vout];-filter_complex-map[vout]:a	-shortestz-afzafade=t=out:st=   z:d=1-c:vlibx264-pix_fmtyuv420p-r	-movflags
+faststart)lenminr   rangeappendjoin)frame_pathsoutput_pathsizefpsduration_per_framebgm_path
ffmpeg_binwhnfade_dfade_out_stcmdfpfilter_partsr+   inputs
filter_strs                     r   _build_fade_cmdrU   (   s    DAqKA(3./F$v-K $'C LdC(:$;T3r7KKL c(m$$ L1X 
s)A3as ##Qqc   &x ((MVHBqc<	

 WW1a11F6()A3n=>,'Jz68<<C1#R+..);a)?!)C(DDIJJ	Ic#h\K C Jr   c                   |\  }}t        |       }	|dg}
| D ]  }|
dddt        |      dt        |      gz  }
! |r|
dt        |      gz  }
g }t        |	      D ]&  }|j                  d| d| d| d	| d| d
| d       ( dj	                  d t        |	      D              }|j                  | d|	 d       dj	                  |      }|
d|ddgz  }
|r|
d|	 ddgz  }
|
dddddt        |      ddt        |      g	z  }
|
S )u1   slide 효과 ffmpeg 명령어를 구성합니다.r   r   r   r   r   r    r!   r"   r#   z:(ow-iw)/2:(oh-ih)/2,setsar=1[vr%   r   c              3  (   K   | ]
  }d | d  ywr'   r(   r)   s     r   r,   z#_build_slide_cmd.<locals>.<genexpr>x   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   r8   r9   r:   r;   r<   r=   r>   )r?   r   rA   rB   rC   )rD   rE   rF   rG   rH   rI   rJ   rK   rL   rM   rP   rQ   rR   r+   rS   rT   s                   r   _build_slide_cmdrX   \   si    DAqKA $'C LdC(:$;T3r7KKL c(m$$ L1X 
s)A3as ##Qqc81>	

 WW1a11F6()A3n=>,'Jz68<<C1#R+..	Ic#h\K C Jr   c                p   |\  }}t        |       }	t        ||z        }
|dg}| D ]  }|dddt        |      dt        |      gz  }! |r|dt        |      gz  }g }t        |	      D ]N  }t        |dz        }t        |dz        }|j	                  d| d| d	| d
| d	| d|
 d| d| d| d| d       P dj                  d t        |	      D              }|j	                  | d|	 d       dj                  |      }|d|ddgz  }|r|d|	 ddgz  }|dddddt        |      d d!t        |      g	z  }|S )"u<   zoom (Ken Burns) 효과 ffmpeg 명령어를 구성합니다.r   r   r   r   r   g333333?r    r!   r"   z+:force_original_aspect_ratio=increase,crop=z$,zoompan=z='min(zoom+0.0005,1.1)':d=z:s=xz:fps=r$   r%   r   c              3  (   K   | ]
  }d | d  ywr'   r(   r)   s     r   r,   z"_build_zoom_cmd.<locals>.<genexpr>   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   r8   r9   r:   r;   r<   r=   r>   )r?   intr   rA   rB   rC   )rD   rE   rF   rG   rH   rI   rJ   rK   rL   rM   total_framesrP   rQ   rR   r+   zoom_wzoom_hrS   rT   s                      r   _build_zoom_cmdr`      s    DAqKAs//0L $'C LdC(:$;T3r7KKL c(m$$ L1X 
QWQWs)F81VH -3as 22>s1#QqcseSUVWUXXY[	

 WW1a11F6()A3n=>,'Jz68<<C1#R+..	Ic#h\K C Jr   c                   |\  }}t        |       }	t        d|dz        }
|dg}| D ]  }|dddt        |      dt        |      gz  }! |r|dt        |      gz  }g }t        |	      D ]&  }|j	                  d| d	| d
| d| d
| d| d       ( |	dk(  r|j	                  d       nWd}t        d|	      D ]F  }t        d||z  |
z
        }||	dz
  k(  rdnd| }|j	                  d| d| d|
 d| d| d       |}H dj                  |      }|d|ddgz  }|r|d|	 ddgz  }|dddd d!t        |      d"d#t        |      g	z  }|d$fS )%uE   dissolve (cross-dissolve) 효과 ffmpeg 명령어를 구성합니다.r   r   r   r   r   r   r   r    r!   r"   r#   z$:(ow-iw)/2:(oh-ih)/2,setsar=1[scaledr%   r7   z[scaled0]copy[vout]scaled0g        voutxfz][scaledz$]xfade=transition=dissolve:duration=z:offset=r1   r2   r3   r4   r5   r6   r8   r9   r:   r;   r<   r=   r>   N)r?   r@   r   rA   rB   maxrC   )rD   rE   rF   rG   rH   rI   rJ   rK   rL   rM   xfade_drP   rQ   rR   r+   
prev_labeloffset	out_labelrT   s                      r   _build_dissolve_cmdrj      s    DAqKA#)C/0G $'C LdC(:$;T3r7KKL c(m$$ L1X 
s)A3as ##Qqc=aSC	

 	Av12
q! 	#Aq#55@AF"#q1u*Bqc(IJ<xs +#9HVHAi[C #J	# ,'Jz68<<C1#R+..	Ic#h\K C 9r   c                4   |\  }}t        j                  dddd      }	| D ]C  }
|	j                  dt        |
      j	                          d       |	j                  d| d	       E | r/|	j                  dt        | d
         j	                          d       |	j                          |	j                  }|dg}|ddddd|gz  }|r|dt        |      gz  }d| d| d| d| d	}|d|ddgz  }|r|g dz  }|dddddt        |      ddt        |      g	z  }||fS ) u   sequence 효과 (concat demuxer) ffmpeg 명령어를 구성합니다.

    Returns:
        (cmd 리스트, concat_list_path 또는 None)
        호출자가 임시 파일을 정리해야 합니다.
    rK   z.txtFmotion_concat_)modesuffixdeleteprefixzfile 'z'
z	duration 
r   z-fconcatz-safe0r   z[0:v]scale=r"   r#   z#:(ow-iw)/2:(oh-ih)/2,setsar=1[vout]r2   r3   r4   )r3   z1:ar6   r8   r9   r:   r;   r<   r=   r>   )tempfileNamedTemporaryFilewriter   absoluteclosenamer   )rD   rE   rF   rG   rH   rI   rJ   rK   rL   concat_filerQ   concat_list_pathrP   rT   s                 r   _build_sequence_cmdr}      s    DAq--6FK  >F48#4#4#6"7s;<I&8%9<=> F4B#8#A#A#C"DCHI"'' $'CD(GS$0@AACc(m$$ aS! c1#8	:  z68<<C++	Ic#h\K C    r   fade   g      ?)effectrG   rH   rI   c          
     d   	 ddl m} |}t        |      }|j                  j                  dd	       h d
}||vrt        d| d      t               }d}	 |dk(  rt!        | ||||||      }n\|dk(  rt#        | ||||||      }nE|dk(  rt%        | ||||||      }n.|dk(  rt'        | ||||||      \  }}nt)        | ||||||      \  }}t+        j,                  |ddd      }|j.                  dk7  r7t1        d|j.                   d|j2                   ddj5                  |             	 |r4t6        j8                  j;                  |      rt7        j<                  |       |S # t        $ r 	 ddlm}	 |	j                  dt        t              j                  dz        }
|
|
j                  J d       |	j                  |
      }|
j                  j                  |       n# t        $ r Y nw xY wY w xY w# |r6t6        j8                  j;                  |      rt7        j<                  |       w w w xY w)u  PNG 프레임 시퀀스를 ffmpeg로 MP4 동영상으로 렌더링합니다.

    Args:
        frame_paths: PNG 프레임 파일 경로 목록 (순서대로)
        output_path: 출력 MP4 파일 경로
        size: (width, height) 출력 해상도
        effect: 모션 효과 ('fade', 'slide', 'zoom', 'dissolve', 'sequence')
        fps: 초당 프레임 수 (기본값: 30)
        duration_per_frame: 프레임당 표시 시간(초) (기본값: 1.0)
        bgm_path: BGM 파일 경로 (없으면 None)

    Returns:
        생성된 MP4 파일의 Path 객체

    Raises:
        ValueError: 알 수 없는 효과 이름인 경우
        RuntimeError: ffmpeg 실행 실패 시 (stderr 포함)
        FileNotFoundError: ffmpeg 바이너리를 찾을 수 없는 경우
    r7   )get_effect_filterr   N_mc_effects_standalonez
effects.pyu/   effects.py 모듈을 로드할 수 없습니다T)parentsexist_ok>   r~   zoomslidedissolvesequenceu   알 수 없는 효과: ''r~   r   r   r   i,  )capture_outputtexttimeoutu$   ffmpeg 렌더링 실패 (returncode=z):
STDERR:
z
CMD:  )effectsr   ImportErrorimportlib.utilutilspec_from_file_locationr   __file__parentloadermodule_from_specexec_module	Exceptionmkdir
ValueErrorr   rU   rX   r`   rj   r}   
subprocessrun
returncodeRuntimeErrorstderrrC   r
   pathr   unlink)rD   rE   rF   r   rG   rH   rI   r   __ilu_spec_effects_modvalid_effectsrJ   r|   rP   results                    r   render_motionr   /  s|   <. {#KTD9 FM]"3F81=>> "J&*(V,[+tSRdfnpz{Cw";T3HZ\dfpqCv!+{D#GY[ceopCz!$7[RVX[]oqy  |F  %G!C!$7[RVX[]oqy  |F  %G!C!	
 !6v7H7H6I J"MM? +(  " /? @II&'m  
	)00(X%%4E $)AtCttA007LLL$$\2 		f /? @II&' !AsC   E! CG5 !	G2+A3GG2	G+(G2*G++G21G25:H/)returnr   )rD   
list[Path]rE   r   rF   tuple[int, int]rG   r\   rH   floatrI   Optional[Path]rJ   r   r   z	list[str])rD   r   rE   r   rF   r   rG   r\   rH   r   rI   r   rJ   r   r   ztuple[list[str], Optional[str]])rD   r   rE   r   rF   r   r   r   rG   r\   rH   r   rI   r   r   r   )__doc__
__future__r   r
   r   r   ru   pathlibr   typingr   r   rU   rX   r`   rj   r}   r   r(   r   r   <module>r      s(   # 	     ,111 1 
	1
 1 1 1 1h,,, , 
	,
 , , , ,^000 0 
	0
 0 0 0 0f999 9 
	9
 9 9 9 %9x2!2!2! 2! 
	2!
 2! 2! 2! %2!t  ##WWW 	W
 W 
W W W 
Wr   