
    :ig3                    f   U 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Zddl	Z	ddl
mZ ddlmZ ddlmZ ej                   j#                  dd      Zd	ed
<    G d de      ZddZddZddZddZd ZddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 d dZdddd	 	 	 	 	 	 	 d!dZd"dZd#dZedk(  r e        yy)$uo  모션 카드뉴스 렌더 큐 (파일 기반 작업 큐).

IDS Phase 5 — HTML→MP4 모션 카드뉴스 백그라운드 처리 큐
- 파일 기반 작업 큐 (/tmp/motion_render_queue/ 또는 MOTION_QUEUE_DIR 환경변수)
- ThreadPoolExecutor 기반 병렬 처리 (기본 max_concurrent=2)
- 자동 재시도 (기본 max_retries=2)
- 작업 타임아웃 처리
- CLI: python3 motion_render_queue.py --process [--max-concurrent N]

사용 예시:
    # 작업 큐에 추가
    job_id = enqueue({
        "frame_paths": ["/path/to/frame1.png", "/path/to/frame2.png"],
        "output_path": "/path/to/output.mp4",
        "size": [1080, 1080],
        "effect": "fade",
        "fps": 30,
        "duration_per_frame": 1.5,
        "bgm_path": None,
    })

    # 큐 처리
    summary = process_queue(max_concurrent=2, timeout_per_job=120, max_retries=2)
    print(summary)
    )annotationsN)ThreadPoolExecutor)Enum)PathMOTION_QUEUE_DIRz/tmp/motion_render_queuestr_DEFAULT_QUEUE_DIRc                  $    e Zd ZdZdZdZdZdZdZy)	JobStatusu   작업 상태 열거형.PENDINGRUNNINGSUCCESSFAILEDTIMEOUTN)	__name__
__module____qualname____doc__r   r   r   r   r        L/home/jay/workspace/.worktrees/task-2459-dev5/scripts/motion_render_queue.pyr   r   *   s    "GGGFGr   r   c                     t        t        j                  j                  dt                    } | j                  dd       | dz  j                  d       | S )uB   큐 디렉토리 경로를 반환하고 없으면 생성합니다.r   T)parentsexist_okjobs)r   )r   osenvirongetr	   mkdir)	queue_dirs    r   _get_queue_dirr!   4   sI    RZZ^^$68JKLIOOD4O0-r   c                ,   t        t        j                               }t               }i | |t        j
                  j                  t        j                         dd}|dz  | dz  }|j                  t        j                  |t               d       |S )u  작업을 큐에 추가합니다.

    Args:
        job: 작업 딕셔너리.
             필수 키: frame_paths, output_path, size, effect, fps,
                      duration_per_frame, bgm_path

    Returns:
        고유 작업 ID (UUID4 문자열)
    r   )job_idstatus
created_atretry_countr   .jsondefaultutf-8encoding)r   uuiduuid4r!   r   r   valuetime
write_textjsondumps)jobr#   r    job_datajob_files        r   enqueuer7   <   s     F I
##))iikH 6!vhe$44H

8S9GLMr   c                L    t        j                  | j                  d            S )u!   작업 파일을 로드합니다.r*   r+   )r2   loads	read_text)r6   s    r   	_load_jobr;   W   s    ::h(('(:;;r   c                |    | d   }|dz  | dz  }|j                  t        j                  | t              d       y)u+   작업 상태를 파일에 저장합니다.r#   r   r'   r(   r*   r+   N)r1   r2   r3   r   )r5   r    r#   r6   s       r   	_save_jobr=   \   s@    hF6!vhe$44H

8S9GLr   c                    ddl m}  ddl}t        t              j
                  j
                  dz  dz  }d}|dz   |j                  v r|j                  |dz      j                  S dD ]  }| d| }||j                  vs| j                  ||| d	z        }||j                  J d
| d|        | j                  |      }||_        ||j                  |<   	 |j                  j                  |        ||j                  vr|| j                  ||dz  t        |      g      }||j                  
J d|        | j                  |      }	||	_        |	|j                  |<   	 |j                  j                  |	       |j                  | d   j                  S # t        $ r |j                  |=  w xY w# t        $ r |j                  |=  w xY w)u   render_motion 함수를 동적으로 로드합니다.

    하이픈이 포함된 스킬 패키지명(motion-cardnews-ko)을 처리합니다.
    r   Nskillszmotion-cardnews-komotion_cardnews_koz.render)sizeseffectsframesrenderocrbgm.z.pyzfailed to load submodule z from z__init__.py)submodule_search_locationszfailed to load package )importlib.utilutilsysr   __file__parentmodulesrender_motionspec_from_file_locationloadermodule_from_spec__package__exec_module	Exceptionr   )
_ilurK   	skill_dirpkg_namesubmodmod_namespecmod	init_specpkg_mods
             r   _get_render_motionr_   c   s   
 "X%%,,x7:NNI#H )s{{*{{8i/0>>> I Zq)3;;&//vhcN*D #(?vC\]c\ddjktjuAvv?''-C&CO$'CKK!'',$ s{{"00%(+I'7 1 
	
 $)9)9)EkI`ai`jGkkE''	2& 'H	((1
 ;;(7+,:::+  KK)"  	H%	s   F%F7 F47Gc                   t               }| j                  dg       D cg c]  }t        |       }}t        | j                  dd            }| j                  dddg      }t        |t        t
        f      r+t        |      dk\  rt        |d         t        |d         f}nd	}| j                  d
d      }|rt        |      nd} ||||t        | j                  dd            t        | j                  dd            t        | j                  dd            |      }	t        j                  j                  | d<   t        j                         | d<   |	}
| S c c}w )ub   단일 작업을 실행합니다.

    Returns:
        업데이트된 job_data 딕셔너리
    frame_pathsoutput_pathz
output.mp4size8     r      )rd   rd   bgm_pathNeffectfadefps   duration_per_frameg      ?)ra   rb   rc   rh   rj   rl   rg   r$   completed_at)r_   r   r   
isinstancelisttuplelenintr   floatr   r   r/   r0   )r5   rO   pra   rb   size_rawrc   bgm_rawrg   result_s              r   _execute_jobry      s6    '(M$,LL$CDq47DKDx||M<@AK||FT4L1H(T5M*s8}/AHQK #hqk"23ll:t,G 'tG}TH8<<&12UB'( .BC!HIF #**00HX#yy{H^AO3 Es   Ec                R    d}d}||k  rt         j                  j                   d<   t         |       dg}g g d fd}	t	        j
                  |	d      }
|
j                          |
j                  |       |
j                         rsd|d<   t         j                  j                   d<   d	| d
 d<   t         |       |5  |j                  dd      dz   |d<   |j                  dd      dz   |d<   ddd       yrRd   }t        ||       |5  |j                  dd      dz   |d<   |dkD  r|j                  dd      |z   |d<   ddd       yrt        d         }nd}|dz  }| d<   | d<   ||k  r#|5  |j                  dd      dz   |d<   ddd       ||k  rt         j                  j                   d<   | d<   t         |       |5  |j                  dd      dz   |d<   ddd       y# 1 sw Y   yxY w# 1 sw Y   yxY w# 1 sw Y   zxY w# 1 sw Y   yxY w)u   단일 작업을 재시도 로직과 함께 직접 실행합니다 (스레드 내에서 호출).

    타임아웃은 threading.Timer로 구현합니다.
    summary 접근은 lock으로 보호합니다.
    r    r$   Fc                     	 t        t                    } j                  |        y # t        $ r}j                  |       Y d }~y d }~ww xY w)N)ry   dictappendrU   )updatede
exc_holderr5   result_holders     r   workerz#_run_job_with_retry.<locals>.worker   sE    %&tH~6$$W- %!!!$$%s   %) 	AAAT)targetdaemontimeoutu   타임아웃 (u   초)errorr   rf   failedNsuccessretry_totalu   알 수 없는 오류r&   
last_errorreturnNone)r   r   r/   r=   	threadingThreadstartjoinis_aliver   r   r   r   )r5   r    timeout_per_jobmax_retriessummarylockr&   r   	timed_outr   tupdated_jobr   r   s   `           @@r   _run_job_with_retryr      sj    KJ

$&..44(I& G	$&&(
	% F48			'::<IaL!*!2!2!8!8HX"00A FHWh	* A%,[[A%>%B	"$+KK!$<q$@!A '*Kk9- Y%,[[A%>%B	"?-4[[-J[-XGM*Y  Z]+J0Jq"-!++% K)0]A)F)J&Kk 
$r #))//HX'H\h	"	 9#KK!4q89 9CA Y K K9 9s0   1G96H?HH9HHHH&re   x   max_concurrentr   r   c                0   t               }t        |dz  j                  d            }g }|D ]K  }	 t        |      }|j	                  d      t
        j                  j                  k(  r|j                  |       M t        |      ddddt        j                         d}	|s|	S |D ]+  }t
        j                   j                  |d<   t#        ||       - t%        j&                         }
t)        | 	      5 }|D cg c]  }|j+                  t,        |||||	|
       }}|D ]  }|j/                  ||d
z   z          	 ddd       |	S # t        $ r5}t        j                  d|j                   d| t               Y d}~Rd}~ww xY wc c}w # 1 sw Y   |	S xY w)u  큐의 모든 PENDING 작업을 처리합니다.

    Args:
        max_concurrent: 동시 처리할 최대 작업 수 (ThreadPoolExecutor max_workers)
        timeout_per_job: 작업당 최대 대기 시간(초)
        max_retries: 실패 시 최대 재시도 횟수

    Returns:
        처리 요약 딕셔너리:
        {
            "total": int,
            "success": int,
            "failed": int,
            "timeout": int,
            "retry_total": int,
        }
    r   *.jsonr$   u   작업 파일 로드 실패 (z): Nr   )totalr   r   r   r   
started_at)max_workersre   r   )r!   sortedglobr;   r   r   r   r/   r~   rU   warningswarnnameUserWarningrq   r0   r   r=   r   Lockr   submitr   rw   )r   r   r   r    	job_filespending_jobsjfjdr   r   r   executorfuturesfs                 r   process_queuer     s   .  I	F*00:;I!L X	X2Bvvh9#4#4#:#::##B'	X \"iikG   ! ((..8"i ! >>D		7 B8 #
  OO#
 
  	BAHH_a@HA	BB  NM  	XMM9"''#aSI;WW	X.
B  Ns6   AE2F7"F!F	F*E>>FFFc                     t               } g }t        | dz  j                  d            D ]  }	 |j                  t	        |              |S # t
        $ r Y -w xY w)u/   큐의 모든 작업 상태를 반환합니다.r   r   )r!   r   r   r~   r;   rU   )r    r   r   s      r   	list_jobsr   U  sc     IDi&(..x89 	KK	"&
 K  		s   A	AAc                    t        j                  dt         j                  d      } | j                  ddd       | j                  ddd	       | j                  d
t        ddd       | j                  dt        ddd       | j                  dt        dd       | j                  dt
        dddt         d       | j                         }|j                  r|j                  t        j                  d<   |j                  rdt               }|st        d       y|D ]F  }t        d|j                  dd       d |j                  d!d"       d#|j                  d$d"              H y|j                  rzt        d%|j                    d&       t#        |j                   |j$                  |j&                  '      }t        d(       |j)                         D ]  \  }}t        d)| d*|         y| j+                          y)+u   CLI 진입점.u(   모션 카드뉴스 렌더 큐 처리기u   
사용 예시:
  # 큐 처리 (기본 설정)
  python3 motion_render_queue.py --process

  # 병렬 워커 수 지정
  python3 motion_render_queue.py --process --max-concurrent 4

  # 작업 목록 조회
  python3 motion_render_queue.py --list
        )descriptionformatter_classepilogz	--process
store_trueu)   큐의 PENDING 작업을 처리합니다.)actionhelpz--listu/   큐의 모든 작업 상태를 출력합니다.z--max-concurrentre   Nu1   동시 처리할 최대 작업 수 (기본값: 2))typer)   metavarr   z	--timeoutr   SECu,   작업당 타임아웃(초) (기본값: 120)z--max-retriesu&   최대 재시도 횟수 (기본값: 2))r   r)   r   z--queue-dirNDIRu$   큐 디렉토리 경로 (기본값: )r   u   큐가 비어있습니다.[r$   ?z] r#   r{   u    — rb   u"   큐 처리 시작 (max_concurrent=z)...r   u   처리 완료:z  z: )argparseArgumentParserRawDescriptionHelpFormatteradd_argumentrr   r   r	   
parse_argsr    r   r   ro   r   printr   processr   r   r   r   items
print_help)parserargsr   r   rw   kvs          r   mainr   a  s   $$> <<
F L?jk
<mn
*a  TG  H
#sEP~
c1Ckl
Cu34F3GqI  
 D~~)-

%&yy{./ 	gBAbffXs+,Brvvh/C.DE"&&Q^`bJcIdef	g||243F3F2GtLM.. LL((

 	LLN 	!DAqBqcA3- 	!
r   __main__)r   r   )r4   r}   r   r   )r6   r   r   r}   )r5   r}   r    r   r   r   )r5   r}   r   r}   )r5   r}   r    r   r   rr   r   rr   r   r}   r   zthreading.Lockr   r   )r   rr   r   rr   r   rr   r   r}   )r   z
list[dict]r   ) r   
__future__r   r   r2   r   r   r0   r-   r   concurrent.futuresr   enumr   pathlibr   r   r   r	   __annotations__r   r!   r7   r;   r=   r_   ry   r   r   r   r   r   r   r   r   <module>r      s!  2 #   	     1  **..);=WX C X 6<
M2;j!HM9M9M9 M9 	M9
 M9 M9 
M9d 	FF F 	F
 
FR	5p zF r   