
    -ǚj                     ~   d 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 ddlmZ ddlmZ ddlmZ dd	lmZmZmZ dd
lmZ ddlmZ ddlmZ ddlm Z  ddl!m"Z" ddl#m$Z$ ddl%m&Z&m'Z'm(Z( ddl)m*Z*m+Z+ ddl,m-Z. ddl/m0Z0 ddl1m2Z2 ddl3m4Z4  ejj                  e6      Z7 G d de      Z8 G d de      Z9 G d de      Z: G d de      Z; G d de      Z< ed d!g"      Z=d#e>d$ed%e$d&e0fd'Z?d#e>d$ed%e$d&e2fd(Z@d#e>d$ed%e$d&e4fd)ZAe=j                  d*eCe&   +       e	dd,-       e	d.       e	d.       e	d.       e	d.       ee       ee       ee      fd,e>dz  d/e>dz  d0e>dz  d1e>dz  d2e>dz  d#e>d$ed3e$fd4       ZDe=j                  d5e&+       ee       ee       ee      fd6e>d#e>d$ed3e$fd7       ZEe=j                  d8      d ee       ee       ee      fd6e>d9eFd#e>d$ed3e$f
d:       ZGe=j                  d;e&+       ee       ee       ee      fd6e>d<e'd=e
d#e>d$ed3e$fd>       ZIe=j                  d?e(+       ee       ee       ee      fd6e>d=e
d#e>d$ed3e$f
d@       ZKe=j                  dAe(+       ee       ee       ee      fd6e>d#e>d$ed3e$fdB       ZLe=j                  dC       ee       ee       ee      fd6e>d=e
d#e>d$ed3e$f
dD       ZMe=j                  dEe&+       ee       ee       ee      fd6e>d=e
d#e>d$ed3e$f
dF       ZNe=j                  dGe&+       ee       ee       ee      fd0e>d<e8d=e
d#e>d$ed3e$fdH       ZOd#e>d0e>dIe>dJe>dz  fdKZPe=j                  dLe&+       e	dM       e	dM       e	ddNO       ee       ee       ee      fd=e
dPe>d2e>d/e>dz  d#e>d$ed3e$fdQ       ZQe=j                  dR       ee       ee       ee      fd0e>d#e>d$ed3e$fdS       ZRe=j                  dT       ee       ee       ee      fd0e>d=e
d#e>d$ed3e$f
dU       ZSe=j                  dVe&+       ee       ee       ee      fd1e>d<e8d=e
d#e>d$ed3e$fdW       ZT G dX dYe      ZUe=j                  dZ       ee       ee       ee      fd1e>d=e
d#e>d$ed3e$f
d[       ZVe=j                  d\       ee       ee       ee      fd#e>d$ed3e$fd]       ZWe=j                  d\       ee       ee       ee      fd<eUd=e
d#e>d$ed3e$f
d^       ZXe=j                  d_e&+       edM       ed.       ed.       ed.      d ee       ee       ee      fd`ed0ee>   d1ee>   d/ee>   d=e
d#e>d$ed3e$fda       ZY	 dd#e>dIe>d/e>dJe>dz  dce>d&dfddZZe=j                  de       e	dbdfg       ee       ee       ee      fd/e>d=e
dce>d#e>d$ed3e$fdh       Z[e=j                  di       e	dbdfg       ee       ee       ee      fd/e>d=e
dce>d#e>d$ed3e$fdj       Z\e=j                  dk       ee       ee       ee      fd/e>d=e
d#e>d$ed3e$f
dl       Z]e=j                  dm       ee       ee       ee      fd/e>d#e>d$ed3e$fdn       Z^e=j                  do       ee       ee       ee      fd/e>d#e>d$ed3e$fdp       Z_e=j                  dq       ee       ee       ee      fd/e>d=e
d#e>d$ed3e$f
dr       Z`e=j                  ds       ee       ee       ee      fd1e>d=e
d#e>d$ed3e$f
dt       Zae=j                  du       ee       ee       ee      fd1e>d=e
d#e>d$ed3e$f
dv       Zbe=j                  dw       ee       ee       ee      fd6e>d<e9d=e
d#e>d$ed3e$fdx       Zce=j                  dy       ee       ee       ee      fd6e>d<e:d=e
d#e>d$ed3e$fdz       Zde=j                  d{       ee       ee       ee      fd1e>d<e;d=e
d#e>d$ed3e$fd|       Zee=j                  d}       ee       ee       ee      fd1e>d<e<d=e
d#e>d$ed3e$fd~       Zfe=j                  d       ee       ee       ee      fd1e>d#e>d$ed3e$fd       Zge=j                  deCe*   +       e	dd       e	ddd       ee       ee       ee      fdeFdeFd#e>d$ed3e$f
d       Zhe=j                  de++       ee       ee       ee      fd#e>d$ed3e$fd       Zie=j                  deCe*   +       ee       ee       ee      fd6e>d#e>d$ed3e$fd       Zjy)u   이미지 관리 API 라우터.    N)Path)Optional)	APIRouterDependsQueryRequest
UploadFileFileForm)FileResponse)	BaseModel)Session)get_dbget_current_userverify_project_accesssettingsSessionLocal)AppError)submit_background_job)t)UserAccount)ImageResponseReviewUpdateValidationResponse)GenerationTraceResponseRejectionStatsResponse)	key_count)ImageService)ReferenceImageService)SceneImageServicec                   6    e Zd ZU dZee   ed<   dZee   ed<   y)GenerateSingleRequestNcustom_promptvariation_index)	__name__
__module____qualname__r%   r   str__annotations__r&   int     G/Users/manta/Documents/Projects/TheRoad-I1/backend/app/api/v1/images.pyr$   r$      s    #'M8C='%)OXc])r.   r$   c                   8    e Zd ZU dZeed<   dZeed<   dZeed<   y)EditAngleRequestr   
horizontalverticalg      ?zoomN)	r'   r(   r)   r2   r,   r+   r3   r4   floatr-   r.   r/   r1   r1   "   s     JHcD%r.   r1   c                       e Zd ZU eed<   y)EditColorRequestcolor_promptNr'   r(   r)   r*   r+   r-   r.   r/   r7   r7   (   s    r.   r7   c                       e Zd ZU eed<   y)SelectVariantRequestvariantNr9   r-   r.   r/   r;   r;   ,   s    Lr.   r;   c                       e Zd ZU eed<   y)SelectOriginalRequestimage_idNr9   r-   r.   r/   r>   r>   0   s    Mr.   r>   z/api/v1/projects/{project_id}images)prefixtags
project_iddbuserreturnc                 2    t        || |j                        S NrD   rC   actor_id)r    idrC   rD   rE   s      r/   _make_servicerM   :   s    
  r.   c                 2    t        || |j                        S rH   )r!   rK   rL   s      r/   _make_reference_servicerO   F   s    
 ! r.   c                 2    t        || |j                        S rH   )r"   rK   rL   s      r/   _make_scene_servicerQ   R   s    
  r.   z/images)response_modeltype)defaultalias)rT   
episode_id	entity_idstill_id
outlook_idcurrent_userc                 H    t        |||      }|j                  || |||      S )zList images, optionally filtered by asset type, episode, entity, or still.

    outlook_id: when provided, returns images whose prompt_used contains
    'outlook_id:{outlook_id}' (composite images referencing this outlook).
    )rV   
asset_typerW   rX   rY   )rM   list_images)	rS   rV   rW   rX   rY   rC   rD   rZ   services	            r/   r]   r]   ^   s7      JL9G$h   r.   z/images/{image_id}r?   c                 >    t        |||      }|j                  |       S )zGet image detail.)rM   	get_imager?   rC   rD   rZ   r^   s        r/   r`   r`   v   s#     JL9GX&&r.   z/images/{image_id}/filethumbc                 f   ddl m} t        |||      }|j                  |       } ||j                        }|t        dt        d      d      |j                         }	t        t        j                        j                         }
t        |	      j                  t        |
      dz         s|	|
k7  rt        dt        d      d	      |j                         st        dt        d      d      |r|j                  |j                  d
z   z  }|j                          xs5 |j!                         j"                  |j!                         j"                  k  }|rW	 ddlm} |j)                  |      5 }|j+                  d       |j-                  d      }|j/                  |dd       ddd       t3        t        |      d|j                  d
z   ddi      S t3        t        |      d|j4                        S # 1 sw Y   QxY w# t0        $ r$ t3        t        |      d|j4                        cY S w xY w)u;   Serve image file. ?thumb=1 로 축소 JPG 썸네일 반환.r   )resolve_image_pathNzimage.not_found  codemessagestatus_code/zimage.path_forbiddeni  z
.thumb.jpg)Image)  rl   RGBJPEGK   )qualityz	image/png)path
media_typefilenamez
image/jpegzCache-Controlzpublic, max-age=86400)rq   rr   rs   headers)app.core.file_pathsrd   rM   r`   	file_pathr   r   resolver   r   projects_dirr*   
startswithexistsparentstemstatst_mtimePILrk   open	thumbnailconvertsave	Exceptionr   name)r?   rb   rC   rD   rZ   rd   r^   imgrv   resolvedproject_dir
thumb_pathneeds_regenPILImagepil_imgs                  r/   get_image_filer      s
    7JL9G


H
%C"3==1I"'(
 	
   "Hx,,-557Kx=##C$4s$:;K@W''(
 	
 "'(
 	
 %%,)FG
$++--g1B1K1KinnN^NgNg1gj1]]9-%%j1%ooe4GLLVRL@ . Z#^^l2$&=>	
 	
 ^  .-  j#YKZcZhZhiijs*   H 07G7'H 7H <H *H0/H0z/images/{image_id}/reviewbodyrequestc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |j                  |j
                  |      S )z!Update review status of an image.Nip)clienthostrM   update_reviewstatusnotesr?   r   r   rC   rD   rZ   r   r^   s           r/   r   r      sK     !(		DBJL9G  4;;

r JJr.   z/images/{image_id}/validatec                    |j                   r|j                   j                  nd}t        |||      }|j                  | |      }| |j	                  d      |j	                  d      |j	                  dg       |j	                  dd      dS )	z0Re-validate a single image using GPT-5.4 vision.Nr   scorepassedissuesdescription )r?   r   r   r   r   )r   r   rM   validate_imageget)r?   r   rC   rD   rZ   r   r^   results           r/   r   r      s     !(		DBJL9G##H#4FG$**X&**Xr*zz-4 r.   z/images/{image_id}/validationc                 >    t        |||      }|j                  |       S )z)Get validation result for a single image.)rM   get_validationra   s        r/   r   r      s#     JL9G!!(++r.   z/images/{image_id}/regeneratec                     |j                   r|j                   j                  nd}t        |||      }|j                  | |       dt	        d      dS )zRegenerate a single image.Nr   Timage.regeneration_startedokrh   )r   r   rM   regenerate_imager   r?   r   rC   rD   rZ   r   r^   s          r/   r   r      sM     !(		DBJL9GX"-1%A#BCCr.   z/images/{image_id}/set-primaryc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |      S )zHSet an image as the primary (representative) image for its entity/still.Nr   )r   r   rM   set_primary_imager   s          r/   set_primaryr     s?     !(		DBJL9G$$X"$55r.   z$/entities/{entity_id}/generate-imagec                    ddl m} ddlm} |j	                  |      j                  |j                  |k(  |j                  | k(        j                         }|st        ddd       ||||j                         |j                  r|j                  j                  nd}	t        |||      }
|
j                  | |j                  |		      }|S )
z0Generate a single reference image for an entity.r   check_ref_images_readyEntityEpisodeLinkzgate.no_episode_linkuW   이 요소가 연결된 에피소드가 없습니다. 분석을 먼저 실행하세요.rl   rf   Nr%   r   )app.core.pipeline_gater   app.models.projectr   queryfilterrC   canon_idfirstr   rV   r   r   rO   generate_single_entity_imager%   )rW   r   r   rC   rD   rZ   r   r   linkr   r^   r   s               r/   generate_entity_imager     s     >488%&--$$
24E4N4NR[4[eg 	 2  =V  dg  h  	h2z4??; '		DB%j"lCG11)4K]K]bd1eFMr.   rJ   r   c                 f     ddl } fd}|j                  |d      j                          y)uB   백그라운드 스레드에서 관련 합성 이미지 재생성.r   Nc                  .   ddl m}   |        }	 t        |      }|j                         |j                          y # t        $ r4}dd l} |j                  t              j                  d|       Y d }~Id }~ww xY w# |j                          w xY w)Nr   r   )rJ   r   zAuto composite regen failed: %s)
app.core.databaser   r!    regenerate_composites_for_entityr   logging	getLoggerr'   warningclose)	r   db2svcexcr   rJ   rW   r   rC   s	        r/   _runz&_trigger_composite_regen.<locals>._run.  s    2n	'Z(KC00r0B
 IIK	  	XGh'//0QSVWW	X IIKs)   !A 	A?*A:5B :A??B BT)targetdaemon)	threadingThreadstart)rC   rW   rJ   r   r   r   s   ````  r/   _trigger_composite_regenr   *  s)    
 D.446r.   z/composites/generate.u   어느 화의 배정인가 — 안 주면 인물이 한 화에만 있을 때만 그 화로, 여러 화면 `shared` 아래에 적는다)r   character_idc                    ddl m} ddlm} t	        |j                  |j                        j                  |j                  |k(  |j                  |k(        j                         D 	ch c]  }	|	d   	 c}	      }
|||
vrt        ddt        |
       dd	      |xs t        |
      d
k(  r|
d   nd}|r
 ||||       | j                  r| j                  j                  nd}t        |||      }|j!                  ||||      S c c}	w )u4   합성 이미지 개별 생성 (인물 + 아웃룩).r   r   r   Nzimage.episode_not_linkedu2   이 인물은 그 화에 없습니다 (걸린 화 u   개)rl   rf      )r   rV   )r   r   r   r   sortedr   rV   r   rC   r   allr   lenr   r   rO   generate_composite_image)r   r   rY   rV   rC   rD   rZ   r   r   r_eps_gate_epr   r^   s                 r/   r   r   =  s&    >4
 hh0;;<CC((J6&&,6
 #%!  D *D"8+HTSWX 	 BTad1gTHr:x8 '		DB%j"lCG++jRJ , @ @!s   'Dz)/entities/{entity_id}/affected-compositesc                 D   ddl m}m} |j                  |      j	                  |j
                  | k(  |j                  |k(        j                         }|sdg dS g }|j                  dk(  r|j                  |      j	                  |j                  | k(  |j                  |k(        j                         }|D ]  }	|j                  |      j	                  |j
                  |	j                  k(        j                         }
|j                  | |j                  |	j                  |
r|
j                  ndd        n|j                  dk(  r|j                  |      j	                  |j                  | k(  |j                  |k(        j                         }|D ]  }	|j                  |      j	                  |j
                  |	j                  k(        j                         }|j                  |	j                  |r|j                  nd| |j                  d        t        |      |dS )uT   이 엔티티의 이미지가 변경되면 영향받는 합성 이미지 수 조회.r   )EntityCanonCharacterOutlook)countcombos	character?)r   character_namerY   outlook_nameoutlook)r   r   r   r   r   rK   rC   r   entity_typer   r   rY   appendr   r   )rW   rC   rD   rZ   r   r   entityr   rowscoolchs               r/   get_affected_compositesr   d  s    AXXk"))+..I*E{G]G]akGklrrtFb))F[(xx()00))Y68H8S8SWa8a

#% 	 B+&--knn.MNTTVBMM9(*RTrwwZ]_ `  
		y	(xx()00''946F6Q6QU_6_

#% 	 B+&--knn.OPVVXBMM2??Y[bggad(16;;P Q 
 [F33r.   z+/entities/{entity_id}/regenerate-compositesc                     |j                   r|j                   j                  nd}t        || |j                  |       dddS )ua   이 엔티티(인물/아웃룩)의 관련 합성 이미지를 모두 재생성 (백그라운드).NTu3   합성 이미지 재생성이 시작되었습니다r   )r   r   r   rK   )rW   r   rC   rD   rZ   r   s         r/   regenerate_entity_compositesr     s9     !(		DBZLOORH#XYYr.   z!/stills/{still_id}/generate-imagec                    ddl m} ddlm} ddlm} |j                  }	|	|	j                         xs d}	|j                  |      j                  |j                  | k(  |j                  |k(        j                         }
|
r+ |||
j                  |	du         ||||
j                         |j                  r|j                  j                  nd}t!        |||      }|j#                  | |	|      S )z*Generate a single scene image for a still.r   check_scene_images_readycheck_bg_catalog_freshness)
SceneStillNenforce_shot_bindingr   )r   r   !app.services.dispatcher_preflightr   r   r   r%   stripr   r   rK   rC   r   rV   r   r   rQ   generate_single_scene_image)rX   r   r   rC   rD   rZ   r   r   _SSr%   stillr   r^   s                r/   generate_still_imager     s     @L4 &&M %++-5HHSM  8!3S^^z5QRXXZE
 	#(("/4"7	
 	!Z1A1AB '		DB!*b,?G.." /  r.   c                   6    e Zd ZU dZee   ed<   dZee   ed<   y)ComposerPromptUpdateNcomposer_system_promptcomposer_user_prompt)r'   r(   r)   r   r   r*   r+   r   r-   r.   r/   r   r     s     ,0HSM0*.(3-.r.   r   z"/stills/{still_id}/compose-promptsc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |      S )z?Run T2I composer on a still, save results, return both prompts.Nr   )r   r   rM   compose_promptsrX   r   rC   rD   rZ   r   r^   s          r/   r   r     s?     !(		DBJL9G""8"33r.   z/settings/composer-promptc                 <    t        | ||      }|j                         S )z3Get current composer prompt settings for a project.)rM   get_composer_promptrC   rD   rZ   r^   s       r/   r  r    !     JL9G&&((r.   c                     |j                   r|j                   j                  nd}t        |||      }|j                  | j                  | j
                  |      S )z.Update project-level composer prompt override.N)system_promptuser_promptr   )r   r   rM   update_composer_promptr   r   )r   r   rC   rD   rZ   r   r^   s          r/   r  r    sV     !(		DBJL9G))11-- *  r.   z/images/uploadfilec                    K   |r"|j                   r|j                   j                  nd}| j                          d{   }	| j                  xs d}
t	        |||      }|j                  |	|
||||      }|S 7 :w)z&Upload a custom reference/scene image.Nzuploaded_image.png)
file_bytesrs   rW   rX   rV   r   )r   r   readrs   rM   upload_custom_image)r	  rW   rX   rV   r   rC   rD   rZ   r   r  rs   r^   r   s                r/   upload_imager    s      !(GNN		Byy{"J}}4 4HJL9G(( ) F M #s   :A9A7;A9resumemodec                 j   t               }	 t        || |      }|j                  |||       	 |j=                          y# t        $ rS}ddl} |j
                  t              j                  d|       	 ddlm	}	 |j                  |	      j                  |	j                  | k(  |	j                  |k(  |	j                  dk(  |	j                  dk(        j!                  |	j"                  j%                               j'                         }
|
rbdd	lm}m} |j-                  |j.                        j1                         }d
|
_        t3        |      dd |
_        ||
_        |j9                           # t        $ r }t:        j                  d|       Y d}~ d}~ww xY wd}~ww xY w# |j=                          w xY w)zDRun image generation in a background thread with its own DB session.rI   r   r  r   NzImage generation failed: %sPipelineProgressimage_generationrunningdatetimetimezoneerror  z2Failed to mark progress as error (still alive): %s)r   r"   generate_imagesr   r   r   r'   r  r   r  r   r   rC   rV   	operationr   order_by
started_atdescr   r  r  nowutc	isoformatr*   error_message
updated_atcommitloggerr   )rC   rJ   rV   r   r  rD   r   r   r   r  stuckr  r  r!  mark_excs                  r/   _run_image_gen_in_backgroundr*  	  s    
B#!

 	J2D9: 	
9  (#))*GM	Y;)*$//:=$//:=$..2DD$++y8	 *55::<=  7ll8<<0::<&&)#huo##& 		 	  	YLLMxXX	Y18 	
N   "A   
F
*F5C6E,+F,	F5FFFFFF   F2z0/episodes/{episode_id}/generate-reference-imagesz^resume$)patternc           	      J   |j                   r|j                   j                  nd}ddlm}  ||||        t        j
                  s$t               dk(  rt        dt        d      d      d }t        d|  |||j                  | ||fd	|  
      }	|	st        ddd      dddS )u  참조 이미지(엔티티)만 생성 (씬 이미지 제외). 백그라운드 스레드.

    mode: resume (only) — 미완성분만 추가 생성. 'full' 모드는
    feedback_never_delete_images 규칙 위반 (ref ImageAsset 일괄 DELETE +
    checkpoint clear) 으로 제거됨. 강제 재생성은 RefImageGenStep mode='force'
    (UPDATE is_primary=0 + 새 row is_primary=1, PNG 보존) 사용.
    Nr   r   image.gemini_key_missingrl   rf   c                 j   t               }	 t        || |      }|j                  |||       	 |j=                          y # t        $ rS}dd l} |j
                  t              j                  d|       	 ddlm	}	 |j                  |	      j                  |	j                  | k(  |	j                  |k(  |	j                  dk(  |	j                  dk(        j!                  |	j"                  j%                               j'                         }
|
rbddlm}m} |j-                  |j.                        j1                         }d	|
_        t3        |      d d
 |
_        ||
_        |j9                           # t        $ r }t:        j                  d|       Y d }~ d }~ww xY wd }~ww xY w# |j=                          w xY w)NrI   r  r   z%Reference image generation failed: %sr  reference_image_generationr  r  r  r  z.Failed to mark reference progress as error: %s)r   r!   generate_reference_images_onlyr   r   r   r'   r  r   r  r   r   rC   rV   r  r   r  r  r   r   r  r  r!  r"  r#  r*   r$  r%  r&  r'  r   )pidrJ   ep_idip_addrgen_moder   r   r   r   r  r(  r  r  r!  r)  s                  r/   _run_ref_genz/generate_reference_images.<locals>._run_ref_genN  sw   n	'33RC..ux.P8 IIK7  	Gh'--.UWZ[Y?II./V(33s:(33u<(226RR(//9<	 X.99>>@AUW  ;",,x||4>>@C#*EL*-c(5D/E''*E$JJL   YMxXXY/	6 IIKr+  zref_gen:z'Reference image generation for episode job_keyr   argsr    image.generation_already_runningu5   참조 이미지 생성이 이미 진행 중입니다  Tu0   참조 이미지 생성이 시작되었습니다r   )r   r   r   r   r   gemini_api_keygemini_key_countr   r   r   rK   )
rV   r   r  rC   rD   rZ   r   r   r6  starteds
             r/   generate_reference_imagesr?  8  s      !(		DB=2z:6""'7'9Q'>6B\@]knoo D $:,',//:r4@=j\J	G >H  NQ  R  	R#UVVr.   z&/episodes/{episode_id}/generate-imagesc           	         |j                   r|j                   j                  nd}ddlm} |j	                  |      j                  |j                  | k(  |j                  |k(        j                         }|st        dt        d      d      |j                  st        dt        d      d      t        j                  s$t               dk(  rt        d	t        d	      d      dd
lm}	 ddlm}
  |
|| d        |	|||        ddlm}  ||||        t+        d|  t,        ||j                  | ||fd|        }|st        ddd      dt        d      dS )uh  Start image generation for an episode (background thread).

    mode: resume (only) — 미완성분만 추가 생성. 'full' 모드는
    feedback_never_delete_images 규칙 위반으로 제거됨. 강제 재생성은
    StepRunner SceneImagePipelineStep mode='force' (기존 row/PNG 보존
    + UPDATE is_primary=0 + 새 row is_primary=1) 를 사용.
    Nr   )Episodezepisode.not_foundre   rf   zanalysis.no_textrl   r.  r   r   Tr   )assert_episode_asset_readinessz
image_gen:zImage generation for episode r7  r:  u.   이미지 생성이 이미 진행 중입니다r;  zimage.generation_startedr   )r   r   r   rA  r   r   rK   rC   r   r   r   fulltextr   r<  r=  r   r   r   r   app.core.asset_readinessrB  r   r*  )rV   r   r  rC   rD   rZ   r   rA  episoder   r   rB  r>  s                r/   r  r  {  sl     !(		DB +
	

j('*<*<
*J	K	 
 $)*
 	

 #()
 	

 ""'7'9Q'>+01
 	
 @L
 z:DQRZ8
 H"2z:>#ZL)+,//:r4@3J<@	G >Hx  GJ  K  	K1%?#@AAr.   z*/episodes/{episode_id}/generate-variationsc                     |j                   r|j                   j                  nd}d }t        d|  |||j                  | fd|        }|st	        ddd	      d
ddS )uN   원본 이미지 기반 A/B 변형 추천 + 생성. 백그라운드 스레드.Nc                     t               }	 t        || |      }|j                  |       |j                          y # |j                          w xY wrH   )r   r"   generate_variations_onlyr   )r2  rJ   r3  r   r   s        r/   _run_var_genz)generate_variations.<locals>._run_var_gen  s=    n	#ssXNC((/IIKCIIKs	   < Azvar_gen:z!Variation generation for episode r7  r:  u+   변형 생성이 이미 진행 중입니다r;  rf   Tu-   변형 추천+생성이 시작되었습니다r   )r   r   r   rK   r   )rV   r   rC   rD   rZ   r   rI  r>  s           r/   generate_variationsrJ    s~     !(		DB $:,',//:67
|D	G >Hu  DG  H  	H#RSSr.   z(/episodes/{episode_id}/generation-statusc           
      |   ddl m}m}m} ddlm}m} t         ||||             }	|	t         ||||             z
  }
|j                  |      j                  |j                  |k(  |j                  | k(  |j                  dk(  |j                  dk\  |j                  dk7        j                         }d}|dkD  rq|j                  |j                         j                  |j                  |k(  |j                  | k(  |j"                  dk(        j%                         j                         }|}ddlm} ddlm} t/        |j0                        |z  d	z  d
z  | z  }d}d}	  ||d      }|j2                  }	  ||d      }|j2                  }|j                  |      j                  |j                  |k(  |j                  | k(  |j"                  dk(  |j:                  dk(        j                         }|j                  |      j                  |j                  |k(  |j                  | k(  |j<                  dk(        j                         }ddlm } t         |||             }|	|
|||||||d	S # t4        $ r!}t6        j9                  d|       Y d}~d}~ww xY w# t4        $ r!}t6        j9                  d|       Y d}~2d}~ww xY w)u6   에피소드의 이미지 생성 완료 상태 조회.r   )r   r   
ImageAsset)missing_reference_entitiesreference_targetsTstalescene)ImageCheckpointManagerr   checkpointsr@   	referenceu>   reference checkpoint failed_count load failed: %s — 0 유지Nu:   scene checkpoint failed_count load failed: %s — 0 유지r   	angle_fal)load_low_freq_skip_ids)		ref_totalref_done
ref_failedref_low_freq_skippedscene_total
scene_donescene_failedscene_primary	fal_count)!r   r   r   rL  r   rM  rN  r   r   r   rC   rV   is_selectedstill_indexr   r   rX   r\   distinctapp.modules.image_checkpointrQ  app.core.configr   r   rx   failed_countr   r'  r   
is_primaryvariant_typeapp.core.low_freq_skiprU  )rV   rC   rD   rZ   r   r   rL  rM  rN  rV  rW  rZ  r[  stills_with_imagesrQ  	_settingscp_dirrX  r\  ref_cpr   scene_cpr]  r^  rU  low_freq_skippeds                             r/   get_generation_statusrn    s    ML
 %b*jABI3"2z:>@ @H 		!!Z/!!Z/""d*""a'(

 
  JQHHZ(()V%%3%%3%%0
 XZUW 	 (
 D5)(()J6FQT^^FJL^'<((
Z)&':,, 		!!Z/!!Z/!!W,!!Q&	

 
  		!!Z/!!Z/##{2


 
  >1*jIJ   0" $&
 
G  ^WY\]]^
  ZSUXYYZs0   I$ /J $	J-J		J	J;J66J;z$/episodes/{episode_id}/pipeline-gatec                 "    ddl m}  ||||       S )uB   파이프라인 단계별 완성도 + 다음 단계 가능 여부.r   )get_pipeline_status)r   rp  )rV   rC   rD   rZ   rp  s        r/   get_pipeline_gaterq  L  s     ;r:z::r.   z+/episodes/{episode_id}/regenerate-needs-fixc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |      }d|t	        d      dS )z/Regenerate all needs_fix images for an episode.Nr   Tr   )r   r   rh   )r   r   rM   regenerate_needs_fixr   )rV   r   rC   rD   rZ   r   r^   r   s           r/   rs  rs  X  sQ     !(		DBJL9G(((;E15Q3RSSr.   z'/stills/{still_id}/recommend-variationsc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |      S )u#   LLM에게 A/B 변형 추천 받기.Nr   )r   r   rQ   recommend_variationsr   s          r/   ru  ru  l  s?     !(		DB!*b,?G''R'88r.   z+/stills/{still_id}/generate-with-variationsc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |      S )u,   원본 + A변형 + B변형 이미지 생성.Nr   )r   r   rQ   generate_scene_with_variationsr   s          r/   generate_with_variationsrx  z  s?     !(		DB!*b,?G11(r1BBr.   z/images/{image_id}/edit-anglec                     |j                   r|j                   j                  nd}t        |||      }ddl}|j	                  |j
                  |j                  |j                  d      }	|j                  | |	|      S )u   앵글 재편집 (i2i).Nr   )r2   r3   r4   )
angle_jsonr   )	r   r   rQ   jsondumpsr2   r3   r4   regenerate_variation)
r?   r   r   rC   rD   rZ   r   r^   r{  rz  s
             r/   
edit_angler~    sn     !(		DB!*b,?GooMM		 J
 ''ZB'OOr.   z/images/{image_id}/edit-colorc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |j                  |      S )u   색감 재편집 (i2i).N)r8   r   )r   r   rQ   r}  r8   r   s           r/   
edit_colorr    sH     !(		DB!*b,?G''t?P?PUW'XXr.   z!/stills/{still_id}/select-variantc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |j                  |      S )u   PDF 대표 변형 선택.Nr   )r   r   rQ   select_variantr<   rX   r   r   rC   rD   rZ   r   r^   s           r/   r  r    sE     !(		DB!*b,?G!!(DLLR!@@r.   z"/stills/{still_id}/select-originalc                     |j                   r|j                   j                  nd}t        |||      }|j                  | |j                  |      S )u   원본 이미지 선택.Nr   )r   r   rQ   select_originalr?   r  s           r/   r  r    sE     !(		DB!*b,?G""8T]]r"BBr.   z/stills/{still_id}/imagesc                 >    t        |||      }|j                  |       S )u.   씬의 모든 이미지 (original/A/B 그룹).)rM   get_still_images_grouped)rX   rC   rD   rZ   r^   s        r/   get_still_imagesr    s#     JL9G++H55r.   z/generation-traces)rT   ge2   r      )rT   r  leoffsetlimitc                 B    t        |||      }|j                  | |      S )z'List all generation traces (paginated).)r  r  )rM   get_generation_traces)r  r  rC   rD   rZ   r^   s         r/   list_generation_tracesr    s(     JL9G((e(DDr.   z/generation-traces/statsc                 <    t        | ||      }|j                         S )z$Get aggregated rejection statistics.)rM   get_rejection_statsr  s       r/   r  r    r  r.   z/images/{image_id}/tracesc                 >    t        |||      }|j                  |       S )z+Get generation traces for a specific image.)rM   get_image_tracesra   s        r/   r  r    s#     JL9G##H--r.   )r  )k__doc__r   pathlibr   typingr   fastapir   r   r   r   r	   r
   r   fastapi.responsesr   pydanticr   sqlalchemy.ormr   
OrmSessionapp.api.depsr   r   r   rc  r   r   r   app.core.errorsr   app.core.job_managerr   app.i18n.loaderr   app.models.catalogr   app.schemas.imager   r   r   app.schemas.tracer   r   app.modules.llm.gemini_key_poolr   r=  app.services.image_servicer    $app.services.reference_image_servicer!    app.services.scene_image_servicer"   r   r'   r'  r$   r1   r7   r;   r>   routerr*   rM   rO   rQ   r   listr]   r`   r,   r   patchr   postr   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r*  r?  r  rJ  rn  rq  rs  ru  rx  r~  r  r  r  r  r  r  r  r-   r.   r/   <module>r     s   %    N N N *  0 H H $ * $ 6  * M M M I 3 F >			8	$*I *
y y 9 I  
*

			 	 					 	 					 	 		 Id=&9:T8"40!$/ ."4034V_ '(8 9
*d
 Tz Dj	
 d
  	  ;.  ? 34V_ '(8 9	''' 	' 	' @' %& 34V_ '(8 9>>> > 		>
 > '>B )-H
 34V_ '(8 9KK
K K 	K
 	K K IK *;MN 34V_ '(8 9  		
  O( +<NO 34V_ '(8 9	,,, 	, 	, P, ,- 34V_ '(8 9DDD D 		D
 D .D -mL 34V_ '(8 9
6
6
6 
6 		
6
 
6 M
6 3MR
 34V_ '(8 9
  	
 	  S07 7 7 7QTW[Q[ 7& #MB c
Cj" Z[ 34V_ '(8 9#@#@#@ #@ d
	#@ #@ 	#@ #@ C#@L 78 34V_ '(8 9	444 	4 	4 94B :; 34V_ '(8 9
Z
Z
Z 
Z 		
Z
 
Z <
Z 0O
 34V_ '(8 9&&
& & 	&
 	& & P&R/9 /
 12 34V_ '(8 9
4
4
4 
4 		
4
 
4 3
4 '(34V_ '(8 9))) ) )) )* 34V_ '(8 9
  		
  +" m<Cy#D1"40 $T 234V_ '(8 9
} sm 	
   	  => ,,, , 	d
	,
 , 
,^ ?@ h
334V_ '(8 9?W?W?W ?W 	?W
 	?W ?W A?WD 56 h
334V_ '(8 9DBDBDB DB 	DB
 	DB DB 7DBN 9: 34V_ '(8 9TTT T 		T
 T ;T: 67 34V_ '(8 9	ggg 	g 	g 8gT 23 34V_ '(8 9	;;; 	; 	; 4; :; 34V_ '(8 9TTT T 		T
 T <T& 67 34V_ '(8 9
9
9
9 
9 		
9
 
9 8
9 :; 34V_ '(8 9
C
C
C 
C 		
C
 
C <
C ,-
 34V_ '(8 9PP
P P 	P
 	P P .P( ,-
 34V_ '(8 9YY
Y Y 	Y
 	Y Y .Y 12
 34V_ '(8 9AA
A A 	A
 	A A 3A 23
 34V_ '(8 9CC
C C 	C
 	C C 4C '( 34V_ '(8 9	666 	6 	6 )6  6M1NOa(raC034V_ '(8 9	E	E	E 	E 			E
 	E P	E &7MN34V_ '(8 9))) ) O) '=T8UV 34V_ '(8 9	... 	. 	. W.r.   