Commit ·
eecf206
1
Parent(s): 176f153
still initial commit
Browse files
app.py
CHANGED
|
@@ -7,12 +7,10 @@ from fastrtc import WebRTC, get_cloudflare_turn_credentials
|
|
| 7 |
|
| 8 |
# ── Modal Backend ───────────────────────────────────────────────────────────
|
| 9 |
try:
|
| 10 |
-
print("🚀 Connecting to Modal VoxelModel...")
|
| 11 |
VoxelModelCls = modal.Cls.from_name("flux-klein-voxel-backend", "VoxelModel")
|
| 12 |
voxel_backend = VoxelModelCls().process_frame
|
| 13 |
print("✅ Modal Cls connected.")
|
| 14 |
except Exception as e:
|
| 15 |
-
print(f"⚠️ Modal Cls failed: {e}. Trying Function fallback...")
|
| 16 |
try:
|
| 17 |
voxel_backend = modal.Function.from_name("flux-klein-voxel-backend", "demo_stream_frame")
|
| 18 |
print("✅ Modal Function connected.")
|
|
@@ -63,7 +61,6 @@ def process_demo_stream(frame: np.ndarray) -> np.ndarray:
|
|
| 63 |
if voxel_backend is None:
|
| 64 |
err = _offline_frame(frame, "Backend Offline")
|
| 65 |
return np.hstack([frame, err])
|
| 66 |
-
|
| 67 |
processed = _run_voxel_backend(
|
| 68 |
frame,
|
| 69 |
prompt="vanilla minecraft voxel landscape, 3d blocky style, cube aesthetic",
|
|
@@ -71,42 +68,25 @@ def process_demo_stream(frame: np.ndarray) -> np.ndarray:
|
|
| 71 |
)
|
| 72 |
if processed.shape[0] != frame.shape[0]:
|
| 73 |
processed = cv2.resize(processed, (frame.shape[1], frame.shape[0]))
|
| 74 |
-
|
| 75 |
raw_labeled = frame.copy()
|
| 76 |
-
cv2.putText(raw_labeled, "RAW", (10, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,255,255), 2)
|
| 77 |
-
cv2.putText(processed, "MINECRAFT", (10, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255,255,255), 2)
|
| 78 |
return np.hstack([raw_labeled, processed])
|
| 79 |
|
| 80 |
|
| 81 |
-
# ──
|
|
|
|
|
|
|
| 82 |
css = """
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
max-height:
|
| 86 |
-
height: 420px !important;
|
| 87 |
overflow: hidden !important;
|
| 88 |
}
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
height: 420px !important;
|
| 94 |
-
max-height: 420px !important;
|
| 95 |
-
object-fit: contain !important; /* letterbox, never crop or overflow */
|
| 96 |
-
background: #000;
|
| 97 |
-
}
|
| 98 |
-
|
| 99 |
-
/* Gradio wraps WebRTC in .webrtc-container — constrain that too */
|
| 100 |
-
#webrtc-single .webrtc-container,
|
| 101 |
-
#webrtc-demo .webrtc-container {
|
| 102 |
-
max-height: 420px !important;
|
| 103 |
-
overflow: hidden !important;
|
| 104 |
-
}
|
| 105 |
-
|
| 106 |
-
/* Prevent the right column from stretching taller than controls */
|
| 107 |
-
.viewport-col {
|
| 108 |
-
max-height: 520px !important;
|
| 109 |
-
overflow: hidden !important;
|
| 110 |
}
|
| 111 |
"""
|
| 112 |
|
|
@@ -123,7 +103,7 @@ with gr.Blocks(title="Minecraft Spatial Voxel Filter", css=css) as demo:
|
|
| 123 |
interactive=True,
|
| 124 |
)
|
| 125 |
|
| 126 |
-
with gr.Row(
|
| 127 |
|
| 128 |
# ── Left: Controls ─────────────────────────────────────────────────
|
| 129 |
with gr.Column(scale=1, min_width=280):
|
|
@@ -142,29 +122,28 @@ with gr.Blocks(title="Minecraft Spatial Voxel Filter", css=css) as demo:
|
|
| 142 |
gr.Markdown(f"**Modal Backend:** {status_color} `{status_text}`")
|
| 143 |
gr.Markdown("_Controls only apply in Minecraft Filter mode._")
|
| 144 |
|
| 145 |
-
# ── Right: Viewports
|
| 146 |
-
|
|
|
|
| 147 |
|
| 148 |
-
|
| 149 |
-
with gr.Group(visible=True) as minecraft_layout:
|
| 150 |
gr.Markdown("### 📺 Spatial Render Pipeline")
|
| 151 |
webrtc_single = WebRTC(
|
| 152 |
label="Live Voxel Viewport",
|
| 153 |
modality="video",
|
| 154 |
mode="send-receive",
|
| 155 |
rtc_configuration=get_cloudflare_turn_credentials,
|
| 156 |
-
|
| 157 |
)
|
| 158 |
|
| 159 |
-
|
| 160 |
-
with gr.Group(visible=False) as demo_layout:
|
| 161 |
gr.Markdown("### 📺 Dual-Feed · _Left: Raw · Right: Minecraft_")
|
| 162 |
webrtc_demo = WebRTC(
|
| 163 |
label="Live Side-by-Side Feed",
|
| 164 |
modality="video",
|
| 165 |
mode="send-receive",
|
| 166 |
rtc_configuration=get_cloudflare_turn_credentials,
|
| 167 |
-
|
| 168 |
)
|
| 169 |
|
| 170 |
# ── Mode Switch ──────────────────────────────────────────────────────────
|
|
|
|
| 7 |
|
| 8 |
# ── Modal Backend ───────────────────────────────────────────────────────────
|
| 9 |
try:
|
|
|
|
| 10 |
VoxelModelCls = modal.Cls.from_name("flux-klein-voxel-backend", "VoxelModel")
|
| 11 |
voxel_backend = VoxelModelCls().process_frame
|
| 12 |
print("✅ Modal Cls connected.")
|
| 13 |
except Exception as e:
|
|
|
|
| 14 |
try:
|
| 15 |
voxel_backend = modal.Function.from_name("flux-klein-voxel-backend", "demo_stream_frame")
|
| 16 |
print("✅ Modal Function connected.")
|
|
|
|
| 61 |
if voxel_backend is None:
|
| 62 |
err = _offline_frame(frame, "Backend Offline")
|
| 63 |
return np.hstack([frame, err])
|
|
|
|
| 64 |
processed = _run_voxel_backend(
|
| 65 |
frame,
|
| 66 |
prompt="vanilla minecraft voxel landscape, 3d blocky style, cube aesthetic",
|
|
|
|
| 68 |
)
|
| 69 |
if processed.shape[0] != frame.shape[0]:
|
| 70 |
processed = cv2.resize(processed, (frame.shape[1], frame.shape[0]))
|
|
|
|
| 71 |
raw_labeled = frame.copy()
|
| 72 |
+
cv2.putText(raw_labeled, "RAW", (10, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)
|
| 73 |
+
cv2.putText(processed, "MINECRAFT", (10, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)
|
| 74 |
return np.hstack([raw_labeled, processed])
|
| 75 |
|
| 76 |
|
| 77 |
+
# ── THE FIX: from official FastRTC docs ────────────────────────────────────
|
| 78 |
+
# Use elem_classes on gr.Group + gr.Column, not elem_id CSS targeting
|
| 79 |
+
# Use height= directly on WebRTC() — the component accepts it natively
|
| 80 |
css = """
|
| 81 |
+
.webrtc-group {
|
| 82 |
+
max-width: 100% !important;
|
| 83 |
+
max-height: 480px !important;
|
|
|
|
| 84 |
overflow: hidden !important;
|
| 85 |
}
|
| 86 |
+
.webrtc-col {
|
| 87 |
+
display: flex !important;
|
| 88 |
+
justify-content: center !important;
|
| 89 |
+
align-items: flex-start !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
}
|
| 91 |
"""
|
| 92 |
|
|
|
|
| 103 |
interactive=True,
|
| 104 |
)
|
| 105 |
|
| 106 |
+
with gr.Row():
|
| 107 |
|
| 108 |
# ── Left: Controls ─────────────────────────────────────────────────
|
| 109 |
with gr.Column(scale=1, min_width=280):
|
|
|
|
| 122 |
gr.Markdown(f"**Modal Backend:** {status_color} `{status_text}`")
|
| 123 |
gr.Markdown("_Controls only apply in Minecraft Filter mode._")
|
| 124 |
|
| 125 |
+
# ── Right: Viewports ───────────────────────────────────────────────
|
| 126 |
+
# ✅ OFFICIAL PATTERN: elem_classes on Column + Group, height= on WebRTC
|
| 127 |
+
with gr.Column(scale=2, elem_classes=["webrtc-col"]):
|
| 128 |
|
| 129 |
+
with gr.Group(visible=True, elem_classes=["webrtc-group"]) as minecraft_layout:
|
|
|
|
| 130 |
gr.Markdown("### 📺 Spatial Render Pipeline")
|
| 131 |
webrtc_single = WebRTC(
|
| 132 |
label="Live Voxel Viewport",
|
| 133 |
modality="video",
|
| 134 |
mode="send-receive",
|
| 135 |
rtc_configuration=get_cloudflare_turn_credentials,
|
| 136 |
+
height=440, # ← native height param, docs-official fix
|
| 137 |
)
|
| 138 |
|
| 139 |
+
with gr.Group(visible=False, elem_classes=["webrtc-group"]) as demo_layout:
|
|
|
|
| 140 |
gr.Markdown("### 📺 Dual-Feed · _Left: Raw · Right: Minecraft_")
|
| 141 |
webrtc_demo = WebRTC(
|
| 142 |
label="Live Side-by-Side Feed",
|
| 143 |
modality="video",
|
| 144 |
mode="send-receive",
|
| 145 |
rtc_configuration=get_cloudflare_turn_credentials,
|
| 146 |
+
height=440, # ← same
|
| 147 |
)
|
| 148 |
|
| 149 |
# ── Mode Switch ──────────────────────────────────────────────────────────
|