Fumiya Imazato Claude Opus 4.5 commited on
Commit ·
63ce202
1
Parent(s): b4886e5
Fix: restore gr.Blocks + WebRTC, add prevention checklist
Browse files- Reverted from Stream.ui.launch() to gr.Blocks + WebRTC
- Fixed track_constraints structure with "video" key
- Enhanced CSS for footer hiding
- Added prevention checklist to TROUBLESHOOTING.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- TROUBLESHOOTING.md +40 -0
- app.py +55 -8
TROUBLESHOOTING.md
CHANGED
|
@@ -739,3 +739,43 @@ Dockerfile でビルド時に事前ダウンロード:
|
|
| 739 |
```dockerfile
|
| 740 |
RUN python -c "from paddleocr import PaddleOCR; PaddleOCR(use_angle_cls=True, lang='japan')" || true
|
| 741 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 739 |
```dockerfile
|
| 740 |
RUN python -c "from paddleocr import PaddleOCR; PaddleOCR(use_angle_cls=True, lang='japan')" || true
|
| 741 |
```
|
| 742 |
+
|
| 743 |
+
## 再発防止チェックリスト
|
| 744 |
+
|
| 745 |
+
### 必須確認事項(コード変更時に必ずチェック)
|
| 746 |
+
|
| 747 |
+
1. **外カメラ設定**
|
| 748 |
+
- `track_constraints` に `"facingMode": {"exact": "environment"}` が含まれているか
|
| 749 |
+
- `track_constraints` の構造が正しいか(`{"video": {...}}` 形式)
|
| 750 |
+
|
| 751 |
+
2. **フッター非表示**
|
| 752 |
+
- `CUSTOM_CSS` に `footer { display: none !important; }` が含まれているか
|
| 753 |
+
- `gr.Blocks()` に `css=CUSTOM_CSS` が渡されているか
|
| 754 |
+
|
| 755 |
+
3. **ボタンラベル**
|
| 756 |
+
- `button_labels` に日本語ではなく英語("Start", "Stop")が設定されているか
|
| 757 |
+
- "録音" という文字列がコード内に存在しないか
|
| 758 |
+
|
| 759 |
+
4. **Stream.ui.launch() は使わない**
|
| 760 |
+
- `Stream.ui.launch()` はデフォルトUIを使うため、カスタマイズ不可
|
| 761 |
+
- 必ず `gr.Blocks()` + `WebRTC` コンポーネントを使用すること
|
| 762 |
+
|
| 763 |
+
### track_constraints の正しい形式
|
| 764 |
+
|
| 765 |
+
```python
|
| 766 |
+
# 正しい形式
|
| 767 |
+
TRACK_CONSTRAINTS = {
|
| 768 |
+
"video": {
|
| 769 |
+
"width": {"ideal": 1280},
|
| 770 |
+
"height": {"ideal": 720},
|
| 771 |
+
"frameRate": {"ideal": 15},
|
| 772 |
+
"facingMode": {"exact": "environment"},
|
| 773 |
+
}
|
| 774 |
+
}
|
| 775 |
+
|
| 776 |
+
# 間違った形式(videoキーがない)
|
| 777 |
+
TRACK_CONSTRAINTS = {
|
| 778 |
+
"width": {"ideal": 1280},
|
| 779 |
+
"facingMode": {"exact": "environment"},
|
| 780 |
+
}
|
| 781 |
+
```
|
app.py
CHANGED
|
@@ -5,7 +5,8 @@ dokoCame - Real-time video location identification service
|
|
| 5 |
from typing import Optional
|
| 6 |
import os
|
| 7 |
import numpy as np
|
| 8 |
-
|
|
|
|
| 9 |
import cv2
|
| 10 |
|
| 11 |
from config.settings import settings
|
|
@@ -153,20 +154,66 @@ def process_video_frame(frame: np.ndarray) -> np.ndarray:
|
|
| 153 |
return frame
|
| 154 |
|
| 155 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
if __name__ == "__main__":
|
| 157 |
if not settings.validate():
|
| 158 |
print("Warning: GEMINI_API_KEY required for VLM")
|
| 159 |
|
| 160 |
rtc_config = get_cloudflare_turn_credentials if os.getenv("HF_TOKEN") else None
|
| 161 |
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
-
|
| 170 |
server_name="0.0.0.0",
|
| 171 |
server_port=7860,
|
| 172 |
share=False,
|
|
|
|
| 5 |
from typing import Optional
|
| 6 |
import os
|
| 7 |
import numpy as np
|
| 8 |
+
import gradio as gr
|
| 9 |
+
from fastrtc import WebRTC, get_cloudflare_turn_credentials
|
| 10 |
import cv2
|
| 11 |
|
| 12 |
from config.settings import settings
|
|
|
|
| 154 |
return frame
|
| 155 |
|
| 156 |
|
| 157 |
+
# ========== 設定 ==========
|
| 158 |
+
# 外カメラ強制
|
| 159 |
+
TRACK_CONSTRAINTS = {
|
| 160 |
+
"video": {
|
| 161 |
+
"width": {"ideal": 1280},
|
| 162 |
+
"height": {"ideal": 720},
|
| 163 |
+
"frameRate": {"ideal": 15},
|
| 164 |
+
"facingMode": {"exact": "environment"},
|
| 165 |
+
}
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
# フッター非表示CSS
|
| 169 |
+
CUSTOM_CSS = """
|
| 170 |
+
.gradio-container { max-width: 100% !important; padding: 0 !important; }
|
| 171 |
+
footer { display: none !important; visibility: hidden !important; height: 0 !important; }
|
| 172 |
+
.built-with { display: none !important; }
|
| 173 |
+
.svelte-1rjryqp { display: none !important; }
|
| 174 |
+
#footer { display: none !important; }
|
| 175 |
+
.footer { display: none !important; }
|
| 176 |
+
.gr-footer { display: none !important; }
|
| 177 |
+
[class*="footer"] { display: none !important; }
|
| 178 |
+
"""
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
# ========== メイン ==========
|
| 182 |
if __name__ == "__main__":
|
| 183 |
if not settings.validate():
|
| 184 |
print("Warning: GEMINI_API_KEY required for VLM")
|
| 185 |
|
| 186 |
rtc_config = get_cloudflare_turn_credentials if os.getenv("HF_TOKEN") else None
|
| 187 |
|
| 188 |
+
with gr.Blocks(
|
| 189 |
+
title="dokoCame",
|
| 190 |
+
css=CUSTOM_CSS,
|
| 191 |
+
theme=gr.themes.Default(),
|
| 192 |
+
) as demo:
|
| 193 |
+
gr.Markdown("# dokoCame")
|
| 194 |
+
|
| 195 |
+
webrtc = WebRTC(
|
| 196 |
+
label="Camera",
|
| 197 |
+
mode="send-receive",
|
| 198 |
+
modality="video",
|
| 199 |
+
track_constraints=TRACK_CONSTRAINTS,
|
| 200 |
+
rtc_configuration=rtc_config,
|
| 201 |
+
icon_button_color="#4CAF50",
|
| 202 |
+
button_labels={
|
| 203 |
+
"start": "Start",
|
| 204 |
+
"stop": "Stop",
|
| 205 |
+
"waiting": "...",
|
| 206 |
+
},
|
| 207 |
+
)
|
| 208 |
+
|
| 209 |
+
webrtc.stream(
|
| 210 |
+
fn=process_video_frame,
|
| 211 |
+
inputs=[webrtc],
|
| 212 |
+
outputs=[webrtc],
|
| 213 |
+
time_limit=None,
|
| 214 |
+
)
|
| 215 |
|
| 216 |
+
demo.launch(
|
| 217 |
server_name="0.0.0.0",
|
| 218 |
server_port=7860,
|
| 219 |
share=False,
|