ginipick commited on
Commit
98e8d3c
ยท
verified ยท
1 Parent(s): ad33dd7

Update app-backup.py

Browse files
Files changed (1) hide show
  1. app-backup.py +175 -74
app-backup.py CHANGED
@@ -537,67 +537,93 @@ class ImageGenerator:
537
  enhanced_prompt = core_elements
538
 
539
  return enhanced_prompt # ์˜ฌ๋ฐ”๋ฅธ ์ธ๋ดํ…Œ์ด์…˜
540
-
 
541
  def generate_image(self, prompt: str, panel_id: str, session_id: str,
542
  scene_type: str = "medium", genre: str = "๋กœ๋งจ์Šค",
 
543
  progress_callback=None) -> Dict[str, Any]:
544
- """Generate image using qwen/qwen-image with webtoon optimization and auto-resize to 690x1227"""
545
  try:
546
  if not REPLICATE_API_TOKEN:
547
  logger.warning("No Replicate API token")
548
  return {"panel_id": panel_id, "status": "error", "message": "No API token"}
549
 
550
- # Fix: Use panel_id to extract panel_number
 
 
 
 
 
 
 
 
 
 
551
  panel_number = int(re.findall(r'\d+', panel_id)[-1]) if re.findall(r'\d+', panel_id) else 1
552
 
553
  # ์›นํˆฐ ์Šคํƒ€์ผ ํ”„๋กฌํ”„ํŠธ ๊ฐ•ํ™”
554
  enhanced_prompt = self.enhance_prompt_for_webtoon(prompt, panel_number, scene_type, genre)
555
 
556
- # qwen/qwen-image ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ • (์ง€์›๋˜๋Š” ์˜ต์…˜๋“ค)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
  input_params = {
558
  "prompt": enhanced_prompt,
559
- "aspect_ratio": "9:16", # ์„ธ๋กœํ˜• ์›นํˆฐ ํฌ๋งท (9:16, 16:9, 1:1, 4:3, 3:4 ๋“ฑ ์ง€์›)
560
- "num_outputs": 1, # ์ƒ์„ฑํ•  ์ด๋ฏธ์ง€ ์ˆ˜
561
- "guidance_scale": 4.5, # ํ”„๋กฌํ”„ํŠธ ์ถฉ์‹ค๋„ (1-10, ๋†’์„์ˆ˜๋ก ํ”„๋กฌํ”„ํŠธ์— ์ถฉ์‹ค)
562
- "num_inference_steps": 50, # ์ƒ์„ฑ ๋‹จ๊ณ„ ์ˆ˜ (๋” ๋†’์œผ๋ฉด ํ’ˆ์งˆ ํ–ฅ์ƒ, ์†๋„ ๊ฐ์†Œ)
563
- "output_format": "jpg", # JPG ํฌ๋งท์œผ๋กœ ๋ณ€๊ฒฝ (์›นํˆฐ์— ์ ํ•ฉ)
564
- "output_quality": 95, # ์ด๋ฏธ์ง€ ํ’ˆ์งˆ (1-100, jpg/webp์—๋งŒ ์ ์šฉ)
565
- "disable_safety_checker": False, # ์•ˆ์ „ ๊ฒ€์‚ฌ ๋น„ํ™œ์„ฑํ™” (๊ธฐ๋ณธ: False)
566
  "negative_prompt": "comic strip, multiple panels, divided frames, split screen, grid layout, manga panels",
567
- "seed": -1 # ์‹œ๋“œ๊ฐ’ (-1์€ ๋žœ๋ค)
568
  }
569
 
570
- logger.info(f"Generating image for panel {panel_id} with qwen/qwen-image")
571
- logger.info(f"Prompt (first 200 chars): {enhanced_prompt[:200]}...")
572
 
573
  try:
574
- # Replicate API ํ˜ธ์ถœ - qwen/qwen-image ์‚ฌ์šฉ
575
  output = replicate.run(
576
  "qwen/qwen-image",
577
  input=input_params
578
  )
579
 
580
  if output:
581
- # qwen/qwen-image๋Š” FileObject ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜
582
- # ์˜ˆ์ œ์ฒ˜๋Ÿผ .url() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉ
583
  if isinstance(output, list) and len(output) > 0:
584
- # ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
585
  image_item = output[0]
586
-
587
- # FileObject์˜ url() ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
588
  if hasattr(image_item, 'url'):
589
  if callable(image_item.url):
590
  image_url = image_item.url()
591
  else:
592
  image_url = image_item.url
593
  else:
594
- # ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ ์‹œ๋„
595
  image_url = str(image_item)
596
  elif isinstance(output, str):
597
- # ์ง์ ‘ URL ๋ฌธ์ž์—ด์ธ ๊ฒฝ์šฐ
598
  image_url = output
599
  else:
600
- # ๊ธฐํƒ€ ํ˜•์‹ ์ฒ˜๋ฆฌ
601
  image_url = str(output)
602
 
603
  # URL ์œ ํšจ์„ฑ ํ™•์ธ
@@ -605,7 +631,7 @@ class ImageGenerator:
605
  logger.error(f"Invalid image URL: {image_url}")
606
  return {"panel_id": panel_id, "status": "error", "message": "Invalid image URL format"}
607
 
608
- # ์ด๋ฏธ์ง€๋ฅผ 690x1227๋กœ ๋ฆฌ์‚ฌ์ด์ฆˆ
609
  logger.info(f"Resizing image to {WEBTOON_IMAGE_WIDTH}x{WEBTOON_IMAGE_HEIGHT}px")
610
  resized_image_url = self.resize_image_from_url(
611
  image_url,
@@ -613,18 +639,19 @@ class ImageGenerator:
613
  WEBTOON_IMAGE_HEIGHT
614
  )
615
 
616
- # ์บ์‹œ ์ €์žฅ (๋ฆฌ์‚ฌ์ด์ฆˆ๋œ ์ด๋ฏธ์ง€ URL ์ €์žฅ)
617
- cache_key = f"{session_id}_{panel_id}"
618
  generated_images_cache[cache_key] = resized_image_url
619
 
620
- logger.info(f"Successfully generated and resized image for panel {panel_id}")
621
  return {
622
  "panel_id": panel_id,
623
  "status": "success",
624
- "image_url": resized_image_url, # ๋ฆฌ์‚ฌ์ด์ฆˆ๋œ ์ด๋ฏธ์ง€ URL ๋ฐ˜ํ™˜
625
- "original_url": image_url, # ์›๋ณธ URL๋„ ์ €์žฅ
626
  "prompt": enhanced_prompt,
627
- "size": f"{WEBTOON_IMAGE_WIDTH}x{WEBTOON_IMAGE_HEIGHT}" # ํฌ๊ธฐ ์ •๋ณด ์ถ”๊ฐ€
 
 
628
  }
629
  else:
630
  logger.error(f"No output from qwen/qwen-image for panel {panel_id}")
@@ -641,6 +668,8 @@ class ImageGenerator:
641
  logger.error(f"Image generation error: {e}", exc_info=True)
642
  return {"panel_id": panel_id, "status": "error", "message": str(e)}
643
 
 
 
644
  # --- LLM Integration ---
645
  class WebtoonSystem:
646
  """Webtoon planning and storyboard generation system"""
@@ -1959,68 +1988,133 @@ def create_interface():
1959
 
1960
  # ์ฃผ์š” ์ˆ˜์ • ๋ถ€๋ถ„๋งŒ ํฌํ•จํ•œ ํŒจ์น˜ ์ฝ”๋“œ
1961
  # generate_selected_panel_images ํ•จ์ˆ˜ ์ˆ˜์ •
1962
- def generate_selected_panel_images(panel_data, selected_panels, session_id, character_profiles, webtoon_system, panel_prompts, genre, progress=gr.Progress()):
1963
- """Generate images for selected panels only - ์ˆ˜์ •๋œ ๋ฒ„์ „"""
 
 
1964
  if not REPLICATE_API_TOKEN:
1965
- html, _ = display_panels_with_editable_prompts(panel_data, panel_prompts, session_id, character_profiles, webtoon_system, genre)
1966
- return panel_data, html, gr.update(visible=True, value="โš ๏ธ Replicate API ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
1967
-
 
 
1968
  if not panel_data:
1969
- html, _ = display_panels_with_editable_prompts(panel_data, panel_prompts, session_id, character_profiles, webtoon_system, genre)
1970
- return panel_data, html, gr.update(visible=True, value="โš ๏ธ ํŒจ๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
1971
-
 
 
1972
  if not selected_panels:
1973
- html, _ = display_panels_with_editable_prompts(panel_data, panel_prompts, session_id, character_profiles, webtoon_system, genre)
1974
- return panel_data, html, gr.update(visible=True, value="โš ๏ธ ์ƒ์„ฑํ•  ํŒจ๋„์„ ์„ ํƒํ•˜์„ธ์š”.")
1975
-
 
 
1976
  if not webtoon_system:
1977
- webtoon_system = WebtoonSystem()
1978
- webtoon_system.current_genre = genre
1979
-
1980
  selected_numbers = [int(p.split()[1]) for p in selected_panels]
1981
  total = len(selected_numbers)
1982
  successful = 0
1983
-
1984
- # panel_data๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ์ž‘์—… (์›๋ณธ ๋ณด์กด)
 
1985
  updated_panel_data = panel_data.copy()
1986
-
1987
  for i, panel in enumerate(updated_panel_data):
1988
  if panel['number'] in selected_numbers:
1989
  idx = selected_numbers.index(panel['number'])
1990
  progress((idx / total), desc=f"ํŒจ๋„ {panel['number']} ์ƒ์„ฑ ์ค‘...")
1991
-
1992
  panel_id = f"panel_{panel['number']}"
1993
  prompt = panel_prompts.get(panel_id, panel.get('prompt', ''))
1994
-
 
 
 
1995
  if prompt:
1996
  try:
1997
  panel['prompt'] = prompt
1998
-
1999
- if not panel.get('prompt_en'):
2000
- panel['prompt_en'] = webtoon_system.translate_prompt_to_english(
2001
- prompt, character_profiles
2002
- )
2003
-
 
 
 
2004
  result = webtoon_system.image_generator.generate_image(
2005
- panel['prompt_en'],
2006
- f"ep1_panel{panel['number']}",
2007
- session_id,
2008
- scene_type=panel.get('scene_type', 'medium'),
2009
- genre=genre
 
2010
  )
2011
-
2012
  if result['status'] == 'success':
2013
  panel['image_url'] = result['image_url']
2014
  successful += 1
 
 
 
 
 
 
 
2015
  except Exception as e:
2016
  logger.error(f"Error generating panel {panel['number']}: {e}")
2017
-
 
2018
  time.sleep(0.5)
2019
-
2020
- progress(1.0, desc=f"์™„๋ฃŒ! {successful}/{total} ํŒจ๋„ ์ƒ์„ฑ ์„ฑ๊ณต")
2021
- html, _ = display_panels_with_editable_prompts(updated_panel_data, panel_prompts, session_id, character_profiles, webtoon_system, genre)
 
 
 
 
 
 
2022
  return updated_panel_data, html, gr.update(visible=False)
2023
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2024
  # generate_all_panel_images ํ•จ์ˆ˜ ์ˆ˜์ •
2025
  def generate_all_panel_images(panel_data, session_id, character_profiles, webtoon_system, panel_prompts, genre, progress=gr.Progress()):
2026
  """Generate images for all panels - ์ˆ˜์ •๋œ ๋ฒ„์ „"""
@@ -2288,11 +2382,23 @@ def create_interface():
2288
  inputs=[panel_data_state],
2289
  outputs=[panel_data_state, generation_progress]
2290
  ).then(
2291
- fn=generate_selected_panel_images,
2292
- inputs=[panel_data_state, panel_selector, current_session_id, character_profiles_state, webtoon_system, panel_prompts_state, current_genre],
2293
- outputs=[panel_data_state, panels_display, generation_progress] # panel_data_state ์ถœ๋ ฅ ์ถ”๊ฐ€
 
 
 
 
 
 
 
 
 
2294
  )
2295
 
 
 
 
2296
  # ๋ชจ๋“  ํŒจ๋„ ์ด๋ฏธ์ง€ ์ƒ์„ฑ - panel_data_state ์—…๋ฐ์ดํŠธ ์ถ”๊ฐ€
2297
  generate_all_images_btn.click(
2298
  fn=lambda pd: (pd, gr.update(visible=True, value="๋ชจ๋“  ํŒจ๋„ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹œ์ž‘...")),
@@ -2304,12 +2410,7 @@ def create_interface():
2304
  outputs=[panel_data_state, panels_display, generation_progress] # panel_data_state ์ถœ๋ ฅ ์ถ”๊ฐ€
2305
  )
2306
 
2307
- # ์ด๋ฏธ์ง€ ์ดˆ๊ธฐํ™” - panel_data_state ์—…๋ฐ์ดํŠธ ์ถ”๊ฐ€
2308
- clear_images_btn.click(
2309
- fn=clear_all_images,
2310
- inputs=[panel_data_state, panel_prompts_state, current_session_id, character_profiles_state, webtoon_system, current_genre],
2311
- outputs=[panel_data_state, panels_display] # panel_data_state ์ถœ๋ ฅ ์ถ”๊ฐ€
2312
- )
2313
 
2314
  # ์ „์ฒด ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ - ์ˆ˜์ •๋œ ๋ฉ”์‹œ์ง€
2315
  download_all_images_btn.click(
 
537
  enhanced_prompt = core_elements
538
 
539
  return enhanced_prompt # ์˜ฌ๋ฐ”๋ฅธ ์ธ๋ดํ…Œ์ด์…˜
540
+
541
+
542
  def generate_image(self, prompt: str, panel_id: str, session_id: str,
543
  scene_type: str = "medium", genre: str = "๋กœ๋งจ์Šค",
544
+ force_regenerate: bool = False, # ์žฌ์ƒ์„ฑ ๊ฐ•์ œ ํ”Œ๋ž˜๊ทธ ์ถ”๊ฐ€
545
  progress_callback=None) -> Dict[str, Any]:
546
+ """Generate image using qwen/qwen-image with regeneration support"""
547
  try:
548
  if not REPLICATE_API_TOKEN:
549
  logger.warning("No Replicate API token")
550
  return {"panel_id": panel_id, "status": "error", "message": "No API token"}
551
 
552
+ # ์บ์‹œ ํ‚ค ์ƒ์„ฑ
553
+ cache_key = f"{session_id}_{panel_id}"
554
+
555
+ # force_regenerate๊ฐ€ True์ด๋ฉด ์บ์‹œ ๋ฌด์‹œํ•˜๊ณ  ์ƒˆ๋กœ ์ƒ์„ฑ
556
+ if not force_regenerate and cache_key in generated_images_cache:
557
+ logger.info(f"Using cached image for {panel_id}")
558
+ # ์บ์‹œ๋œ ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋„๋ก ์ˆ˜์ •
559
+ # return {"panel_id": panel_id, "status": "success", "image_url": generated_images_cache[cache_key]}
560
+ pass # ์บ์‹œ ๋ฌด์‹œํ•˜๊ณ  ๊ณ„์† ์ง„ํ–‰
561
+
562
+ # Panel number ์ถ”์ถœ
563
  panel_number = int(re.findall(r'\d+', panel_id)[-1]) if re.findall(r'\d+', panel_id) else 1
564
 
565
  # ์›นํˆฐ ์Šคํƒ€์ผ ํ”„๋กฌํ”„ํŠธ ๊ฐ•ํ™”
566
  enhanced_prompt = self.enhance_prompt_for_webtoon(prompt, panel_number, scene_type, genre)
567
 
568
+ # ์žฌ์ƒ์„ฑ ์‹œ ํ”„๋กฌํ”„ํŠธ์— ๋ณ€ํ™” ์ถ”๊ฐ€ (๋” ๋‹ค์–‘ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•ด)
569
+ if force_regenerate or cache_key in generated_images_cache:
570
+ # ํ”„๋กฌํ”„ํŠธ์— ์•ฝ๊ฐ„์˜ ๋ณ€ํ™” ์ถ”๊ฐ€
571
+ variation_keywords = [
572
+ "different angle", "alternative view", "varied perspective",
573
+ "new composition", "fresh approach", "revised scene",
574
+ "different lighting", "alternative mood", "varied atmosphere"
575
+ ]
576
+ variation = random.choice(variation_keywords)
577
+ enhanced_prompt = f"{enhanced_prompt}, {variation}"
578
+ logger.info(f"Regenerating with variation: {variation}")
579
+
580
+ # ์žฌ์ƒ์„ฑ ์‹œ ๋‹ค๋ฅธ seed ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค๋ฅธ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ณด์žฅ
581
+ if force_regenerate or cache_key in generated_images_cache:
582
+ # ๋žœ๋ค seed ์ƒ์„ฑ (1-1000000 ๋ฒ”์œ„)
583
+ random_seed = random.randint(1, 1000000)
584
+ logger.info(f"Using random seed for regeneration: {random_seed}")
585
+ else:
586
+ # ์ฒซ ์ƒ์„ฑ ์‹œ์—๋„ ๋žœ๋ค seed ์‚ฌ์šฉ
587
+ random_seed = random.randint(1, 1000000)
588
+
589
+ # qwen/qwen-image ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •
590
  input_params = {
591
  "prompt": enhanced_prompt,
592
+ "aspect_ratio": "9:16",
593
+ "num_outputs": 1,
594
+ "guidance_scale": 4.5 + random.uniform(-0.5, 0.5), # ์•ฝ๊ฐ„์˜ ๋žœ๋ค์„ฑ ์ถ”๊ฐ€
595
+ "num_inference_steps": 50,
596
+ "output_format": "jpg",
597
+ "output_quality": 95,
598
+ "disable_safety_checker": False,
599
  "negative_prompt": "comic strip, multiple panels, divided frames, split screen, grid layout, manga panels",
600
+ "seed": random_seed # ๋ช…์‹œ์ ์œผ๋กœ ๋žœ๋ค seed ์„ค์ •
601
  }
602
 
603
+ logger.info(f"Generating {'new variant' if force_regenerate else 'image'} for panel {panel_id}")
604
+ logger.info(f"Seed: {random_seed}, Guidance: {input_params['guidance_scale']:.2f}")
605
 
606
  try:
607
+ # Replicate API ํ˜ธ์ถœ
608
  output = replicate.run(
609
  "qwen/qwen-image",
610
  input=input_params
611
  )
612
 
613
  if output:
614
+ # ์ด๋ฏธ์ง€ URL ์ถ”์ถœ
 
615
  if isinstance(output, list) and len(output) > 0:
 
616
  image_item = output[0]
 
 
617
  if hasattr(image_item, 'url'):
618
  if callable(image_item.url):
619
  image_url = image_item.url()
620
  else:
621
  image_url = image_item.url
622
  else:
 
623
  image_url = str(image_item)
624
  elif isinstance(output, str):
 
625
  image_url = output
626
  else:
 
627
  image_url = str(output)
628
 
629
  # URL ์œ ํšจ์„ฑ ํ™•์ธ
 
631
  logger.error(f"Invalid image URL: {image_url}")
632
  return {"panel_id": panel_id, "status": "error", "message": "Invalid image URL format"}
633
 
634
+ # ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ฆˆ
635
  logger.info(f"Resizing image to {WEBTOON_IMAGE_WIDTH}x{WEBTOON_IMAGE_HEIGHT}px")
636
  resized_image_url = self.resize_image_from_url(
637
  image_url,
 
639
  WEBTOON_IMAGE_HEIGHT
640
  )
641
 
642
+ # ์บ์‹œ ์—…๋ฐ์ดํŠธ (์žฌ์ƒ์„ฑ ์‹œ์—๋„ ์ƒˆ ์ด๋ฏธ์ง€๋กœ ๋ฎ์–ด์“ฐ๊ธฐ)
 
643
  generated_images_cache[cache_key] = resized_image_url
644
 
645
+ logger.info(f"Successfully generated {'variant' if force_regenerate else 'image'} for panel {panel_id}")
646
  return {
647
  "panel_id": panel_id,
648
  "status": "success",
649
+ "image_url": resized_image_url,
650
+ "original_url": image_url,
651
  "prompt": enhanced_prompt,
652
+ "size": f"{WEBTOON_IMAGE_WIDTH}x{WEBTOON_IMAGE_HEIGHT}",
653
+ "seed": random_seed, # seed ์ •๋ณด ๋ฐ˜ํ™˜
654
+ "regenerated": force_regenerate # ์žฌ์ƒ์„ฑ ์—ฌ๋ถ€ ํ‘œ์‹œ
655
  }
656
  else:
657
  logger.error(f"No output from qwen/qwen-image for panel {panel_id}")
 
668
  logger.error(f"Image generation error: {e}", exc_info=True)
669
  return {"panel_id": panel_id, "status": "error", "message": str(e)}
670
 
671
+
672
+
673
  # --- LLM Integration ---
674
  class WebtoonSystem:
675
  """Webtoon planning and storyboard generation system"""
 
1988
 
1989
  # ์ฃผ์š” ์ˆ˜์ • ๋ถ€๋ถ„๋งŒ ํฌํ•จํ•œ ํŒจ์น˜ ์ฝ”๋“œ
1990
  # generate_selected_panel_images ํ•จ์ˆ˜ ์ˆ˜์ •
1991
+ def generate_selected_panel_images_fixed(panel_data, selected_panels, session_id,
1992
+ character_profiles, webtoon_system,
1993
+ panel_prompts, genre, progress=gr.Progress()):
1994
+ """Generate images for selected panels with regeneration support"""
1995
  if not REPLICATE_API_TOKEN:
1996
+ html, _ = display_panels_with_editable_prompts(panel_data, panel_prompts,
1997
+ session_id, character_profiles,
1998
+ webtoon_system, genre)
1999
+ return panel_data, html, gr.update(visible=True, value="โš ๏ธ Replicate API ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
2000
+
2001
  if not panel_data:
2002
+ html, _ = display_panels_with_editable_prompts(panel_data, panel_prompts,
2003
+ session_id, character_profiles,
2004
+ webtoon_system, genre)
2005
+ return panel_data, html, gr.update(visible=True, value="โš ๏ธ ํŒจ๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
2006
+
2007
  if not selected_panels:
2008
+ html, _ = display_panels_with_editable_prompts(panel_data, panel_prompts,
2009
+ session_id, character_profiles,
2010
+ webtoon_system, genre)
2011
+ return panel_data, html, gr.update(visible=True, value="โš ๏ธ ์ƒ์„ฑํ•  ํŒจ๋„์„ ์„ ํƒํ•˜์„ธ์š”.")
2012
+
2013
  if not webtoon_system:
2014
+ webtoon_system = WebtoonSystem()
2015
+ webtoon_system.current_genre = genre
2016
+
2017
  selected_numbers = [int(p.split()[1]) for p in selected_panels]
2018
  total = len(selected_numbers)
2019
  successful = 0
2020
+ regenerated = 0
2021
+
2022
+ # panel_data๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ์ž‘์—…
2023
  updated_panel_data = panel_data.copy()
2024
+
2025
  for i, panel in enumerate(updated_panel_data):
2026
  if panel['number'] in selected_numbers:
2027
  idx = selected_numbers.index(panel['number'])
2028
  progress((idx / total), desc=f"ํŒจ๋„ {panel['number']} ์ƒ์„ฑ ์ค‘...")
2029
+
2030
  panel_id = f"panel_{panel['number']}"
2031
  prompt = panel_prompts.get(panel_id, panel.get('prompt', ''))
2032
+
2033
+ # ์ด๋ฏธ ์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
2034
+ has_existing_image = bool(panel.get('image_url'))
2035
+
2036
  if prompt:
2037
  try:
2038
  panel['prompt'] = prompt
2039
+
2040
+ # ์˜์–ด ํ”„๋กฌํ”„ํŠธ ๋ฒˆ์—ญ
2041
+ if not panel.get('prompt_en') or has_existing_image:
2042
+ # ์žฌ์ƒ์„ฑ ์‹œ ํ”„๋กฌํ”„ํŠธ ๋‹ค์‹œ ๋ฒˆ์—ญ
2043
+ panel['prompt_en'] = webtoon_system.translate_prompt_to_english(
2044
+ prompt, character_profiles
2045
+ )
2046
+
2047
+ # ์ด๋ฏธ์ง€ ์ƒ์„ฑ (์žฌ์ƒ์„ฑ ํ”Œ๋ž˜๊ทธ ํฌํ•จ)
2048
  result = webtoon_system.image_generator.generate_image(
2049
+ panel['prompt_en'],
2050
+ f"ep1_panel{panel['number']}",
2051
+ session_id,
2052
+ scene_type=panel.get('scene_type', 'medium'),
2053
+ genre=genre,
2054
+ force_regenerate=has_existing_image # ๊ธฐ์กด ์ด๋ฏธ์ง€๊ฐ€ ์žˆ์œผ๋ฉด ์žฌ์ƒ์„ฑ
2055
  )
2056
+
2057
  if result['status'] == 'success':
2058
  panel['image_url'] = result['image_url']
2059
  successful += 1
2060
+ if result.get('regenerated'):
2061
+ regenerated += 1
2062
+
2063
+ # seed ์ •๋ณด ์ €์žฅ (๋””๋ฒ„๊น…์šฉ)
2064
+ if 'seed' in result:
2065
+ panel['last_seed'] = result['seed']
2066
+
2067
  except Exception as e:
2068
  logger.error(f"Error generating panel {panel['number']}: {e}")
2069
+
2070
+ # API ๋ถ€ํ•˜ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•œ ๋”œ๋ ˆ์ด
2071
  time.sleep(0.5)
2072
+
2073
+ status_msg = f"์™„๋ฃŒ! {successful}/{total} ํŒจ๋„ ์ƒ์„ฑ ์„ฑ๊ณต"
2074
+ if regenerated > 0:
2075
+ status_msg += f" (์žฌ์ƒ์„ฑ: {regenerated}๊ฐœ)"
2076
+
2077
+ progress(1.0, desc=status_msg)
2078
+ html, _ = display_panels_with_editable_prompts(updated_panel_data, panel_prompts,
2079
+ session_id, character_profiles,
2080
+ webtoon_system, genre)
2081
  return updated_panel_data, html, gr.update(visible=False)
2082
 
2083
+ def clear_image_cache(session_id=None):
2084
+ """์ด๋ฏธ์ง€ ์บ์‹œ ํด๋ฆฌ์–ด"""
2085
+ global generated_images_cache
2086
+
2087
+ if session_id:
2088
+ # ํŠน์ • ์„ธ์…˜์˜ ์บ์‹œ๋งŒ ํด๋ฆฌ์–ด
2089
+ keys_to_remove = [k for k in generated_images_cache.keys() if k.startswith(f"{session_id}_")]
2090
+ for key in keys_to_remove:
2091
+ del generated_images_cache[key]
2092
+ logger.info(f"Cleared {len(keys_to_remove)} cached images for session {session_id}")
2093
+ else:
2094
+ # ์ „์ฒด ์บ์‹œ ํด๋ฆฌ์–ด
2095
+ cache_size = len(generated_images_cache)
2096
+ generated_images_cache.clear()
2097
+ logger.info(f"Cleared all {cache_size} cached images")
2098
+
2099
+
2100
+ def clear_all_images_with_cache(panel_data, panel_prompts, session_id,
2101
+ character_profiles, webtoon_system, genre):
2102
+ """Clear all images and cache"""
2103
+ # ์บ์‹œ ํด๋ฆฌ์–ด
2104
+ clear_image_cache(session_id)
2105
+
2106
+ # panel_data๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ์ˆ˜์ •
2107
+ updated_panel_data = panel_data.copy()
2108
+ for panel in updated_panel_data:
2109
+ panel['image_url'] = None
2110
+ panel.pop('last_seed', None) # seed ์ •๋ณด๋„ ์ œ๊ฑฐ
2111
+
2112
+ html, _ = display_panels_with_editable_prompts(updated_panel_data, panel_prompts,
2113
+ session_id, character_profiles,
2114
+ webtoon_system, genre)
2115
+ return updated_panel_data, html
2116
+
2117
+
2118
  # generate_all_panel_images ํ•จ์ˆ˜ ์ˆ˜์ •
2119
  def generate_all_panel_images(panel_data, session_id, character_profiles, webtoon_system, panel_prompts, genre, progress=gr.Progress()):
2120
  """Generate images for all panels - ์ˆ˜์ •๋œ ๋ฒ„์ „"""
 
2382
  inputs=[panel_data_state],
2383
  outputs=[panel_data_state, generation_progress]
2384
  ).then(
2385
+ fn=generate_selected_panel_images_fixed, # ์ˆ˜์ •๋œ ํ•จ์ˆ˜ ์ด๋ฆ„ ์‚ฌ์šฉ
2386
+ inputs=[panel_data_state, panel_selector, current_session_id,
2387
+ character_profiles_state, webtoon_system, panel_prompts_state, current_genre],
2388
+ outputs=[panel_data_state, panels_display, generation_progress]
2389
+ )
2390
+
2391
+ # clear_all_images_with_cache ํ•จ์ˆ˜๋„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ ์‚ฌ์šฉ
2392
+ clear_images_btn.click(
2393
+ fn=clear_all_images_with_cache, # ์บ์‹œ ํด๋ฆฌ์–ด ๊ธฐ๋Šฅ์ด ํฌํ•จ๋œ ๋ฒ„์ „ ์‚ฌ์šฉ
2394
+ inputs=[panel_data_state, panel_prompts_state, current_session_id,
2395
+ character_profiles_state, webtoon_system, current_genre],
2396
+ outputs=[panel_data_state, panels_display]
2397
  )
2398
 
2399
+
2400
+
2401
+
2402
  # ๋ชจ๋“  ํŒจ๋„ ์ด๋ฏธ์ง€ ์ƒ์„ฑ - panel_data_state ์—…๋ฐ์ดํŠธ ์ถ”๊ฐ€
2403
  generate_all_images_btn.click(
2404
  fn=lambda pd: (pd, gr.update(visible=True, value="๋ชจ๋“  ํŒจ๋„ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹œ์ž‘...")),
 
2410
  outputs=[panel_data_state, panels_display, generation_progress] # panel_data_state ์ถœ๋ ฅ ์ถ”๊ฐ€
2411
  )
2412
 
2413
+
 
 
 
 
 
2414
 
2415
  # ์ „์ฒด ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ - ์ˆ˜์ •๋œ ๋ฉ”์‹œ์ง€
2416
  download_all_images_btn.click(