
    (<i<              	       <   d 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	m
Z
 ddlZddlZerddlmZ dZdZddee   dz  d	ej$                  fd
Zdeded	efdZddeded	eee	f   fdZded	efdZdeee	f   ded	efdZddee   dz  d	dfdZedk(  r e        yy)uG  
youtube-transcribe.py

범용 유튜브 음성 추출 CLI 스크립트

yt-dlp로 유튜브 영상에서 오디오를 다운로드하고,
로컬 Whisper GPU 서비스(http://localhost:8200/v1/transcribe)에
multipart/form-data로 전송하여 전사 결과를 반환합니다.

사용법:
    python3 youtube-transcribe.py --url "https://youtube.com/watch?v=..."
    python3 youtube-transcribe.py --url "..." --format json
    python3 youtube-transcribe.py --url "..." --format srt --output /path/to/out.srt
    python3 youtube-transcribe.py --url "..." --format text --language en
    N)Path)TYPE_CHECKINGAnycast)_Paramsz#http://localhost:8200/v1/transcribeiX  argvreturnc                    t        j                  dt         j                        }|j                  ddd       |j                  dg dd	d
       |j                  ddd       |j                  ddd       |j	                  |       S )u,   CLI 인자를 파싱하여 Namespace 반환.u5   유튜브 영상 음성 추출 및 Whisper 전사 CLI)descriptionformatter_classz--urlTu   YouTube URL (필수))requiredhelpz--format)textjsonsrtr   u+   출력 형식 (text|json|srt, 기본: text))choicesdefaultr   z--outputNu'   출력 파일 경로 (없으면 stdout))r   r   z
--languagekou   언어 코드 (기본: ko))argparseArgumentParserRawDescriptionHelpFormatteradd_argument
parse_args)r   parsers     K/home/jay/workspace/.worktrees/task-2057-dev2/scripts/youtube-transcribe.pyr   r   +   s    $$K <<F #  
 ':	   6  
 )  
 T""    url
output_dirc           	         t        ddt        t        |      dz        dddgddd      }t        j                  |      5 }|j                  | d	
      }|j                  |      }|j                  | g       ddd       t              j                  }t        |      | dz  }|j                         rt        |      S t        t        |      j                  d            }|rt        |d         S t        d|       # 1 sw Y   xY w)u,  
    yt-dlp로 YouTube URL에서 오디오를 WAV 포맷으로 다운로드.

    Args:
        url: YouTube 영상 URL
        output_dir: 임시 디렉토리 경로

    Returns:
        다운로드된 WAV 파일의 절대 경로

    Raises:
        Exception: yt-dlp 다운로드 실패 시
    zyt_dlp._Paramszbestaudio/bestz%(title)s.%(ext)sFFmpegExtractAudiowav)keypreferredcodecT)formatouttmplpostprocessorsquietno_warningsF)downloadNz.wavz*.wavr   u'   WAV 파일을 찾을 수 없습니다: )r   strr   yt_dlp	YoutubeDLextract_infoprepare_filenamer)   stemexistslistglobFileNotFoundError)	r   r   ydl_optsydlinfofilenamer/   wav_path	wav_filess	            r   download_audior:   N   s    &4
+.AAB 0&+ 	
H  
		(	# se4''-cU >DJTF$-/H8} T*%**734I9Q<  
Ej\R
SS# s   7DD
audio_pathlanguagec                    	 t        | d      5 }dt        |       j                  |dfi}d|i}t        j                  t
        ||t              }ddd       j                          |j                         S # 1 sw Y   )xY w# t        j                  j                  t        j                  j                  f$ r-}t        d| t        j                         d	g d
cY d}~S d}~ww xY w)u  
    로컬 Whisper GPU 서비스에 오디오 파일을 전송하여 전사 결과 반환.

    연결 실패 또는 타임아웃 시 경고 로그를 출력하고
    fallback 메시지를 포함한 dict를 반환합니다.

    Args:
        audio_path: 오디오 파일 경로
        language: 언어 코드 (기본: ko)

    Returns:
        전사 결과 dict {text, segments} 또는 fallback dict
    rbfilez	audio/wavr<   )filesdatatimeoutNu1   [경고] 로컬 Whisper 서비스 연결 실패: r?   u5   로컬 Whisper 서비스가 응답하지 않습니다)r   segments)openr   namerequestspostWHISPER_SERVICE_URLREQUEST_TIMEOUTraise_for_statusr   
exceptionsConnectionErrorTimeoutprintsysstderr)r;   r<   
audio_filer@   rA   responseexcs          r   transcribe_audiorU      s    
*d# 	zd:.33ZMNE)D}}#'	H	 	!!#}}	 	 //1D1D1L1LM 
?uE	

 L
 	

s5   B A A6'B 6A?;B 7C&9"C!C&!C&secondsc                     t        | dz        }t        | dz  dz        }t        | dz        }t        t        | t        |       z
  dz              }|dd|dd|dd|dS )u:   초를 SRT 타임코드 형식(HH:MM:SS,mmm)으로 변환.i  <   i  02d:,03d)intround)rV   hoursminutessecsmilliss        r   _seconds_to_srt_timerc      ss    4 E7T>b()Gw|D#g,.$678FC['#aSz6#,??r   resultfmtc                    ddl }|dk(  r|j                  | dd      S |dk(  r| j                  dg       }g }t        |d	
      D ]x  \  }}t	        |j                  dd            }t	        |j                  dd            }|j                  dd      j                         }	|j                  | d| d| d|	 d       z dj                  |      S | j                  dd      S )u   
    전사 결과를 지정된 형식으로 문자열 변환.

    Args:
        result: 전사 결과 dict
        fmt: 출력 형식 (text|json|srt)

    Returns:
        형식화된 문자열
    r   Nr   F   )ensure_asciiindentr   rD      )startrk   g        endr    
z --> )r   dumpsget	enumeraterc   stripappendjoin)
rd   re   _jsonrD   linesidxsegrk   rl   r   s
             r   format_outputry      s     
f}{{6a{@@
e|::j"-!(!4 	@HC(#)>?E&swwuc':;C7762&,,.DLLC55'se2dV2>?		@
 yy ::fb!!r   c                 B   t        |       }t        j                  d      }	 t        |j                  |      }t        ||j                        }t        ||j                        }|j                  r't        |j                        j                  |d       nt        |       t#        j$                  |d
       y	# t        $ r=}t        d| t        j                         t        j                   d       Y d	}~Yd	}~ww xY w# t#        j$                  |d
       w xY w)u   CLI 메인 함수.yt_transcribe_)prefix)r<   zutf-8)encodingz[ERROR] rC   rj   NT)ignore_errors)r   tempfilemkdtempr:   r   rU   r<   ry   r$   outputr   
write_textrO   	ExceptionrP   rQ   exitshutilrmtree)r   argstmp_dirr;   rd   output_textrT   s          r   mainr      s    dD&67G3#DHHg6
 "*t}}E $FDKK8 ;;((w(G+ 	gT2	  SZZ0 	gT2s*   BB< <	D3C=8D =DD D__main__)N)r   )__doc__r   r   rP   r   pathlibr   typingr   r   r   rG   r+   r   rI   rJ   r1   r*   	Namespacer   r:   dictrU   floatrc   ry   r   __name__ r   r   <module>r      s
      
   + +   <  #T#Y% #1C1C #F/T /T /T /Tn#
 #
 #
tCH~ #
V@% @C @"$sCx. "s "s "F3tCy4 34 38 zF r   