flashstudio 0.1.9__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.

Files changed (110) hide show
  1. {flashstudio-0.1.9 → flashstudio-0.2.1}/PKG-INFO +1 -1
  2. {flashstudio-0.1.9 → flashstudio-0.2.1}/README.md +15 -13
  3. flashstudio-0.2.1/flashstudio/launcher.py +288 -0
  4. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio.egg-info/PKG-INFO +1 -1
  5. {flashstudio-0.1.9 → flashstudio-0.2.1}/pyproject.toml +1 -1
  6. flashstudio-0.1.9/flashstudio/launcher.py +0 -158
  7. {flashstudio-0.1.9 → flashstudio-0.2.1}/README_PYPI.md +0 -0
  8. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/__init__.py +0 -0
  9. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/app.py +0 -0
  10. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/cli.py +0 -0
  11. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/components/__init__.py +0 -0
  12. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/components/project_manager.py +0 -0
  13. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/components/sidebar.py +0 -0
  14. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/components/styles.py +0 -0
  15. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/components/wizard.py +0 -0
  16. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/components/zone_drawer/__init__.py +0 -0
  17. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/constants.py +0 -0
  18. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/core/__init__.py +0 -0
  19. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/core/constants.py +0 -0
  20. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/__init__.py +0 -0
  21. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/__init__.py +0 -0
  22. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/_common.py +0 -0
  23. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/__init__.py +0 -0
  24. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/metrics.py +0 -0
  25. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/pipeline_status.py +0 -0
  26. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/project_banner.py +0 -0
  27. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/quick_actions.py +0 -0
  28. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/recent_runs.py +0 -0
  29. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/overview/tab.py +0 -0
  30. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/dashboard/page.py +0 -0
  31. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/__init__.py +0 -0
  32. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/_common.py +0 -0
  33. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/download/__init__.py +0 -0
  34. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/download/tab.py +0 -0
  35. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/helpers.py +0 -0
  36. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/page.py +0 -0
  37. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/preview/__init__.py +0 -0
  38. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/preview/tab.py +0 -0
  39. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/upload/__init__.py +0 -0
  40. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/upload/tab.py +0 -0
  41. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/verify/__init__.py +0 -0
  42. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/data/verify/tab.py +0 -0
  43. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/export/__init__.py +0 -0
  44. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/export/page.py +0 -0
  45. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/export/weights/__init__.py +0 -0
  46. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/export/weights/exporter.py +0 -0
  47. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/export/weights/settings.py +0 -0
  48. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/__init__.py +0 -0
  49. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/_common.py +0 -0
  50. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/data/__init__.py +0 -0
  51. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/data/tab.py +0 -0
  52. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/model/__init__.py +0 -0
  53. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/model/tab.py +0 -0
  54. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/page.py +0 -0
  55. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/run/__init__.py +0 -0
  56. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/run/detection.py +0 -0
  57. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/run/tab.py +0 -0
  58. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/run/video.py +0 -0
  59. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/solution/__init__.py +0 -0
  60. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/inference/solution/tab.py +0 -0
  61. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/__init__.py +0 -0
  62. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/_common.py +0 -0
  63. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/advanced/__init__.py +0 -0
  64. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/advanced/tab.py +0 -0
  65. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/architecture/__init__.py +0 -0
  66. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/architecture/tab.py +0 -0
  67. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/augment/__init__.py +0 -0
  68. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/augment/tab.py +0 -0
  69. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/hyperparams/__init__.py +0 -0
  70. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/hyperparams/tab.py +0 -0
  71. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/page.py +0 -0
  72. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/utils/__init__.py +0 -0
  73. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/model/utils/config.py +0 -0
  74. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/__init__.py +0 -0
  75. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/_common.py +0 -0
  76. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/launch/__init__.py +0 -0
  77. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/launch/controls.py +0 -0
  78. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/launch/dialogs.py +0 -0
  79. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/launch/preflight.py +0 -0
  80. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/launch/runner.py +0 -0
  81. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/launch/tab.py +0 -0
  82. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/__init__.py +0 -0
  83. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/checkpoints.py +0 -0
  84. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/curves.py +0 -0
  85. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/dashboard.py +0 -0
  86. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/gt_verification.py +0 -0
  87. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/log_viewer.py +0 -0
  88. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/parsers.py +0 -0
  89. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/run_meta.py +0 -0
  90. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/tab.py +0 -0
  91. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/monitor/visualizations.py +0 -0
  92. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/pages/training/page.py +0 -0
  93. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/utils/__init__.py +0 -0
  94. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/utils/config.py +0 -0
  95. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/utils/device.py +0 -0
  96. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/utils/filesystem.py +0 -0
  97. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/utils/jobs.py +0 -0
  98. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio/utils/training_hooks.py +0 -0
  99. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio.egg-info/SOURCES.txt +0 -0
  100. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio.egg-info/dependency_links.txt +0 -0
  101. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio.egg-info/entry_points.txt +0 -0
  102. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio.egg-info/requires.txt +0 -0
  103. {flashstudio-0.1.9 → flashstudio-0.2.1}/flashstudio.egg-info/top_level.txt +0 -0
  104. {flashstudio-0.1.9 → flashstudio-0.2.1}/setup.cfg +0 -0
  105. {flashstudio-0.1.9 → flashstudio-0.2.1}/tests/test_app.py +0 -0
  106. {flashstudio-0.1.9 → flashstudio-0.2.1}/tests/test_cli.py +0 -0
  107. {flashstudio-0.1.9 → flashstudio-0.2.1}/tests/test_constants.py +0 -0
  108. {flashstudio-0.1.9 → flashstudio-0.2.1}/tests/test_launcher.py +0 -0
  109. {flashstudio-0.1.9 → flashstudio-0.2.1}/tests/test_pipeline_flow.py +0 -0
  110. {flashstudio-0.1.9 → flashstudio-0.2.1}/tests/test_save_dir_compat.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flashstudio
3
- Version: 0.1.9
3
+ Version: 0.2.1
4
4
  Summary: Interactive Training & Inference UI for FlashDet — runs on Google Colab
5
5
  Author: FlashVision
6
6
  License: Apache-2.0
@@ -287,33 +287,35 @@ FlashStudio/
287
287
 
288
288
  ## Troubleshooting
289
289
 
290
- ### Colab: "localhost can't be reached"
290
+ ### Colab: "localhost can't be reached" or ngrok error
291
291
 
292
- This means the tunnel didn't connect. Try running in a new cell:
292
+ `launch()` uses Colab's built-in proxy automatically. If it doesn't work, run this in a **new cell**:
293
293
 
294
294
  ```python
295
+ import subprocess, sys, time
296
+
297
+ proc = subprocess.Popen(
298
+ [sys.executable, "-m", "streamlit", "run",
299
+ __import__('flashstudio.launcher', fromlist=['_get_app_path'])._get_app_path(),
300
+ "--server.port", "8501", "--server.headless", "true",
301
+ "--server.enableCORS", "false", "--server.enableXsrfProtection", "false"],
302
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE
303
+ )
304
+ time.sleep(5)
305
+
295
306
  from google.colab.output import eval_js
296
- print(eval_js('google.colab.kernel.proxyPort(8501)'))
307
+ print(eval_js("google.colab.kernel.proxyPort(8501)"))
297
308
  ```
298
309
 
299
- Open the printed URL in a new tab.
310
+ Open the printed URL in a new tab. No token or signup needed.
300
311
 
301
312
  ### Streamlit port already in use
302
313
 
303
314
  ```bash
304
- # Kill existing Streamlit processes
305
315
  pkill -f "streamlit run"
306
-
307
- # Then restart
308
316
  flashstudio --port 8501
309
317
  ```
310
318
 
311
- ### `ModuleNotFoundError: No module named 'flashdet'`
312
-
313
- ```bash
314
- pip install flashdet
315
- ```
316
-
317
319
  ---
318
320
 
319
321
  ## Requirements
@@ -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
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flashstudio
3
- Version: 0.1.9
3
+ Version: 0.2.1
4
4
  Summary: Interactive Training & Inference UI for FlashDet — runs on Google Colab
5
5
  Author: FlashVision
6
6
  License: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flashstudio"
7
- version = "0.1.9"
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,158 +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
- _launch_colab(app_path, port, share, ngrok_token)
59
- else:
60
- _launch_local(app_path, port)
61
-
62
-
63
- def _launch_colab(app_path: str, port: int, share: bool, ngrok_token: str | None):
64
- """Launch in Google Colab with automatic tunnel detection."""
65
- import time as _time
66
-
67
- proc = subprocess.Popen(
68
- [sys.executable, "-m", "streamlit", "run", app_path,
69
- "--server.port", str(port),
70
- "--server.headless", "true",
71
- "--server.enableCORS", "false",
72
- "--server.enableXsrfProtection", "false"],
73
- stdout=subprocess.PIPE,
74
- stderr=subprocess.PIPE,
75
- )
76
-
77
- _time.sleep(3)
78
-
79
- public_url = None
80
-
81
- # Method 1: Colab's built-in proxy (simplest, no install needed)
82
- if not public_url:
83
- try:
84
- from google.colab.output import eval_js
85
- public_url = str(eval_js(f"google.colab.kernel.proxyPort({port})"))
86
- except Exception:
87
- pass
88
-
89
- # Method 2: ngrok (if token provided or pyngrok available)
90
- if not public_url and share:
91
- token = ngrok_token or os.environ.get("NGROK_TOKEN", "")
92
- try:
93
- from pyngrok import ngrok, conf
94
- if token:
95
- conf.get_default().auth_token = token
96
- public_url = str(ngrok.connect(port, "http"))
97
- except ImportError:
98
- if token:
99
- try:
100
- subprocess.check_call(
101
- [sys.executable, "-m", "pip", "install", "-q", "pyngrok"],
102
- stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
103
- )
104
- from pyngrok import ngrok, conf
105
- conf.get_default().auth_token = token
106
- public_url = str(ngrok.connect(port, "http"))
107
- except Exception:
108
- pass
109
- except Exception:
110
- pass
111
-
112
- # Method 3: localtunnel (no auth needed)
113
- if not public_url and share:
114
- try:
115
- lt_proc = subprocess.run(
116
- ["npx", "localtunnel", "--port", str(port)],
117
- capture_output=True, text=True, timeout=15,
118
- )
119
- for line in lt_proc.stdout.splitlines():
120
- if "https://" in line:
121
- public_url = line.strip().split()[-1]
122
- break
123
- except Exception:
124
- pass
125
-
126
- print(f"\n{'=' * 60}")
127
- print(" FlashStudio is running!")
128
- if public_url:
129
- print(f" Open: {public_url}")
130
- else:
131
- print(f" Port: {port}")
132
- print(" ")
133
- print(" If the URL doesn't work, try in a Colab cell:")
134
- print(f" from google.colab.output import eval_js")
135
- print(f" print(eval_js('google.colab.kernel.proxyPort({port})'))")
136
- print(f"{'=' * 60}\n")
137
-
138
- if public_url:
139
- try:
140
- from IPython.display import display, HTML
141
- display(HTML(
142
- f'<a href="{public_url}" target="_blank" '
143
- f'style="font-size:16px;padding:8px 16px;background:#7C3AED;'
144
- f'color:white;border-radius:6px;text-decoration:none;">'
145
- f'Open FlashStudio</a>'
146
- ))
147
- except Exception:
148
- pass
149
-
150
- return proc
151
-
152
-
153
- def _launch_local(app_path: str, port: int):
154
- """Launch locally with streamlit."""
155
- subprocess.run(
156
- [sys.executable, "-m", "streamlit", "run", app_path,
157
- "--server.port", str(port)],
158
- )
File without changes
File without changes