config->enabled) { return $this->denied($response); } $station = $request->getStation(); $token = (string)($params['token'] ?? ''); $resource = (string)($params['resource'] ?? ''); $authorized = $this->authorizer->authorize( $station->getRadioTempDir(), $station->id, $token, $resource, ); if (null === $authorized) { $this->logger->warning('Rejected on-demand HLS asset request.', [ 'station_id' => $station->id, 'resource' => $resource, 'token_fingerprint' => '' !== $token ? substr(hash('sha256', $token), 0, 12) : null, ]); return $this->denied($response); } // Keep cleanup request-driven without adding latency to every segment request. if (0 === random_int(0, 99)) { try { $this->cleanup->run($station->getRadioTempDir()); } catch (Throwable $exception) { // Delivery is already authorized; maintenance failures must not // interrupt playback or turn a recoverable cleanup issue into 5xx. $this->logger->warning('Request-driven on-demand HLS cleanup failed.', [ 'station_id' => $station->id, 'exception' => $exception, ]); } } $accelPath = '/internal/ondemand-hls/' . $station->id . '/' . $authorized['session']->cacheKey . '/' . $resource; $contentType = str_ends_with($resource, '.m3u8') ? 'application/vnd.apple.mpegurl' : 'video/mp2t'; return $response ->withHeader('Content-Type', $contentType) ->withHeader('Content-Disposition', 'inline') ->withHeader('Cache-Control', 'private, no-store, max-age=0') ->withHeader('Pragma', 'no-cache') ->withHeader('X-Content-Type-Options', 'nosniff') ->withHeader('X-Accel-Redirect', $accelPath); } private function denied(Response $response): ResponseInterface { return $response ->withJson(['error' => 'Invalid or expired playback session.'], 403) ->withHeader('Cache-Control', 'no-store'); } }