connect method

Future<void> connect({
  1. required AudioCodec codec,
  2. required int sampleRate,
  3. String language = 'en-US',
  4. String? userId,
  5. bool includeSpeechProfile = true,
  6. String? websocketUrl,
  7. String? apiKey,
  8. Map<String, String>? customHeaders,
  9. Map<String, String>? customParams,
})

Implementation

Future<void> connect({
  required AudioCodec codec,
  required int sampleRate,
  String language = 'en-US',
  String? userId,
  bool includeSpeechProfile = true,
  String? websocketUrl,
  String? apiKey,
  Map<String, String>? customHeaders,
  Map<String, String>? customParams,
}) async {
  if (_state == WebSocketState.connected) {
    print('WebSocket already connected');
    return;
  }

  if (_state == WebSocketState.connecting) {
    throw Exception('Connection already in progress');
  }

  // Store connection parameters
  _lastCodec = codec;
  _lastSampleRate = sampleRate;
  _lastLanguage = language;
  _lastUserId = userId;
  _lastWebsocketUrl = websocketUrl;
  _lastApiKey = apiKey;

  _updateState(WebSocketState.connecting);
  print('Connecting to WebSocket...');

  try {
    // FIXED: Proper URL construction
    String baseUrl;
    if (websocketUrl != null) {
      // Use provided URL, ensure it's wss://
      baseUrl = websocketUrl
          .replaceFirst('https://', 'wss://')
          .replaceFirst('http://', 'ws://');
      // Remove any duplicate paths
      if (baseUrl.contains('/v1/listen/v1/listen')) {
        baseUrl = baseUrl.replaceAll('/v1/listen/v1/listen', '/v1/listen');
      }
    } else {
      // Default Deepgram URL
      baseUrl = 'wss://api.deepgram.com/v1/listen';
    }

    // Build query parameters for Deepgram
    final params = <String, String>{
      'language': language,
      'sample_rate': sampleRate.toString(),
      'channels': '1',
      'encoding': _getEncodingForCodec(codec),
      'model': 'nova-2',
      'smart_format': 'true',
      'interim_results': 'true',
      'punctuate': 'true',
      'endpointing': '300',
      'vad_events': 'true',
      'diarize': 'true',
    };

    // Add custom parameters
    if (customParams != null) {
      params.addAll(customParams);
    }

    if (userId != null) {
      params['uid'] = userId;
    }

    final queryString = params.entries
        .map((e) => '${e.key}=${Uri.encodeComponent(e.value)}')
        .join('&');

    // FIXED: Proper URI construction
    final uri = Uri.parse('$baseUrl?$queryString');

    // Set up headers
    final headers = <String, dynamic>{};
    final effectiveApiKey = apiKey ?? _config.apiKey;

    if (effectiveApiKey != null) {
      headers['Authorization'] = 'Token $effectiveApiKey';
    }

    if (customHeaders != null) {
      headers.addAll(customHeaders);
    }

    print('Connecting to: $uri');
    print('Using codec: $codec, sample rate: $sampleRate Hz');

    _channel = IOWebSocketChannel.connect(
      uri,
      headers: headers.isNotEmpty ? headers : null,
      pingInterval: Duration(seconds: 20),
      connectTimeout: Duration(seconds: 15),
    );

    await _channel!.ready;
    _updateState(WebSocketState.connected);
    _reconnectAttempts = 0;
    _audioPacketsSent = 0;
    _totalAudioBytesSent = 0;

    print('WebSocket connected successfully');
    _startHeartbeat();

    // Listen for messages
    _channel!.stream.listen(
      (message) {
        try {
          _handleMessage(message);
        } catch (e) {
          print('Error handling WebSocket message: $e');
        }
      },
      onError: (error) {
        print('WebSocket stream error: $error');
        _updateState(WebSocketState.error);
        _scheduleReconnect();
      },
      onDone: () {
        print('WebSocket connection closed');
        _updateState(WebSocketState.disconnected);
        if (_config.autoReconnect) {
          _scheduleReconnect();
        }
      },
    );
  } catch (e) {
    print('WebSocket connection failed: $e');
    _updateState(WebSocketState.error);
    if (_config.autoReconnect) {
      _scheduleReconnect();
    }
    rethrow;
  }
}