diff --git a/just_audio/CHANGELOG.md b/just_audio/CHANGELOG.md index 550615a52..c1a11ff10 100644 --- a/just_audio/CHANGELOG.md +++ b/just_audio/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.9.44 + +* Add timeoutMillis parameter to UriAudioSource (@ctedgar). + * Android platform implementation completed + ## 0.9.43 * Fix NPE in load on iOS/macOS. diff --git a/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java b/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java index dc8a8e66e..d5e6f6d0b 100644 --- a/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java +++ b/just_audio/android/src/main/java/com/ryanheise/just_audio/AudioPlayer.java @@ -615,20 +615,20 @@ private MediaSource decodeAudioSource(final Object json) { String id = (String)map.get("id"); switch ((String)map.get("type")) { case "progressive": - return new ProgressiveMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers")), buildExtractorsFactory(mapGet(map, "options"))) + return new ProgressiveMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers"), mapGet(map, "timeoutMillis")), buildExtractorsFactory(mapGet(map, "options"))) .createMediaSource(new MediaItem.Builder() .setUri(Uri.parse((String)map.get("uri"))) .setTag(id) .build()); case "dash": - return new DashMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers"))) + return new DashMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers"), mapGet(map, "timeoutMillis"))) .createMediaSource(new MediaItem.Builder() .setUri(Uri.parse((String)map.get("uri"))) .setMimeType(MimeTypes.APPLICATION_MPD) .setTag(id) .build()); case "hls": - return new HlsMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers"))) + return new HlsMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers"), mapGet(map, "timeoutMillis"))) .createMediaSource(new MediaItem.Builder() .setUri(Uri.parse((String)map.get("uri"))) .setMimeType(MimeTypes.APPLICATION_M3U8) @@ -709,7 +709,7 @@ private void clearAudioEffects() { audioEffectsMap.clear(); } - private DataSource.Factory buildDataSourceFactory(Map headers) { + private DataSource.Factory buildDataSourceFactory(Map headers, Integer timeoutMillis) { final Map stringHeaders = castToStringMap(headers); String userAgent = null; if (stringHeaders != null) { @@ -727,6 +727,11 @@ private DataSource.Factory buildDataSourceFactory(Map headers) { if (stringHeaders != null && stringHeaders.size() > 0) { httpDataSourceFactory.setDefaultRequestProperties(stringHeaders); } + //Default for both timeout setting is 8 seconds, so using timeoutMillis to control both + if(timeoutMillis!=null){ + httpDataSourceFactory.setConnectTimeoutMs(timeoutMillis); + httpDataSourceFactory.setReadTimeoutMs(timeoutMillis); + } return new DefaultDataSource.Factory(context, httpDataSourceFactory); } diff --git a/just_audio/lib/just_audio.dart b/just_audio/lib/just_audio.dart index 057028a6e..3b6a65b22 100644 --- a/just_audio/lib/just_audio.dart +++ b/just_audio/lib/just_audio.dart @@ -681,7 +681,7 @@ class AudioPlayer { /// This is equivalent to: /// /// ``` - /// setAudioSource(AudioSource.uri(Uri.parse(url), headers: headers, tag: tag), + /// setAudioSource(AudioSource.uri(Uri.parse(url), headers: headers, timeoutMillis: timeoutMillis, tag: tag), /// initialPosition: Duration.zero, preload: true); /// ``` /// @@ -689,12 +689,13 @@ class AudioPlayer { Future setUrl( String url, { Map? headers, + int? timeoutMillis, Duration? initialPosition, bool preload = true, dynamic tag, }) => setAudioSource( - AudioSource.uri(Uri.parse(url), headers: headers, tag: tag), + AudioSource.uri(Uri.parse(url), headers: headers, timeoutMillis: timeoutMillis, tag: tag), initialPosition: initialPosition, preload: preload); @@ -2232,17 +2233,20 @@ abstract class AudioSource { /// provided by that package. If you wish to have more control over the tag /// for background audio purposes, consider using the plugin audio_service /// instead of just_audio_background. + /// + /// [timeoutMillis] can be used to adjust http connection timeout settings + /// (currently only supported for Android) static UriAudioSource uri(Uri uri, - {Map? headers, dynamic tag}) { + {Map? headers, int? timeoutMillis, dynamic tag}) { bool hasExtension(Uri uri, String extension) => uri.path.toLowerCase().endsWith('.$extension') || uri.fragment.toLowerCase().endsWith('.$extension'); if (hasExtension(uri, 'mpd')) { - return DashAudioSource(uri, headers: headers, tag: tag); + return DashAudioSource(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag); } else if (hasExtension(uri, 'm3u8')) { - return HlsAudioSource(uri, headers: headers, tag: tag); + return HlsAudioSource(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag); } else { - return ProgressiveAudioSource(uri, headers: headers, tag: tag); + return ProgressiveAudioSource(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag); } } @@ -2329,9 +2333,10 @@ abstract class IndexedAudioSource extends AudioSource { abstract class UriAudioSource extends IndexedAudioSource { final Uri uri; final Map? headers; + final int? timeoutMillis; Uri? _overrideUri; - UriAudioSource(this.uri, {this.headers, dynamic tag, Duration? duration}) + UriAudioSource(this.uri, {this.headers, this.timeoutMillis, dynamic tag, Duration? duration}) : super(tag: tag, duration: duration); /// If [uri] points to an asset, this gives us [_overrideUri] which is the URI @@ -2423,12 +2428,16 @@ abstract class UriAudioSource extends IndexedAudioSource { /// /// If headers are set, just_audio will create a cleartext local HTTP proxy on /// your device to forward HTTP requests with headers included. +/// +/// [timeoutMillis] can be used to adjust http connection timeout settings +/// (currently only supported for Android) class ProgressiveAudioSource extends UriAudioSource { final ProgressiveAudioSourceOptions? options; ProgressiveAudioSource( super.uri, { super.headers, + super.timeoutMillis, super.tag, super.duration, this.options, @@ -2439,6 +2448,7 @@ class ProgressiveAudioSource extends UriAudioSource { id: _id, uri: _effectiveUri.toString(), headers: _mergedHeaders, + timeoutMillis: timeoutMillis, tag: tag, options: options?._toMessage(), ); @@ -2458,16 +2468,20 @@ class ProgressiveAudioSource extends UriAudioSource { /// /// If headers are set, just_audio will create a cleartext local HTTP proxy on /// your device to forward HTTP requests with headers included. +/// +/// [timeoutMillis] can be used to adjust http connection timeout settings +/// (currently only supported for Android) class DashAudioSource extends UriAudioSource { DashAudioSource(Uri uri, - {Map? headers, dynamic tag, Duration? duration}) - : super(uri, headers: headers, tag: tag, duration: duration); + {Map? headers, int? timeoutMillis, dynamic tag, Duration? duration}) + : super(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag, duration: duration); @override AudioSourceMessage _toMessage() => DashAudioSourceMessage( id: _id, uri: _effectiveUri.toString(), headers: _mergedHeaders, + timeoutMillis: timeoutMillis, tag: tag, ); } @@ -2485,16 +2499,20 @@ class DashAudioSource extends UriAudioSource { /// /// If headers are set, just_audio will create a cleartext local HTTP proxy on /// your device to forward HTTP requests with headers included. +/// +/// [timeoutMillis] can be used to adjust http connection timeout settings +/// (currently only supported for Android) class HlsAudioSource extends UriAudioSource { HlsAudioSource(Uri uri, - {Map? headers, dynamic tag, Duration? duration}) - : super(uri, headers: headers, tag: tag, duration: duration); + {Map? headers, int? timeoutMillis, dynamic tag, Duration? duration}) + : super(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag, duration: duration); @override AudioSourceMessage _toMessage() => HlsAudioSourceMessage( id: _id, uri: _effectiveUri.toString(), headers: _mergedHeaders, + timeoutMillis: timeoutMillis, tag: tag, ); } diff --git a/just_audio/pubspec.yaml b/just_audio/pubspec.yaml index 63601311a..4a2b90be2 100644 --- a/just_audio/pubspec.yaml +++ b/just_audio/pubspec.yaml @@ -1,6 +1,6 @@ name: just_audio description: A feature-rich audio player for Flutter. Loop, clip and concatenate any sound from any source (asset/file/URL/stream) in a variety of audio formats with gapless playback. -version: 0.9.43 +version: 0.9.44 repository: https://github.com/ryanheise/just_audio/tree/minor/just_audio issue_tracker: https://github.com/ryanheise/just_audio/issues topics: @@ -14,7 +14,7 @@ environment: flutter: ">=3.10.0" dependencies: - just_audio_platform_interface: ^4.3.0 + just_audio_platform_interface: ^4.3.1 # just_audio_platform_interface: # path: ../just_audio_platform_interface just_audio_web: ^0.4.11 diff --git a/just_audio/test/just_audio_test.dart b/just_audio/test/just_audio_test.dart index 6b67812d1..bdf318067 100644 --- a/just_audio/test/just_audio_test.dart +++ b/just_audio/test/just_audio_test.dart @@ -190,6 +190,20 @@ void runTests() { exception = e; } expect(exception != null, equals(true)); + try { + await player.setUrl('https://foo.foo/timeout.mp3'); + exception = null; + } catch (e) { + exception = e; + } + expect(exception != null, equals(true)); + try { + await player.setUrl('https://foo.foo/timeout.mp3', timeoutMillis: 11); + exception = null; + } catch (e) { + exception = e; + } + expect(exception == null, equals(true)); await player.dispose(); }); @@ -1479,6 +1493,10 @@ class MockAudioPlayer extends AudioPlayerPlatform { throw PlatformException(code: '404', message: 'Not found'); } else if (audioSource.uri.contains('error')) { throw PlatformException(code: 'error', message: 'Unknown error'); + } else if (audioSource.uri.contains('timeout')){ + if ((audioSource.timeoutMillis ?? 0) < 10) { + throw PlatformException(code: 'timeout', message: 'Timed out (because timeoutMillis setting is less than 10)'); + } } _duration = audioSourceDuration; } else if (audioSource is ClippingAudioSourceMessage) {