flashstudio 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of flashstudio might be problematic. Click here for more details.
- {flashstudio-0.2.0 → flashstudio-0.2.1}/PKG-INFO +1 -1
- flashstudio-0.2.1/flashstudio/launcher.py +288 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio.egg-info/PKG-INFO +1 -1
- {flashstudio-0.2.0 → flashstudio-0.2.1}/pyproject.toml +1 -1
- flashstudio-0.2.0/flashstudio/launcher.py +0 -183
- {flashstudio-0.2.0 → flashstudio-0.2.1}/README.md +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/README_PYPI.md +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/app.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/cli.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/components/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/components/project_manager.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/components/sidebar.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/components/styles.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/components/wizard.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/components/zone_drawer/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/constants.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/core/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/core/constants.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/_common.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/metrics.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/pipeline_status.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/project_banner.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/quick_actions.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/recent_runs.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/page.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/_common.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/download/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/download/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/helpers.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/page.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/preview/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/preview/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/upload/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/upload/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/verify/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/data/verify/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/export/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/export/page.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/export/weights/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/export/weights/exporter.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/export/weights/settings.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/_common.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/data/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/data/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/model/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/model/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/page.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/run/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/run/detection.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/run/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/run/video.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/solution/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/inference/solution/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/_common.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/advanced/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/advanced/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/architecture/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/architecture/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/augment/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/augment/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/hyperparams/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/hyperparams/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/page.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/utils/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/model/utils/config.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/_common.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/launch/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/launch/controls.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/launch/dialogs.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/launch/preflight.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/launch/runner.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/launch/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/checkpoints.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/curves.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/dashboard.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/gt_verification.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/log_viewer.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/parsers.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/run_meta.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/tab.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/visualizations.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/page.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/utils/__init__.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/utils/config.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/utils/device.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/utils/filesystem.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/utils/jobs.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/utils/training_hooks.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio.egg-info/SOURCES.txt +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio.egg-info/dependency_links.txt +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio.egg-info/entry_points.txt +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio.egg-info/requires.txt +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio.egg-info/top_level.txt +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/setup.cfg +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/tests/test_app.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/tests/test_cli.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/tests/test_constants.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/tests/test_launcher.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/tests/test_pipeline_flow.py +0 -0
- {flashstudio-0.2.0 → flashstudio-0.2.1}/tests/test_save_dir_compat.py +0 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Launch FlashStudio UI — works locally and inside Google Colab."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import time as _time
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from flashstudio.utils.device import is_colab
|
|
11
|
+
|
|
12
|
+
_STREAMLIT_CONFIG = """\
|
|
13
|
+
[theme]
|
|
14
|
+
primaryColor = "#7C3AED"
|
|
15
|
+
backgroundColor = "#FFFFFF"
|
|
16
|
+
secondaryBackgroundColor = "#F8F9FB"
|
|
17
|
+
textColor = "#1A1A2E"
|
|
18
|
+
font = "sans serif"
|
|
19
|
+
|
|
20
|
+
[server]
|
|
21
|
+
headless = true
|
|
22
|
+
port = 8501
|
|
23
|
+
enableCORS = false
|
|
24
|
+
enableXsrfProtection = false
|
|
25
|
+
|
|
26
|
+
[client]
|
|
27
|
+
showSidebarNavigation = false
|
|
28
|
+
|
|
29
|
+
[browser]
|
|
30
|
+
gatherUsageStats = false
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_app_path() -> str:
|
|
35
|
+
return str(Path(__file__).parent / "app.py")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _ensure_streamlit_config():
|
|
39
|
+
"""Write the default Streamlit config if one doesn't already exist."""
|
|
40
|
+
config_dir = Path.home() / ".streamlit"
|
|
41
|
+
config_file = config_dir / "config.toml"
|
|
42
|
+
if config_file.exists():
|
|
43
|
+
return
|
|
44
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
config_file.write_text(_STREAMLIT_CONFIG)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def launch(port: int = 8501, share: bool = True, ngrok_token: str | None = None):
|
|
49
|
+
"""Launch FlashStudio Streamlit app.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
port: Port to run the Streamlit server on.
|
|
53
|
+
share: If True and running in Colab, creates a public tunnel
|
|
54
|
+
(works on mobile too).
|
|
55
|
+
ngrok_token: Optional ngrok auth token. If not provided, uses
|
|
56
|
+
NGROK_TOKEN env var.
|
|
57
|
+
"""
|
|
58
|
+
_ensure_streamlit_config()
|
|
59
|
+
app_path = _get_app_path()
|
|
60
|
+
|
|
61
|
+
if is_colab():
|
|
62
|
+
return _launch_colab(app_path, port, share, ngrok_token)
|
|
63
|
+
else:
|
|
64
|
+
_launch_local(app_path, port)
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Tunnel helpers
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
def _start_cloudflared_tunnel(port: int) -> str | None:
|
|
73
|
+
"""Start a free Cloudflare tunnel (no sign-up required).
|
|
74
|
+
|
|
75
|
+
Returns the public https URL or None on failure.
|
|
76
|
+
"""
|
|
77
|
+
binary = "/usr/local/bin/cloudflared"
|
|
78
|
+
if not os.path.isfile(binary):
|
|
79
|
+
try:
|
|
80
|
+
subprocess.check_call(
|
|
81
|
+
["wget", "-q",
|
|
82
|
+
"https://github.com/cloudflare/cloudflared/releases/latest"
|
|
83
|
+
"/download/cloudflared-linux-amd64",
|
|
84
|
+
"-O", binary],
|
|
85
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
86
|
+
)
|
|
87
|
+
os.chmod(binary, 0o755)
|
|
88
|
+
except Exception:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
tunnel_proc = subprocess.Popen(
|
|
92
|
+
[binary, "tunnel", "--url", f"http://localhost:{port}"],
|
|
93
|
+
stdout=subprocess.PIPE,
|
|
94
|
+
stderr=subprocess.PIPE,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
url_re = re.compile(r"https://[a-z0-9-]+\.trycloudflare\.com")
|
|
98
|
+
deadline = _time.monotonic() + 20
|
|
99
|
+
while _time.monotonic() < deadline:
|
|
100
|
+
line = tunnel_proc.stderr.readline().decode("utf-8", errors="replace")
|
|
101
|
+
if not line:
|
|
102
|
+
if tunnel_proc.poll() is not None:
|
|
103
|
+
return None
|
|
104
|
+
_time.sleep(0.2)
|
|
105
|
+
continue
|
|
106
|
+
match = url_re.search(line)
|
|
107
|
+
if match:
|
|
108
|
+
return match.group(0)
|
|
109
|
+
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _start_ngrok_tunnel(port: int, ngrok_token: str) -> str | None:
|
|
114
|
+
"""Start an ngrok tunnel. Requires an auth token."""
|
|
115
|
+
try:
|
|
116
|
+
from pyngrok import ngrok, conf
|
|
117
|
+
except ImportError:
|
|
118
|
+
try:
|
|
119
|
+
subprocess.check_call(
|
|
120
|
+
[sys.executable, "-m", "pip", "install", "-q", "pyngrok"],
|
|
121
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
122
|
+
)
|
|
123
|
+
from pyngrok import ngrok, conf
|
|
124
|
+
except Exception:
|
|
125
|
+
return None
|
|
126
|
+
try:
|
|
127
|
+
conf.get_default().auth_token = ngrok_token
|
|
128
|
+
return str(ngrok.connect(port, "http"))
|
|
129
|
+
except Exception:
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _get_colab_proxy_url(port: int) -> str | None:
|
|
134
|
+
"""Get Colab's built-in proxy URL (only works in the same browser session)."""
|
|
135
|
+
try:
|
|
136
|
+
from google.colab.output import eval_js
|
|
137
|
+
result = eval_js(f"google.colab.kernel.proxyPort({port})")
|
|
138
|
+
if result and str(result).startswith("http"):
|
|
139
|
+
return str(result)
|
|
140
|
+
except Exception:
|
|
141
|
+
pass
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
# Colab launcher
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
def _launch_colab(app_path: str, port: int, share: bool, ngrok_token: str | None):
|
|
150
|
+
"""Launch in Google Colab with a public URL that works on any device."""
|
|
151
|
+
import urllib.request
|
|
152
|
+
|
|
153
|
+
print(" Starting FlashStudio...", end="", flush=True)
|
|
154
|
+
|
|
155
|
+
proc = subprocess.Popen(
|
|
156
|
+
[sys.executable, "-m", "streamlit", "run", app_path,
|
|
157
|
+
"--server.port", str(port),
|
|
158
|
+
"--server.headless", "true",
|
|
159
|
+
"--server.enableCORS", "false",
|
|
160
|
+
"--server.enableXsrfProtection", "false"],
|
|
161
|
+
stdout=subprocess.PIPE,
|
|
162
|
+
stderr=subprocess.PIPE,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
ready = False
|
|
166
|
+
for _ in range(30):
|
|
167
|
+
_time.sleep(1)
|
|
168
|
+
if proc.poll() is not None:
|
|
169
|
+
_, stderr = proc.communicate()
|
|
170
|
+
err_msg = (stderr.decode("utf-8", errors="replace")[-500:]
|
|
171
|
+
if stderr else "Unknown error")
|
|
172
|
+
print(f"\n\n FlashStudio failed to start:\n {err_msg}")
|
|
173
|
+
return proc
|
|
174
|
+
try:
|
|
175
|
+
urllib.request.urlopen(
|
|
176
|
+
f"http://localhost:{port}/_stcore/health", timeout=2)
|
|
177
|
+
ready = True
|
|
178
|
+
break
|
|
179
|
+
except Exception:
|
|
180
|
+
print(".", end="", flush=True)
|
|
181
|
+
|
|
182
|
+
if ready:
|
|
183
|
+
print(" Ready!")
|
|
184
|
+
else:
|
|
185
|
+
print(" (still loading, URL may take a moment)")
|
|
186
|
+
|
|
187
|
+
# --- Obtain a public URL ---
|
|
188
|
+
# Priority: cloudflared (free, works everywhere including mobile)
|
|
189
|
+
# > ngrok (needs token) > Colab proxy (desktop-only)
|
|
190
|
+
public_url = None
|
|
191
|
+
tunnel_type = None
|
|
192
|
+
|
|
193
|
+
if share:
|
|
194
|
+
# 1) Free Cloudflare tunnel — works on mobile, no sign-up
|
|
195
|
+
print(" Setting up public URL (works on mobile)...", end="", flush=True)
|
|
196
|
+
public_url = _start_cloudflared_tunnel(port)
|
|
197
|
+
if public_url:
|
|
198
|
+
tunnel_type = "cloudflared"
|
|
199
|
+
print(" Done!")
|
|
200
|
+
|
|
201
|
+
# 2) ngrok — if a token is available
|
|
202
|
+
if not public_url:
|
|
203
|
+
token = ngrok_token or os.environ.get("NGROK_TOKEN", "")
|
|
204
|
+
if token:
|
|
205
|
+
public_url = _start_ngrok_tunnel(port, token)
|
|
206
|
+
if public_url:
|
|
207
|
+
tunnel_type = "ngrok"
|
|
208
|
+
print(" Done!")
|
|
209
|
+
|
|
210
|
+
if not public_url:
|
|
211
|
+
print(" Skipped (using Colab proxy)")
|
|
212
|
+
|
|
213
|
+
# 3) Colab's built-in proxy (fallback — desktop browser only)
|
|
214
|
+
colab_proxy_url = _get_colab_proxy_url(port)
|
|
215
|
+
if not public_url:
|
|
216
|
+
public_url = colab_proxy_url
|
|
217
|
+
|
|
218
|
+
# --- Display results ---
|
|
219
|
+
print(f"\n{'=' * 60}")
|
|
220
|
+
print(" FlashStudio is running!")
|
|
221
|
+
if public_url:
|
|
222
|
+
print(f" Open: {public_url}")
|
|
223
|
+
if tunnel_type:
|
|
224
|
+
print(f" (works on mobile & any device)")
|
|
225
|
+
else:
|
|
226
|
+
print(f" ⚠ This URL only works in this browser.")
|
|
227
|
+
print(f" On mobile? Re-run with: launch(share=True)")
|
|
228
|
+
else:
|
|
229
|
+
print(f" Port: {port}")
|
|
230
|
+
print("")
|
|
231
|
+
print(" Run this in a new cell to get the URL:")
|
|
232
|
+
print(f" from google.colab.output import eval_js")
|
|
233
|
+
print(f" print(eval_js('google.colab.kernel.proxyPort({port})'))")
|
|
234
|
+
print(f"{'=' * 60}\n")
|
|
235
|
+
|
|
236
|
+
# Show clickable link
|
|
237
|
+
if public_url:
|
|
238
|
+
try:
|
|
239
|
+
from IPython.display import display, HTML
|
|
240
|
+
mobile_badge = ""
|
|
241
|
+
if tunnel_type:
|
|
242
|
+
mobile_badge = (
|
|
243
|
+
'<span style="display:inline-block;font-size:11px;'
|
|
244
|
+
'padding:2px 8px;background:#10B981;color:white;'
|
|
245
|
+
'border-radius:4px;margin-left:8px;vertical-align:middle;">'
|
|
246
|
+
'Mobile Ready</span>'
|
|
247
|
+
)
|
|
248
|
+
display(HTML(
|
|
249
|
+
f'<div style="margin:8px 0;">'
|
|
250
|
+
f'<a href="{public_url}" target="_blank" '
|
|
251
|
+
f'style="display:inline-block;font-size:16px;padding:10px 24px;'
|
|
252
|
+
f'background:#7C3AED;color:white;border-radius:8px;'
|
|
253
|
+
f'text-decoration:none;font-weight:600;">'
|
|
254
|
+
f'Open FlashStudio</a>{mobile_badge}</div>'
|
|
255
|
+
))
|
|
256
|
+
except Exception:
|
|
257
|
+
pass
|
|
258
|
+
|
|
259
|
+
# Embed Streamlit directly in the Colab cell output
|
|
260
|
+
try:
|
|
261
|
+
from google.colab import output
|
|
262
|
+
output.serve_kernel_port_as_iframe(port, height=800)
|
|
263
|
+
except Exception:
|
|
264
|
+
if not public_url:
|
|
265
|
+
try:
|
|
266
|
+
from IPython.display import display, HTML
|
|
267
|
+
display(HTML(
|
|
268
|
+
f'<div style="padding:12px;background:#FEF3C7;'
|
|
269
|
+
f'border-radius:8px;margin:8px 0;font-size:14px;">'
|
|
270
|
+
f'Run in a <b>new cell</b>:<br>'
|
|
271
|
+
f'<code style="background:#F3F4F6;padding:4px 8px;'
|
|
272
|
+
f'border-radius:4px;">'
|
|
273
|
+
f'from google.colab import output; '
|
|
274
|
+
f'output.serve_kernel_port_as_iframe({port}, height=800)'
|
|
275
|
+
f'</code></div>'
|
|
276
|
+
))
|
|
277
|
+
except Exception:
|
|
278
|
+
pass
|
|
279
|
+
|
|
280
|
+
return proc
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _launch_local(app_path: str, port: int):
|
|
284
|
+
"""Launch locally with streamlit."""
|
|
285
|
+
subprocess.run(
|
|
286
|
+
[sys.executable, "-m", "streamlit", "run", app_path,
|
|
287
|
+
"--server.port", str(port)],
|
|
288
|
+
)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "flashstudio"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Interactive Training & Inference UI for FlashDet — runs on Google Colab"
|
|
9
9
|
readme = "README_PYPI.md"
|
|
10
10
|
license = {text = "Apache-2.0"}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"""Launch FlashStudio UI — works locally and inside Google Colab."""
|
|
2
|
-
|
|
3
|
-
import subprocess
|
|
4
|
-
import sys
|
|
5
|
-
import os
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
from flashstudio.utils.device import is_colab
|
|
9
|
-
|
|
10
|
-
_STREAMLIT_CONFIG = """\
|
|
11
|
-
[theme]
|
|
12
|
-
primaryColor = "#7C3AED"
|
|
13
|
-
backgroundColor = "#FFFFFF"
|
|
14
|
-
secondaryBackgroundColor = "#F8F9FB"
|
|
15
|
-
textColor = "#1A1A2E"
|
|
16
|
-
font = "sans serif"
|
|
17
|
-
|
|
18
|
-
[server]
|
|
19
|
-
headless = true
|
|
20
|
-
port = 8501
|
|
21
|
-
enableCORS = false
|
|
22
|
-
enableXsrfProtection = false
|
|
23
|
-
|
|
24
|
-
[client]
|
|
25
|
-
showSidebarNavigation = false
|
|
26
|
-
|
|
27
|
-
[browser]
|
|
28
|
-
gatherUsageStats = false
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _get_app_path() -> str:
|
|
33
|
-
return str(Path(__file__).parent / "app.py")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _ensure_streamlit_config():
|
|
37
|
-
"""Write the default Streamlit config if one doesn't already exist."""
|
|
38
|
-
config_dir = Path.home() / ".streamlit"
|
|
39
|
-
config_file = config_dir / "config.toml"
|
|
40
|
-
if config_file.exists():
|
|
41
|
-
return
|
|
42
|
-
config_dir.mkdir(parents=True, exist_ok=True)
|
|
43
|
-
config_file.write_text(_STREAMLIT_CONFIG)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def launch(port: int = 8501, share: bool = True, ngrok_token: str | None = None):
|
|
47
|
-
"""Launch FlashStudio Streamlit app.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
port: Port to run the Streamlit server on.
|
|
51
|
-
share: If True and running in Colab, creates a public ngrok tunnel.
|
|
52
|
-
ngrok_token: Optional ngrok auth token. If not provided, uses NGROK_TOKEN env var.
|
|
53
|
-
"""
|
|
54
|
-
_ensure_streamlit_config()
|
|
55
|
-
app_path = _get_app_path()
|
|
56
|
-
|
|
57
|
-
if is_colab():
|
|
58
|
-
return _launch_colab(app_path, port, share, ngrok_token)
|
|
59
|
-
else:
|
|
60
|
-
_launch_local(app_path, port)
|
|
61
|
-
return None
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _launch_colab(app_path: str, port: int, share: bool, ngrok_token: str | None):
|
|
65
|
-
"""Launch in Google Colab with automatic tunnel detection."""
|
|
66
|
-
import time as _time
|
|
67
|
-
import urllib.request
|
|
68
|
-
import urllib.error
|
|
69
|
-
|
|
70
|
-
print(" Starting FlashStudio...", end="", flush=True)
|
|
71
|
-
|
|
72
|
-
proc = subprocess.Popen(
|
|
73
|
-
[sys.executable, "-m", "streamlit", "run", app_path,
|
|
74
|
-
"--server.port", str(port),
|
|
75
|
-
"--server.headless", "true",
|
|
76
|
-
"--server.enableCORS", "false",
|
|
77
|
-
"--server.enableXsrfProtection", "false"],
|
|
78
|
-
stdout=subprocess.PIPE,
|
|
79
|
-
stderr=subprocess.PIPE,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
# Wait until Streamlit is actually responding (up to 30s)
|
|
83
|
-
ready = False
|
|
84
|
-
for i in range(30):
|
|
85
|
-
_time.sleep(1)
|
|
86
|
-
if proc.poll() is not None:
|
|
87
|
-
_, stderr = proc.communicate()
|
|
88
|
-
err_msg = stderr.decode("utf-8", errors="replace")[-500:] if stderr else "Unknown error"
|
|
89
|
-
print(f"\n\n FlashStudio failed to start:\n {err_msg}")
|
|
90
|
-
return proc
|
|
91
|
-
try:
|
|
92
|
-
urllib.request.urlopen(f"http://localhost:{port}/_stcore/health", timeout=2)
|
|
93
|
-
ready = True
|
|
94
|
-
break
|
|
95
|
-
except Exception:
|
|
96
|
-
print(".", end="", flush=True)
|
|
97
|
-
|
|
98
|
-
if ready:
|
|
99
|
-
print(" Ready!")
|
|
100
|
-
else:
|
|
101
|
-
print(" (still loading, URL may take a moment)")
|
|
102
|
-
|
|
103
|
-
public_url = None
|
|
104
|
-
|
|
105
|
-
# Method 1: Colab's built-in proxy (simplest, no install needed)
|
|
106
|
-
try:
|
|
107
|
-
from google.colab.output import eval_js
|
|
108
|
-
result = eval_js(f"google.colab.kernel.proxyPort({port})")
|
|
109
|
-
if result and str(result).startswith("http"):
|
|
110
|
-
public_url = str(result)
|
|
111
|
-
except Exception:
|
|
112
|
-
pass
|
|
113
|
-
|
|
114
|
-
# Method 2: ngrok (only if token is provided)
|
|
115
|
-
if not public_url:
|
|
116
|
-
token = ngrok_token or os.environ.get("NGROK_TOKEN", "")
|
|
117
|
-
if token:
|
|
118
|
-
try:
|
|
119
|
-
from pyngrok import ngrok, conf
|
|
120
|
-
conf.get_default().auth_token = token
|
|
121
|
-
public_url = str(ngrok.connect(port, "http"))
|
|
122
|
-
except ImportError:
|
|
123
|
-
try:
|
|
124
|
-
subprocess.check_call(
|
|
125
|
-
[sys.executable, "-m", "pip", "install", "-q", "pyngrok"],
|
|
126
|
-
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
127
|
-
)
|
|
128
|
-
from pyngrok import ngrok, conf
|
|
129
|
-
conf.get_default().auth_token = token
|
|
130
|
-
public_url = str(ngrok.connect(port, "http"))
|
|
131
|
-
except Exception:
|
|
132
|
-
pass
|
|
133
|
-
except Exception:
|
|
134
|
-
pass
|
|
135
|
-
|
|
136
|
-
print(f"\n{'=' * 60}")
|
|
137
|
-
print(" FlashStudio is running!")
|
|
138
|
-
if public_url:
|
|
139
|
-
print(f" Open: {public_url}")
|
|
140
|
-
else:
|
|
141
|
-
print(f" Port: {port}")
|
|
142
|
-
print("")
|
|
143
|
-
print(" Run this in a new cell to get the URL:")
|
|
144
|
-
print(f" from google.colab.output import eval_js")
|
|
145
|
-
print(f" print(eval_js('google.colab.kernel.proxyPort({port})'))")
|
|
146
|
-
print(f"{'=' * 60}\n")
|
|
147
|
-
|
|
148
|
-
if public_url:
|
|
149
|
-
try:
|
|
150
|
-
from IPython.display import display, HTML
|
|
151
|
-
display(HTML(
|
|
152
|
-
f'<a href="{public_url}" target="_blank" '
|
|
153
|
-
f'style="display:inline-block;font-size:16px;padding:10px 24px;'
|
|
154
|
-
f'background:#7C3AED;color:white;border-radius:8px;'
|
|
155
|
-
f'text-decoration:none;font-weight:600;margin:8px 0;">'
|
|
156
|
-
f'Open FlashStudio</a>'
|
|
157
|
-
))
|
|
158
|
-
except Exception:
|
|
159
|
-
pass
|
|
160
|
-
else:
|
|
161
|
-
try:
|
|
162
|
-
from IPython.display import display, HTML
|
|
163
|
-
display(HTML(
|
|
164
|
-
f'<div style="padding:12px;background:#FEF3C7;border-radius:8px;'
|
|
165
|
-
f'margin:8px 0;font-size:14px;">'
|
|
166
|
-
f'Run in a <b>new cell</b>:<br>'
|
|
167
|
-
f'<code style="background:#F3F4F6;padding:4px 8px;border-radius:4px;">'
|
|
168
|
-
f'from google.colab.output import eval_js; '
|
|
169
|
-
f'print(eval_js("google.colab.kernel.proxyPort({port})"))</code>'
|
|
170
|
-
f'</div>'
|
|
171
|
-
))
|
|
172
|
-
except Exception:
|
|
173
|
-
pass
|
|
174
|
-
|
|
175
|
-
return proc
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def _launch_local(app_path: str, port: int):
|
|
179
|
-
"""Launch locally with streamlit."""
|
|
180
|
-
subprocess.run(
|
|
181
|
-
[sys.executable, "-m", "streamlit", "run", app_path,
|
|
182
|
-
"--server.port", str(port)],
|
|
183
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/pipeline_status.py
RENAMED
|
File without changes
|
{flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/project_banner.py
RENAMED
|
File without changes
|
{flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/quick_actions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/gt_verification.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{flashstudio-0.2.0 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/visualizations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|