diff --git a/.changes/adaptive-stream-manual-quality-merge b/.changes/adaptive-stream-manual-quality-merge deleted file mode 100644 index 083a3def6..000000000 --- a/.changes/adaptive-stream-manual-quality-merge +++ /dev/null @@ -1 +0,0 @@ -patch type="improved" "Allow manual video quality selection with adaptive stream enabled" diff --git a/.changes/adaptive-stream-pixel-density b/.changes/adaptive-stream-pixel-density deleted file mode 100644 index 94de7146f..000000000 --- a/.changes/adaptive-stream-pixel-density +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix adaptive stream dimensions on high-density displays" diff --git a/.changes/align-screenshare-simulcast-defaults b/.changes/align-screenshare-simulcast-defaults deleted file mode 100644 index 5ef6b9d5a..000000000 --- a/.changes/align-screenshare-simulcast-defaults +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Align screen share simulcast default low layer" diff --git a/.changes/clamp-simulcast-lower-layers b/.changes/clamp-simulcast-lower-layers deleted file mode 100644 index 2426b13b7..000000000 --- a/.changes/clamp-simulcast-lower-layers +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Clamp simulcast lower layers to the top layer" diff --git a/.changes/fix-buffer-status-busy-wait b/.changes/fix-buffer-status-busy-wait deleted file mode 100644 index 9f048b1d1..000000000 --- a/.changes/fix-buffer-status-busy-wait +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix waitForBufferStatusLow busy-wait after engine close" diff --git a/.changes/fix-connected-server-address b/.changes/fix-connected-server-address deleted file mode 100644 index e55d6ccea..000000000 --- a/.changes/fix-connected-server-address +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix connected server address using wrong peer connection" diff --git a/.changes/fix-deferred-track-listener-leak b/.changes/fix-deferred-track-listener-leak deleted file mode 100644 index fa6955d75..000000000 --- a/.changes/fix-deferred-track-listener-leak +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix deferred track listener leak across reconnects" diff --git a/.changes/fix-log-interpolation b/.changes/fix-log-interpolation deleted file mode 100644 index 8919c3a8e..000000000 --- a/.changes/fix-log-interpolation +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix string interpolation in forceRelay log messages" diff --git a/.changes/fix-premature-publication-dispose b/.changes/fix-premature-publication-dispose deleted file mode 100644 index e97a95a32..000000000 --- a/.changes/fix-premature-publication-dispose +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix premature publication dispose during unpublish" diff --git a/.changes/fix-reconnect-counter b/.changes/fix-reconnect-counter deleted file mode 100644 index 7291fd827..000000000 --- a/.changes/fix-reconnect-counter +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix reconnect counter null assertion on first reconnect attempt" diff --git a/.changes/fix-region-failover-condition b/.changes/fix-region-failover-condition deleted file mode 100644 index 722a10933..000000000 --- a/.changes/fix-region-failover-condition +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix region failover condition allowing null provider dereference" diff --git a/.changes/fix-send-sync-state-return-type b/.changes/fix-send-sync-state-return-type deleted file mode 100644 index ed91cbb36..000000000 --- a/.changes/fix-send-sync-state-return-type +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Fix sendSyncState using async void and swallowing sync-state preparation errors" diff --git a/.changes/fix-session-start-reentrance b/.changes/fix-session-start-reentrance deleted file mode 100644 index 286024ba9..000000000 --- a/.changes/fix-session-start-reentrance +++ /dev/null @@ -1 +0,0 @@ -patch type="fixed" "Guard Session.start() against concurrent calls" diff --git a/.changes/simplify-session-e2ee b/.changes/simplify-session-e2ee deleted file mode 100644 index d63c8d54c..000000000 --- a/.changes/simplify-session-e2ee +++ /dev/null @@ -1 +0,0 @@ -minor type="added" "Simplify enabling E2EE with Session API" diff --git a/.changes/update-protocol-v1-45-8 b/.changes/update-protocol-v1-45-8 deleted file mode 100644 index 6760a419c..000000000 --- a/.changes/update-protocol-v1-45-8 +++ /dev/null @@ -1 +0,0 @@ -patch type="changed" "Update generated protocol definitions to v1.45.8" diff --git a/.version b/.version index 24ba9a38d..834f26295 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.7.0 +2.8.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index ce2b2620c..0f1778128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # CHANGELOG +## 2.8.0 + +* Added: Session API support for simpler E2EE setup +* Changed: Manual video quality selection can be used with adaptive stream enabled +* Changed: Generated protocol definitions for LiveKit protocol v1.45.8 +* Fixed: waitForBufferStatusLow busy-waiting after engine close +* Fixed: Simulcast lower layers exceeding the top layer +* Fixed: forceRelay log message interpolation +* Fixed: sendSyncState error handling so sync-state preparation failures are not swallowed +* Fixed: Screen share simulcast default low layer alignment +* Fixed: Region failover null-provider dereference +* Fixed: Android builds with dependencies that require compileSdk 36 +* Fixed: Deferred track listener leaks across reconnects +* Fixed: Adaptive stream dimensions on high-density displays +* Fixed: Session.start() reentrancy during concurrent calls +* Fixed: Connected server address resolving from the wrong peer connection +* Fixed: Reconnect counter null assertion on the first reconnect attempt +* Fixed: Premature publication disposal during unpublish + ## 2.7.0 * Added: Add setVideoDimensions for remote track publications diff --git a/README.md b/README.md index 1bbbce27f..9a80d3dfb 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Include this package to your `pubspec.yaml` ```yaml --- dependencies: - livekit_client: ^2.7.0 + livekit_client: ^2.8.0 ``` ### iOS diff --git a/ios/livekit_client.podspec b/ios/livekit_client.podspec index ff758ac38..e9b3032eb 100644 --- a/ios/livekit_client.podspec +++ b/ios/livekit_client.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'livekit_client' - s.version = '2.7.0' + s.version = '2.8.0' s.summary = 'Open source platform for real-time audio and video.' s.description = 'Open source platform for real-time audio and video.' s.homepage = 'https://livekit.io/' diff --git a/lib/src/livekit.dart b/lib/src/livekit.dart index 413774285..6cdc2cba8 100644 --- a/lib/src/livekit.dart +++ b/lib/src/livekit.dart @@ -20,7 +20,7 @@ import 'support/platform.dart' show lkPlatformIsMobile; /// Main entry point to connect to a room. /// {@category Room} class LiveKitClient { - static const version = '2.7.0'; + static const version = '2.8.0'; /// Initialize the WebRTC plugin. If this is not manually called, will be /// initialized with default settings. diff --git a/macos/livekit_client.podspec b/macos/livekit_client.podspec index 10cbd676c..16c318b85 100644 --- a/macos/livekit_client.podspec +++ b/macos/livekit_client.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'livekit_client' - s.version = '2.7.0' + s.version = '2.8.0' s.summary = 'Open source platform for real-time audio and video.' s.description = 'Open source platform for real-time audio and video.' s.homepage = 'https://livekit.io/' diff --git a/pubspec.lock b/pubspec.lock index 9a7a4b124..39350a0a9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -452,10 +452,10 @@ packages: dependency: "direct main" description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: transitive description: @@ -737,10 +737,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" timing: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a72f7b8eb..2c8783473 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ name: livekit_client description: Flutter Client SDK for LiveKit. Build real-time video and audio into your apps. Supports iOS, Android, and Web. -version: 2.7.0 +version: 2.8.0 homepage: https://github.com/livekit/client-sdk-flutter environment: diff --git a/scripts/create_version.dart b/scripts/create_version.dart index ec4d96f0d..26e176d2b 100755 --- a/scripts/create_version.dart +++ b/scripts/create_version.dart @@ -23,7 +23,7 @@ import 'dart:io'; /// /// Where: /// - level: One of [patch, minor, major] indicating the version bump level -/// - kind: One of [added, changed, fixed, refactor, performance, security, deprecated, removed, docs, chore] +/// - kind: One of [added, changed, fixed, refactor, performance, security, deprecated, removed, docs] /// - description: A detailed description of the change /// /// Examples: @@ -103,6 +103,10 @@ class Change { }); } +String _allowedChangeKinds() => ChangeKind.values.map((e) => e.name).join(', '); + +String _allowedChangeLevels() => ChangeLevel.values.map((e) => e.name).join(', '); + class SemanticVersion { final int major; final int minor; @@ -154,39 +158,55 @@ List parseChanges() { } final changes = []; + final errors = []; final files = changesDir.listSync().whereType().where((f) => !f.path.split('/').last.startsWith('.')); for (final file in files) { final content = file.readAsStringSync(); final lines = content.split('\n'); - for (final line in lines) { + for (var i = 0; i < lines.length; i++) { + final rawLine = lines[i]; + final line = rawLine.trim(); + final location = '${file.path}:${i + 1}'; + // Skip empty lines - if (line.trim().isEmpty) continue; + if (line.isEmpty) continue; // Parse format: level type="kind" "description" - final parts = line.split(RegExp(r'\s+')); - if (parts.length < 3) continue; + final match = RegExp(r'^(\w+)\s+type="([^"]+)"\s+"([^"]+)"$').firstMatch(line); + if (match == null) { + errors.add('$location: expected `level type="kind" "description"`, found `$rawLine`'); + continue; + } // Extract level - final level = ChangeLevel.fromString(parts[0]); - if (level == null) continue; + final levelName = match.group(1)!; + final level = ChangeLevel.fromString(levelName); + if (level == null) { + errors.add('$location: unsupported level `$levelName`; expected one of: ${_allowedChangeLevels()}'); + continue; + } // Extract type from type="kind" format - final typeMatch = RegExp(r'type="(\w+)"').firstMatch(parts[1]); - if (typeMatch == null) continue; - final kind = ChangeKind.fromString(typeMatch.group(1)!); - if (kind == null) continue; + final kindName = match.group(2)!; + final kind = ChangeKind.fromString(kindName); + if (kind == null) { + errors.add('$location: unsupported type `$kindName`; expected one of: ${_allowedChangeKinds()}'); + continue; + } // Extract description from the last quoted string - final descMatch = RegExp(r'"([^"]+)"$').firstMatch(line); - if (descMatch == null) continue; - final description = descMatch.group(1)!; + final description = match.group(3)!; changes.add(Change(level: level, kind: kind, description: description)); } } + if (errors.isNotEmpty) { + throw Exception('Invalid change entries:\n${errors.map((e) => ' - $e').join('\n')}'); + } + if (changes.isEmpty) { throw Exception('No changes found in ${_Path.changes}'); }