AnimeOverlord commited on
Commit
6d33571
·
1 Parent(s): 23d298b

new update fix

Browse files
Files changed (1) hide show
  1. app.py +150 -18
app.py CHANGED
@@ -7,6 +7,9 @@ Beautiful Minecraft-themed Gradio UI with a dummy inference function
7
 
8
  import os
9
  import sys
 
 
 
10
  import torch
11
  import gradio as gr
12
  import spaces
@@ -14,6 +17,19 @@ from PIL import Image
14
  from huggingface_hub import snapshot_download
15
  from diffusers import DiffusionPipeline
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  _original_unraisablehook = sys.unraisablehook
18
 
19
 
@@ -32,6 +48,7 @@ def _ignore_asyncio_closed_fd_warning(unraisable):
32
 
33
 
34
  sys.unraisablehook = _ignore_asyncio_closed_fd_warning
 
35
 
36
  # ----------------------------------------------------------------------
37
  # CUSTOM MINECRAFT CSS — THE ULTIMATE THEME
@@ -635,6 +652,11 @@ LORA_DIR = "AnimeOverlord/flux2-klein-4b-mc-v2"
635
 
636
  _pipe = None
637
  _pipe_ready = False
 
 
 
 
 
638
 
639
  IMAGE_MODE = "Image"
640
  LIVE_MODE = "Live Camera"
@@ -655,35 +677,48 @@ if GRADIO_MAJOR >= 6:
655
  else:
656
  BLOCKS_KWARGS.update({"css": CUSTOM_CSS, "theme": MC_THEME})
657
 
 
 
 
 
658
 
659
  def _ensure_model_files():
 
660
  os.makedirs(MODEL_DIR, exist_ok=True)
661
  os.makedirs(LORA_DIR, exist_ok=True)
662
 
663
  # Download base model once into the volume
664
  if not os.path.exists(os.path.join(MODEL_DIR, "model_index.json")):
 
665
  snapshot_download(
666
  repo_id=BASE_MODEL_ID,
667
  local_dir=MODEL_DIR,
668
  local_dir_use_symlinks=False,
669
  )
 
 
670
 
671
  # Download LoRA once into the volume
672
  if not os.path.exists(os.path.join(LORA_DIR, "pytorch_lora_weights.safetensors")):
 
673
  snapshot_download(
674
  repo_id=LORA_ID,
675
  local_dir=LORA_DIR,
676
  local_dir_use_symlinks=False,
677
  allow_patterns=["pytorch_lora_weights.safetensors"],
678
  )
 
 
679
 
680
 
681
  def _load_pipe():
682
  global _pipe, _pipe_ready
683
 
684
  if _pipe_ready and _pipe is not None:
 
685
  return _pipe
686
 
 
687
  _ensure_model_files()
688
 
689
  pipe = DiffusionPipeline.from_pretrained(
@@ -706,6 +741,7 @@ def _load_pipe():
706
 
707
  _pipe = pipe
708
  _pipe_ready = True
 
709
  return pipe
710
 
711
 
@@ -720,6 +756,10 @@ def _minecraftify_image(
720
  seed: int,
721
  extra_details: str,
722
  ):
 
 
 
 
723
  pipe = _load_pipe()
724
  image = image.convert("RGB")
725
 
@@ -746,6 +786,7 @@ def _minecraftify_image(
746
  generator=generator,
747
  )
748
 
 
749
  return out.images[0]
750
 
751
 
@@ -758,28 +799,75 @@ def minecraftify(
758
  extra_details: str,
759
  ):
760
  if image is None:
 
761
  raise gr.Error("⛏️ Please upload a photo first!")
762
 
 
763
  return _minecraftify_image(image, steps, guidance_scale, seed, extra_details)
764
 
765
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
766
  @spaces.GPU(duration=120)
767
- def minecraftify_live(
768
- image: Image.Image,
769
- is_running: bool,
770
  steps: int,
771
  guidance_scale: float,
772
  seed: int,
773
  extra_details: str,
774
  ):
775
- if not is_running or image is None:
776
- return gr.skip()
777
 
778
- return _minecraftify_image(image, steps, guidance_scale, seed, extra_details)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
779
 
780
 
781
  def switch_input_mode(mode: str):
 
 
 
782
  image_mode = mode == IMAGE_MODE
 
 
 
783
  return (
784
  gr.update(visible=image_mode),
785
  gr.update(visible=image_mode),
@@ -787,21 +875,49 @@ def switch_input_mode(mode: str):
787
  gr.update(interactive=True),
788
  gr.update(visible=not image_mode, interactive=False),
789
  False,
 
790
  )
791
 
792
 
793
- def start_live():
794
- return True, gr.update(interactive=False), gr.update(interactive=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795
 
796
 
797
  def stop_live():
798
- return False, gr.update(interactive=True), gr.update(interactive=False)
 
 
 
 
 
 
 
 
 
799
 
800
 
801
  # ----------------------------------------------------------------------
802
  # UI
803
  # ----------------------------------------------------------------------
804
 
 
 
805
  with gr.Blocks(**BLOCKS_KWARGS) as demo:
806
 
807
  # ── Title Banner ──
@@ -862,6 +978,13 @@ with gr.Blocks(**BLOCKS_KWARGS) as demo:
862
  interactive=False,
863
  )
864
 
 
 
 
 
 
 
 
865
  extra_details = gr.Textbox(
866
  label="🗒️ Extra scene details (optional)",
867
  placeholder="e.g. 'this is a beach at sunset' or 'make the dog a wolf mob'...",
@@ -945,6 +1068,7 @@ with gr.Blocks(**BLOCKS_KWARGS) as demo:
945
  start_live_btn,
946
  stop_live_btn,
947
  live_running,
 
948
  ],
949
  queue=False,
950
  )
@@ -955,27 +1079,35 @@ with gr.Blocks(**BLOCKS_KWARGS) as demo:
955
  outputs=output_image,
956
  )
957
 
958
- start_live_btn.click(
959
  fn=start_live,
960
- inputs=None,
961
- outputs=[live_running, start_live_btn, stop_live_btn],
962
  queue=False,
963
  )
964
 
 
 
 
 
 
 
 
965
  stop_live_btn.click(
966
  fn=stop_live,
967
  inputs=None,
968
- outputs=[live_running, start_live_btn, stop_live_btn],
969
  queue=False,
970
  )
971
 
972
  live_image.stream(
973
- fn=minecraftify_live,
974
- inputs=[live_image, live_running, steps, guidance_scale, seed, extra_details],
975
- outputs=output_image,
976
- stream_every=0.3,
977
  trigger_mode="always_last",
978
- concurrency_limit=1,
979
  )
980
 
 
981
  demo.queue(max_size=1).launch(**LAUNCH_KWARGS)
 
7
 
8
  import os
9
  import sys
10
+ import logging
11
+ import threading
12
+ import time
13
  import torch
14
  import gradio as gr
15
  import spaces
 
17
  from huggingface_hub import snapshot_download
18
  from diffusers import DiffusionPipeline
19
 
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format="[%(asctime)s] [%(levelname)s] %(message)s",
23
+ force=True,
24
+ )
25
+ logger = logging.getLogger("minecraftify")
26
+
27
+
28
+ def log_event(message: str):
29
+ logger.info(message)
30
+ print(f"[minecraftify] {message}", flush=True)
31
+
32
+
33
  _original_unraisablehook = sys.unraisablehook
34
 
35
 
 
48
 
49
 
50
  sys.unraisablehook = _ignore_asyncio_closed_fd_warning
51
+ log_event("App module imported")
52
 
53
  # ----------------------------------------------------------------------
54
  # CUSTOM MINECRAFT CSS — THE ULTIMATE THEME
 
652
 
653
  _pipe = None
654
  _pipe_ready = False
655
+ _live_lock = threading.Lock()
656
+ _live_latest_frame = None
657
+ _live_latest_frame_id = 0
658
+ _live_capture_count = 0
659
+ _live_active = False
660
 
661
  IMAGE_MODE = "Image"
662
  LIVE_MODE = "Live Camera"
 
677
  else:
678
  BLOCKS_KWARGS.update({"css": CUSTOM_CSS, "theme": MC_THEME})
679
 
680
+ log_event(f"Gradio version detected: {gr.__version__}")
681
+ log_event(f"Blocks kwargs: {BLOCKS_KWARGS}")
682
+ log_event(f"Launch kwargs: {LAUNCH_KWARGS}")
683
+
684
 
685
  def _ensure_model_files():
686
+ log_event("Checking model files")
687
  os.makedirs(MODEL_DIR, exist_ok=True)
688
  os.makedirs(LORA_DIR, exist_ok=True)
689
 
690
  # Download base model once into the volume
691
  if not os.path.exists(os.path.join(MODEL_DIR, "model_index.json")):
692
+ log_event(f"Downloading base model: {BASE_MODEL_ID}")
693
  snapshot_download(
694
  repo_id=BASE_MODEL_ID,
695
  local_dir=MODEL_DIR,
696
  local_dir_use_symlinks=False,
697
  )
698
+ else:
699
+ log_event("Base model already present")
700
 
701
  # Download LoRA once into the volume
702
  if not os.path.exists(os.path.join(LORA_DIR, "pytorch_lora_weights.safetensors")):
703
+ log_event(f"Downloading LoRA: {LORA_ID}")
704
  snapshot_download(
705
  repo_id=LORA_ID,
706
  local_dir=LORA_DIR,
707
  local_dir_use_symlinks=False,
708
  allow_patterns=["pytorch_lora_weights.safetensors"],
709
  )
710
+ else:
711
+ log_event("LoRA already present")
712
 
713
 
714
  def _load_pipe():
715
  global _pipe, _pipe_ready
716
 
717
  if _pipe_ready and _pipe is not None:
718
+ log_event("Pipeline already loaded")
719
  return _pipe
720
 
721
+ log_event("Loading pipeline")
722
  _ensure_model_files()
723
 
724
  pipe = DiffusionPipeline.from_pretrained(
 
741
 
742
  _pipe = pipe
743
  _pipe_ready = True
744
+ log_event("Pipeline ready")
745
  return pipe
746
 
747
 
 
756
  seed: int,
757
  extra_details: str,
758
  ):
759
+ log_event(
760
+ f"Starting inference: size={getattr(image, 'size', None)}, "
761
+ f"steps={steps}, guidance={guidance_scale}, seed={seed}"
762
+ )
763
  pipe = _load_pipe()
764
  image = image.convert("RGB")
765
 
 
786
  generator=generator,
787
  )
788
 
789
+ log_event("Inference complete")
790
  return out.images[0]
791
 
792
 
 
799
  extra_details: str,
800
  ):
801
  if image is None:
802
+ log_event("Still image inference blocked: no image")
803
  raise gr.Error("⛏️ Please upload a photo first!")
804
 
805
+ log_event("Still image inference requested")
806
  return _minecraftify_image(image, steps, guidance_scale, seed, extra_details)
807
 
808
 
809
+ def capture_live_frame(image: Image.Image):
810
+ global _live_latest_frame, _live_latest_frame_id, _live_capture_count
811
+
812
+ if image is None:
813
+ return None
814
+
815
+ with _live_lock:
816
+ _live_latest_frame = image.copy()
817
+ _live_latest_frame_id += 1
818
+ _live_capture_count += 1
819
+ frame_id = _live_latest_frame_id
820
+ capture_count = _live_capture_count
821
+
822
+ if capture_count == 1 or capture_count % 30 == 0:
823
+ log_event(f"Captured live webcam frame id={frame_id}, size={getattr(image, 'size', None)}")
824
+
825
+ return None
826
+
827
+
828
  @spaces.GPU(duration=120)
829
+ def minecraftify_live_loop(
 
 
830
  steps: int,
831
  guidance_scale: float,
832
  seed: int,
833
  extra_details: str,
834
  ):
835
+ log_event("Live GPU loop started")
836
+ last_processed_frame_id = 0
837
 
838
+ while True:
839
+ with _live_lock:
840
+ is_active = _live_active
841
+ frame = _live_latest_frame.copy() if _live_latest_frame is not None else None
842
+ frame_id = _live_latest_frame_id
843
+
844
+ if not is_active:
845
+ log_event("Live GPU loop stopped")
846
+ yield gr.skip(), "Live camera is stopped."
847
+ return
848
+
849
+ if frame is None:
850
+ time.sleep(0.1)
851
+ continue
852
+
853
+ if frame_id == last_processed_frame_id:
854
+ time.sleep(0.02)
855
+ continue
856
+
857
+ last_processed_frame_id = frame_id
858
+ log_event(f"Processing newest live frame id={frame_id}")
859
+ output = _minecraftify_image(frame, steps, guidance_scale, seed, extra_details)
860
+ yield output, f"Live camera is running. Processed frame {frame_id}."
861
 
862
 
863
  def switch_input_mode(mode: str):
864
+ global _live_active
865
+
866
+ log_event(f"Input mode changed: {mode}")
867
  image_mode = mode == IMAGE_MODE
868
+ if image_mode:
869
+ _live_active = False
870
+
871
  return (
872
  gr.update(visible=image_mode),
873
  gr.update(visible=image_mode),
 
875
  gr.update(interactive=True),
876
  gr.update(visible=not image_mode, interactive=False),
877
  False,
878
+ "Live camera is stopped.",
879
  )
880
 
881
 
882
+ def start_live(image: Image.Image):
883
+ global _live_active
884
+
885
+ with _live_lock:
886
+ has_captured_frame = _live_latest_frame is not None
887
+
888
+ if not has_captured_frame:
889
+ log_event("Start live blocked: webcam is not on or no streamed frame was captured")
890
+ raise gr.Error("📷 Webcam is not streaming yet. Click Record and wait for the preview before starting live.")
891
+
892
+ _live_active = True
893
+ log_event(f"Live mode started with webcam frame size={getattr(image, 'size', None)}")
894
+ return (
895
+ True,
896
+ gr.update(interactive=False),
897
+ gr.update(interactive=True),
898
+ "Live camera is running. Minecraft frames will appear on the right.",
899
+ )
900
 
901
 
902
  def stop_live():
903
+ global _live_active
904
+
905
+ _live_active = False
906
+ log_event("Live mode stopped")
907
+ return (
908
+ False,
909
+ gr.update(interactive=True),
910
+ gr.update(interactive=False),
911
+ "Live camera is stopped.",
912
+ )
913
 
914
 
915
  # ----------------------------------------------------------------------
916
  # UI
917
  # ----------------------------------------------------------------------
918
 
919
+ log_event("Building Gradio UI")
920
+
921
  with gr.Blocks(**BLOCKS_KWARGS) as demo:
922
 
923
  # ── Title Banner ──
 
978
  interactive=False,
979
  )
980
 
981
+ live_status = gr.Textbox(
982
+ label="Live status",
983
+ value="Live camera is stopped.",
984
+ interactive=False,
985
+ lines=1,
986
+ )
987
+
988
  extra_details = gr.Textbox(
989
  label="🗒️ Extra scene details (optional)",
990
  placeholder="e.g. 'this is a beach at sunset' or 'make the dog a wolf mob'...",
 
1068
  start_live_btn,
1069
  stop_live_btn,
1070
  live_running,
1071
+ live_status,
1072
  ],
1073
  queue=False,
1074
  )
 
1079
  outputs=output_image,
1080
  )
1081
 
1082
+ start_live_event = start_live_btn.click(
1083
  fn=start_live,
1084
+ inputs=live_image,
1085
+ outputs=[live_running, start_live_btn, stop_live_btn, live_status],
1086
  queue=False,
1087
  )
1088
 
1089
+ start_live_event.then(
1090
+ fn=minecraftify_live_loop,
1091
+ inputs=[steps, guidance_scale, seed, extra_details],
1092
+ outputs=[output_image, live_status],
1093
+ concurrency_limit=1,
1094
+ )
1095
+
1096
  stop_live_btn.click(
1097
  fn=stop_live,
1098
  inputs=None,
1099
+ outputs=[live_running, start_live_btn, stop_live_btn, live_status],
1100
  queue=False,
1101
  )
1102
 
1103
  live_image.stream(
1104
+ fn=capture_live_frame,
1105
+ inputs=live_image,
1106
+ outputs=None,
1107
+ stream_every=0.1,
1108
  trigger_mode="always_last",
1109
+ queue=False,
1110
  )
1111
 
1112
+ log_event("Launching Gradio app")
1113
  demo.queue(max_size=1).launch(**LAUNCH_KWARGS)