()
| 211 | } |
| 212 | |
| 213 | async function runSetup() { |
| 214 | detailsEl.classList.add("hidden"); |
| 215 | retryBtn.classList.add("hidden"); |
| 216 | for (const step of steps) step.classList.remove("active", "done", "error"); |
| 217 | |
| 218 | try { |
| 219 | setStep("runtime", "active"); |
| 220 | setStatus("Checking Python runtime..."); |
| 221 | let [runtime] = await Promise.all([ |
| 222 | invoke("probe_runtime"), |
| 223 | minDelay(350), |
| 224 | ]); |
| 225 | |
| 226 | // Compare the installed runtime against the version this app bundle expects. |
| 227 | // On a DMG upgrade the old runtime is still fully "ready", so this MUST be |
| 228 | // checked before the early-return below -- otherwise setup starts the stale |
| 229 | // backend + frontend and the new release (e.g. new features, version) never |
| 230 | // takes effect until the runtime is manually cleared. |
| 231 | const runtimeStatus = await invoke("runtime_pack_status"); |
| 232 | const expectedVersion = runtimeStatus.manifest?.version; |
| 233 | const installedVersion = runtimeStatus.installedVersion; |
| 234 | // Mismatch when this build expects a version the installed runtime isn't. |
| 235 | // An unknown installedVersion (a runtime from a build that never recorded |
| 236 | // one) also counts, so an upgrade still refreshes it. Self-heals: after one |
| 237 | // refresh the install records the version and subsequent launches match. |
| 238 | const versionMismatch = Boolean(expectedVersion) && installedVersion !== expectedVersion; |
| 239 | |
| 240 | if (runtime.pythonReady && runtime.ffmpegReady && runtime.torchDevice && !versionMismatch) { |
| 241 | for (const step of steps) { |
| 242 | step.classList.remove("active", "error"); |
| 243 | if (step.dataset.step === "backend") { |
| 244 | step.classList.remove("done"); |
| 245 | } else { |
| 246 | step.classList.add("done"); |
| 247 | } |
| 248 | } |
| 249 | await runStep("backend", async () => { |
| 250 | setStatus("Runtime is ready. Starting StemDeck backend..."); |
| 251 | const backend = await startBackend(); |
| 252 | setStatus("Opening StemDeck..."); |
| 253 | window.location.replace(backend.url); |
| 254 | }); |
| 255 | return; |
| 256 | } |
| 257 | |
| 258 | if (!runtime.pythonReady || versionMismatch) { |
| 259 | if (versionMismatch) { |
| 260 | setStatus(`Updating runtime from ${installedVersion || "an older build"} to ${expectedVersion}...`); |
| 261 | } |
| 262 | await invoke("ensure_workspace"); |
| 263 | await installRuntimePack(runtime.appRoot); |
| 264 | runtime = await invoke("probe_runtime"); |
| 265 | if (!runtime.pythonReady) { |
| 266 | setStep("runtime", "error"); |
| 267 | throw Object.assign( |
| 268 | new Error(`Python runtime setup failed under: ${runtime.dataDir}`), |
| 269 | { hint: "Check that your disk has at least 2 GB free and click Retry. If it keeps failing, try reinstalling StemDeck." } |
| 270 | ); |
no test coverage detected