loading(); $this->line('── Project Loading manifest ──'); $this->line(' (press Enter to keep [defaults])'); $this->line(''); $game = null; $gameId = null; $defaultGame = (string) ($prev['game'] ?? ''); while (true) { $query = (string) $this->ask('Game (search)', $defaultGame ?: null); if ($query === '') { $this->error(' ✗ Game is required'); continue; } if ($twitch->enabled()) { $cachedId = (string) ($prev['gameId'] ?? ''); if ($query === $defaultGame && preg_match('/^\d+$/', $cachedId)) { $game = $defaultGame; $gameId = $cachedId; $this->line(" ↻ reusing cached: {$game} (id={$gameId})"); break; } try { $hit = $twitch->searchCategory($query); } catch (Throwable $e) { $this->warn(' ! Twitch search failed: ' . $e->getMessage()); if ($this->confirm('Use the raw input without resolving an id?', true)) { $game = $query; break; } continue; } if ($hit === null) { $this->warn(' ! no Twitch matches for "' . $query . '"'); continue; } $game = $hit['name']; $gameId = $hit['id']; $this->line(" ↳ matched: {$game} (id={$gameId})"); break; } $game = $query; break; } $subtitle = (string) $this->ask('Subtitle (mode / episode / note)', $prev['subtitle'] ?? null); $countdown = (int) $this->ask('Countdown (minutes)', (string) ($prev['countdownMin'] ?? 5)); $camera = $this->confirm('Camera', (bool) ($prev['camera'] ?? true)); $microphone = $this->confirm('Microphone', (bool) ($prev['microphone'] ?? true)); $data = [ 'compiledAt' => gmdate('Y-m-d\TH:i:s\Z'), 'game' => $game, 'gameId' => $gameId, 'subtitle' => $subtitle ?: null, 'countdownMin' => $countdown, 'camera' => $camera, 'microphone' => $microphone, ]; $path = rtrim(config('rig.storage_data'), '/') . '/loading.json'; @mkdir(dirname($path), 0775, true); file_put_contents( $path, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n" ); $this->info(''); $this->info(" ✓ wrote {$path}"); // Push to Twitch — soft-fail so a network blip doesn't block the local manifest. if (!$this->option('no-twitch') && $twitch->enabled() && $gameId) { $title = $subtitle ?: $game; try { $twitch->setChannel($title, $gameId); $this->info(" ✓ Twitch channel updated → \"{$title}\" / category {$game}"); } catch (Throwable $e) { $this->warn(' ! Twitch sync failed (manifest still saved): ' . $e->getMessage()); } } $this->line(''); $this->line(' → refresh the Project Loading browser source in OBS.'); return self::SUCCESS; } }