Commit ·
6d33571
1
Parent(s): 23d298b
new update fix
Browse files
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
|
| 768 |
-
image: Image.Image,
|
| 769 |
-
is_running: bool,
|
| 770 |
steps: int,
|
| 771 |
guidance_scale: float,
|
| 772 |
seed: int,
|
| 773 |
extra_details: str,
|
| 774 |
):
|
| 775 |
-
|
| 776 |
-
|
| 777 |
|
| 778 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 795 |
|
| 796 |
|
| 797 |
def stop_live():
|
| 798 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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=
|
| 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=
|
| 974 |
-
inputs=
|
| 975 |
-
outputs=
|
| 976 |
-
stream_every=0.
|
| 977 |
trigger_mode="always_last",
|
| 978 |
-
|
| 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)
|