db = VDatabase::getInstance(); $this->logger = VLogger::getInstance(); } public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Ensure the user has streaming enabled and a stream key assigned. * @param int $userId * @return string Stream key * @throws Exception when streaming is disabled or user not found */ public function ensureActiveStreamKey($userId) { $record = $this->getUserStreamingRecord($userId); if (!$record) { throw new Exception('User not found'); } if ((int)$record['streaming_enabled'] !== 1) { throw new Exception('Streaming is disabled for this user'); } if (empty($record['live_key'])) { return $this->generateAndPersistKey($userId, $record); } return $record['live_key']; } /** * Activate streaming for a user and ensure a stream key exists. * @param int $userId * @return string Stream key * @throws Exception */ public function activateStreaming($userId) { $record = $this->getUserStreamingRecord($userId); if (!$record) { throw new Exception('User not found'); } $updates = [ 'streaming_enabled' => 1 ]; $createdNewKey = false; if (empty($record['live_key'])) { $updates['live_key'] = $this->generateStreamKeyValue($userId); $updates['stream_key_regenerated_at'] = date('Y-m-d H:i:s'); $createdNewKey = true; } // Only perform update if necessary if ((int)$record['streaming_enabled'] !== 1 || $createdNewKey) { $this->db->doUpdate('db_accountuser', 'usr_id', $updates, $userId); $this->logger->info('Streaming enabled for user', [ 'user_id' => $userId, 'key_generated' => $createdNewKey ]); } return $createdNewKey ? $updates['live_key'] : $record['live_key']; } /** * Regenerate and persist a new stream key for the user. * @param int $userId * @return string New stream key * @throws Exception */ public function regenerateStreamKey($userId) { $record = $this->getUserStreamingRecord($userId); if (!$record) { throw new Exception('User not found'); } if ((int)$record['streaming_enabled'] !== 1) { throw new Exception('Streaming is disabled for this user'); } $newKey = $this->generateStreamKeyValue($userId); $this->db->doUpdate('db_accountuser', 'usr_id', [ 'live_key' => $newKey, 'stream_key_regenerated_at' => date('Y-m-d H:i:s') ], $userId); $this->logger->info('Stream key regenerated', [ 'user_id' => $userId ]); return $newKey; } /** * Deactivate streaming for user and clear stream key. * @param int $userId */ public function deactivateStreaming($userId) { $record = $this->getUserStreamingRecord($userId); if (!$record) { return; } $updates = [ 'streaming_enabled' => 0, 'live_key' => '', 'stream_key_regenerated_at' => date('Y-m-d H:i:s') ]; $this->db->doUpdate('db_accountuser', 'usr_id', $updates, $userId); $this->logger->info('Streaming disabled for user', [ 'user_id' => $userId ]); } /** * Determine if streaming is currently enabled for a user. * @param int $userId * @return bool */ public function isStreamingEnabled($userId) { $record = $this->getUserStreamingRecord($userId); return $record ? (int)$record['streaming_enabled'] === 1 : false; } /** * Retrieve current stream key without side effects. * @param int $userId * @return string|null */ public function getCurrentStreamKey($userId) { $record = $this->getUserStreamingRecord($userId); if (!$record) { return null; } return $record['live_key'] ?: null; } private function generateAndPersistKey($userId, array $record) { $newKey = $this->generateStreamKeyValue($userId); $this->db->doUpdate('db_accountuser', 'usr_id', [ 'live_key' => $newKey, 'stream_key_regenerated_at' => date('Y-m-d H:i:s') ], $userId); $this->logger->info('Stream key generated for user', [ 'user_id' => $userId ]); return $newKey; } private function generateStreamKeyValue($userId) { // Ensure high-entropy stream keys using hex encoding $random = bin2hex(random_bytes(16)); return 'es_' . $userId . '_' . $random; } private function getUserStreamingRecord($userId) { $query = "SELECT usr_id, live_key, streaming_enabled FROM db_accountuser WHERE usr_id = ? LIMIT 1"; $result = $this->db->doQuery($query, [$userId]); return $this->db->doFetch($result); } }