gmdate('Y-m-d\TH:i:s\Z'), 'rig' => $this->rigName(), 'cpu' => $this->cpu(), 'mem' => $this->mem(), 'host' => ['kernel' => $this->kernel()], 'gpu' => $this->gpu(), 'obs' => $this->obs(), ]; } private function rigName(): string { $name = (string) (config('rig.rig_name') ?: gethostname()); return ucfirst(strtolower($name)); } private function cpu(): array { $model = null; if (is_readable('/proc/cpuinfo')) { foreach (file('/proc/cpuinfo') as $line) { if (preg_match('/^model name\s*:\s*(.+)$/', $line, $m)) { $model = trim($m[1]); break; } } } $threads = (int) (shell_exec('nproc 2>/dev/null') ?: 0); return ['model' => $model, 'threads' => $threads ?: null]; } private function mem(): array { $kb = 0; if (is_readable('/proc/meminfo')) { foreach (file('/proc/meminfo') as $line) { if (preg_match('/^MemTotal:\s+(\d+)\s*kB/', $line, $m)) { $kb = (int) $m[1]; break; } } } $gb = $kb > 0 ? round($kb / 1024 / 1024, 1) : null; return ['totalGB' => $gb]; } private function kernel(): ?string { $k = trim((string) shell_exec('uname -r 2>/dev/null')); return $k === '' ? null : $k; } private function gpu(): array { $name = null; $vram = null; if (trim((string) shell_exec('command -v nvidia-smi 2>/dev/null')) !== '') { $row = trim((string) shell_exec( 'nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits 2>/dev/null | head -n1' )); if ($row !== '') { [$n, $v] = array_pad(array_map('trim', explode(',', $row, 2)), 2, null); $name = $n !== '' ? $n : null; $vram = is_numeric($v) ? (int) $v : null; } } return ['name' => $name, 'vramTotalMB' => $vram]; } private function obs(): ?array { $obsDir = rtrim((string) config('rig.obs_studio_dir'), '/'); $userIni = $obsDir . '/user.ini'; if (!is_file($userIni)) return null; $user = $this->parseIni($userIni); $profile = $user['Basic']['Profile'] ?? 'Untitled'; $renderer = $user['Video']['Renderer'] ?? null; $profileDir = "{$obsDir}/basic/profiles/{$profile}"; $basicIni = "{$profileDir}/basic.ini"; if (!is_file($basicIni)) return null; $b = $this->parseIni($basicIni); $mode = $b['Output']['Mode'] ?? null; $baseW = isset($b['Video']['BaseCX']) ? (int) $b['Video']['BaseCX'] : null; $baseH = isset($b['Video']['BaseCY']) ? (int) $b['Video']['BaseCY'] : null; $outW = isset($b['Video']['OutputCX']) ? (int) $b['Video']['OutputCX'] : null; $outH = isset($b['Video']['OutputCY']) ? (int) $b['Video']['OutputCY'] : null; $fps = isset($b['Video']['FPSInt']) ? (int) $b['Video']['FPSInt'] : null; $encRaw = ''; $bitrate = null; $rc = null; $keyint = null; $h264Profile = null; if ($mode === 'Advanced') { $encRaw = $b['AdvOut']['Encoder'] ?? ''; $sej = "{$profileDir}/streamEncoder.json"; if (is_file($sej)) { $j = json_decode((string) file_get_contents($sej), true) ?: []; $rc = $j['rate_control'] ?? null; $bitrate = isset($j['bitrate']) ? (int) $j['bitrate'] : null; $keyint = isset($j['keyint_sec']) ? (int) $j['keyint_sec'] : null; $h264Profile = $j['profile'] ?? null; } } else { $encRaw = $b['SimpleOutput']['StreamEncoder'] ?? ''; $bitrate = isset($b['SimpleOutput']['VBitrate']) ? (int) $b['SimpleOutput']['VBitrate'] : null; } return [ 'profile' => $profile, 'renderer' => $renderer, 'outputMode' => $mode, 'canvas' => ['w' => $baseW, 'h' => $baseH], 'output' => ['w' => $outW, 'h' => $outH], 'fps' => $fps, 'color' => [ 'format' => $b['Video']['ColorFormat'] ?? null, 'space' => $b['Video']['ColorSpace'] ?? null, 'range' => $b['Video']['ColorRange'] ?? null, ], 'stream' => [ 'encoder' => $this->prettifyEncoder($encRaw), 'rateControl' => $rc, 'bitrateKbps' => $bitrate, 'keyintSec' => $keyint, 'profile' => $h264Profile, ], 'audio' => [ 'sampleRateHz' => isset($b['Audio']['SampleRate']) ? (int) $b['Audio']['SampleRate'] : null, 'channels' => $b['Audio']['ChannelSetup'] ?? null, ], ]; } private function prettifyEncoder(string $id): ?string { $map = [ 'obs_x264' => 'x264', 'x264' => 'x264', 'jim_nvenc' => 'NVENC H.264', 'ffmpeg_nvenc' => 'NVENC H.264', 'obs_nvenc_h264_tex' => 'NVENC H.264', 'obs_nvenc_h264_soft' => 'NVENC H.264', 'obs_nvenc_hevc_tex' => 'NVENC HEVC', 'obs_nvenc_hevc_soft' => 'NVENC HEVC', 'obs_nvenc_av1_tex' => 'NVENC AV1', 'obs_nvenc_av1_soft' => 'NVENC AV1', 'obs_qsv11' => 'QSV', ]; if ($id === '') return null; return $map[$id] ?? $id; } /** @return array> */ private function parseIni(string $path): array { $data = []; $section = null; foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) { $trim = trim($line); if ($trim === '' || str_starts_with($trim, ';') || str_starts_with($trim, '#')) continue; if (preg_match('/^\[(.+)\]$/', $trim, $m)) { $section = $m[1]; $data[$section] = $data[$section] ?? []; continue; } if ($section !== null && str_contains($line, '=')) { [$k, $v] = explode('=', $line, 2); $data[$section][trim($k)] = trim($v); } } return $data; } }