From 154aa89d0784a2dd5d07a5eda68750ee0cf56e4b Mon Sep 17 00:00:00 2001 From: Ryan Heise Date: Mon, 10 Oct 2022 02:53:23 +1100 Subject: [PATCH 1/5] Fix bug using headers with LockCachingAudioSource. --- just_audio/CHANGELOG.md | 1 + just_audio/lib/just_audio.dart | 35 ++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/just_audio/CHANGELOG.md b/just_audio/CHANGELOG.md index bd2f0f375..a01c91fbf 100644 --- a/just_audio/CHANGELOG.md +++ b/just_audio/CHANGELOG.md @@ -1,6 +1,7 @@ ## 0.9.30 * Upgrade ExoPlayer to 2.18.1. +* Fix bug using headers with LockCachingAudioSource. ## 0.9.29 diff --git a/just_audio/lib/just_audio.dart b/just_audio/lib/just_audio.dart index f5caae329..6b6b16ee4 100644 --- a/just_audio/lib/just_audio.dart +++ b/just_audio/lib/just_audio.dart @@ -2783,11 +2783,25 @@ class LockCachingAudioSource extends StreamAudioSource { partialCacheFile.existsSync() ? partialCacheFile : cacheFile; final httpClient = HttpClient(); + final userAgent = _player?._userAgent; + if (userAgent != null) { + httpClient.userAgent = userAgent; + } final httpRequest = await httpClient.getUrl(uri); if (headers != null) { + final host = httpRequest.headers.value(HttpHeaders.hostHeader); httpRequest.headers.clear(); + httpRequest.headers.set(HttpHeaders.contentLengthHeader, '0'); + if (host != null) { + httpRequest.headers.set(HttpHeaders.hostHeader, host); + } + if (userAgent != null) { + httpRequest.headers.set(HttpHeaders.userAgentHeader, userAgent); + } headers!.forEach((name, value) => httpRequest.headers.set(name, value)); } + // Match ExoPlayer's native behavior + httpRequest.maxRedirects = 20; final response = await httpRequest.close(); if (response.statusCode != 200) { httpClient.close(); @@ -2902,14 +2916,27 @@ class LockCachingAudioSource extends StreamAudioSource { final start = request.start!; final end = request.end ?? sourceLength; final httpClient = HttpClient(); + if (userAgent != null) { + httpClient.userAgent = userAgent; + } httpClient.getUrl(uri).then((httpRequest) async { if (headers != null) { + final host = httpRequest.headers.value(HttpHeaders.hostHeader); httpRequest.headers.clear(); + httpRequest.headers.set(HttpHeaders.contentLengthHeader, '0'); + if (host != null) { + httpRequest.headers.set(HttpHeaders.hostHeader, host); + } + if (userAgent != null) { + httpRequest.headers.set(HttpHeaders.userAgentHeader, userAgent); + } headers! .forEach((name, value) => httpRequest.headers.set(name, value)); } final rangeRequest = _HttpRangeRequest(start, end); httpRequest.headers.set(HttpHeaders.rangeHeader, rangeRequest.header); + // Match ExoPlayer's native behavior + httpRequest.maxRedirects = 20; final response = await httpRequest.close(); if (response.statusCode != 206) { httpClient.close(); @@ -3140,7 +3167,7 @@ _ProxyHandler _proxyHandlerForUri( final originRequest = await client.getUrl(redirectedUri ?? uri); // Rewrite request headers - final host = originRequest.headers.value('host'); + final host = originRequest.headers.value(HttpHeaders.hostHeader); originRequest.headers.clear(); // Match ExoPlayer's native behavior @@ -3157,9 +3184,9 @@ _ProxyHandler _proxyHandlerForUri( originRequest.headers.set(entry.key, entry.value); } if (host != null) { - originRequest.headers.set('host', host); + originRequest.headers.set(HttpHeaders.hostHeader, host); } else { - originRequest.headers.removeAll('host'); + originRequest.headers.removeAll(HttpHeaders.hostHeader); } // Try to make normal request @@ -3241,7 +3268,7 @@ _ProxyHandler _proxyHandlerForUri( // Rewrite headers final headers = {}; request.headers.forEach((name, value) { - if (name.toLowerCase() != 'host') { + if (name.toLowerCase() != HttpHeaders.hostHeader) { headers[name] = value.join(","); } }); From 27ab201c18d75f299643615661f87e5ebd34b459 Mon Sep 17 00:00:00 2001 From: Ryan Heise Date: Wed, 19 Oct 2022 19:06:04 +1100 Subject: [PATCH 2/5] Refactor the HTTP code. --- just_audio/lib/just_audio.dart | 119 +++++++++++++-------------------- 1 file changed, 46 insertions(+), 73 deletions(-) diff --git a/just_audio/lib/just_audio.dart b/just_audio/lib/just_audio.dart index 6b6b16ee4..2a23731d9 100644 --- a/just_audio/lib/just_audio.dart +++ b/just_audio/lib/just_audio.dart @@ -2782,27 +2782,9 @@ class LockCachingAudioSource extends StreamAudioSource { File getEffectiveCacheFile() => partialCacheFile.existsSync() ? partialCacheFile : cacheFile; - final httpClient = HttpClient(); - final userAgent = _player?._userAgent; - if (userAgent != null) { - httpClient.userAgent = userAgent; - } - final httpRequest = await httpClient.getUrl(uri); - if (headers != null) { - final host = httpRequest.headers.value(HttpHeaders.hostHeader); - httpRequest.headers.clear(); - httpRequest.headers.set(HttpHeaders.contentLengthHeader, '0'); - if (host != null) { - httpRequest.headers.set(HttpHeaders.hostHeader, host); - } - if (userAgent != null) { - httpRequest.headers.set(HttpHeaders.userAgentHeader, userAgent); - } - headers!.forEach((name, value) => httpRequest.headers.set(name, value)); - } - // Match ExoPlayer's native behavior - httpRequest.maxRedirects = 20; - final response = await httpRequest.close(); + final httpClient = _createHttpClient(userAgent: _player?._userAgent); + final request = await _getUrl(httpClient, uri, headers: headers); + final response = await request.close(); if (response.statusCode != 200) { httpClient.close(); throw Exception('HTTP Status Error: ${response.statusCode}'); @@ -2915,28 +2897,13 @@ class LockCachingAudioSource extends StreamAudioSource { _requests.remove(request); final start = request.start!; final end = request.end ?? sourceLength; - final httpClient = HttpClient(); - if (userAgent != null) { - httpClient.userAgent = userAgent; - } - httpClient.getUrl(uri).then((httpRequest) async { - if (headers != null) { - final host = httpRequest.headers.value(HttpHeaders.hostHeader); - httpRequest.headers.clear(); - httpRequest.headers.set(HttpHeaders.contentLengthHeader, '0'); - if (host != null) { - httpRequest.headers.set(HttpHeaders.hostHeader, host); - } - if (userAgent != null) { - httpRequest.headers.set(HttpHeaders.userAgentHeader, userAgent); - } - headers! - .forEach((name, value) => httpRequest.headers.set(name, value)); - } - final rangeRequest = _HttpRangeRequest(start, end); - httpRequest.headers.set(HttpHeaders.rangeHeader, rangeRequest.header); - // Match ExoPlayer's native behavior - httpRequest.maxRedirects = 20; + final httpClient = _createHttpClient(userAgent: _player?._userAgent); + + final rangeRequest = _HttpRangeRequest(start, end); + _getUrl(httpClient, uri, headers: { + if (headers != null) ...headers!, + HttpHeaders.rangeHeader: rangeRequest.header, + }).then((httpRequest) async { final response = await httpRequest.close(); if (response.statusCode != 206) { httpClient.close(); @@ -3159,38 +3126,16 @@ _ProxyHandler _proxyHandlerForUri( // Keep redirected [Uri] to speed-up requests Uri? redirectedUri; Future handler(_ProxyHttpServer server, HttpRequest request) async { - final client = HttpClient(); - - if (userAgent != null) { - client.userAgent = userAgent; - } - final originRequest = await client.getUrl(redirectedUri ?? uri); - - // Rewrite request headers - final host = originRequest.headers.value(HttpHeaders.hostHeader); - originRequest.headers.clear(); - - // Match ExoPlayer's native behavior - originRequest.maxRedirects = 20; - - // Make sure that we send the userAgent header also on the first request - if (userAgent != null) { - originRequest.headers.set(HttpHeaders.userAgentHeader, userAgent); - } - request.headers.forEach((name, value) { - originRequest.headers.set(name, value); - }); - for (var entry in headers?.entries ?? >[]) { - originRequest.headers.set(entry.key, entry.value); - } - if (host != null) { - originRequest.headers.set(HttpHeaders.hostHeader, host); - } else { - originRequest.headers.removeAll(HttpHeaders.hostHeader); - } - // Try to make normal request + String? host; try { + final client = _createHttpClient(userAgent: userAgent); + final requestHeaders = {if (headers != null) ...headers}; + request.headers + .forEach((name, value) => requestHeaders[name] = value.join(', ')); + final originRequest = + await _getUrl(client, redirectedUri ?? uri, headers: requestHeaders); + host = originRequest.headers.value(HttpHeaders.hostHeader); final originResponse = await originRequest.close(); if (originResponse.redirects.isNotEmpty) { redirectedUri = originResponse.redirects.last.location; @@ -3835,3 +3780,31 @@ enum PositionDiscontinuityReason { /// the current item and auto-advanced to the next item. autoAdvance, } + +Future _getUrl(HttpClient client, Uri uri, + {Map? headers}) async { + final request = await client.getUrl(uri); + if (headers != null) { + final host = request.headers.value(HttpHeaders.hostHeader); + request.headers.clear(); + request.headers.set(HttpHeaders.contentLengthHeader, '0'); + headers.forEach((name, value) => request.headers.set(name, value)); + if (host != null) { + request.headers.set(HttpHeaders.hostHeader, host); + } + if (client.userAgent != null) { + request.headers.set(HttpHeaders.userAgentHeader, client.userAgent!); + } + } + // Match ExoPlayer's native behavior + request.maxRedirects = 20; + return request; +} + +HttpClient _createHttpClient({String? userAgent}) { + final client = HttpClient(); + if (userAgent != null) { + client.userAgent = userAgent; + } + return client; +} From 537c45e7f60c5cd6cb287e97172e59c0cea233b8 Mon Sep 17 00:00:00 2001 From: Ryan Heise Date: Wed, 19 Oct 2022 19:34:33 +1100 Subject: [PATCH 3/5] Remove deprecated APIs. --- just_audio/lib/just_audio.dart | 9 ++++----- just_audio/test/just_audio_test.dart | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/just_audio/lib/just_audio.dart b/just_audio/lib/just_audio.dart index 2a23731d9..979976703 100644 --- a/just_audio/lib/just_audio.dart +++ b/just_audio/lib/just_audio.dart @@ -7,7 +7,6 @@ import 'package:audio_session/audio_session.dart'; import 'package:crypto/crypto.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:just_audio_platform_interface/just_audio_platform_interface.dart'; import 'package:meta/meta.dart' show experimental; import 'package:path/path.dart' as p; @@ -1566,7 +1565,7 @@ class PlaybackEvent { ); @override - int get hashCode => hashValues( + int get hashCode => Object.hash( processingState, updateTime, updatePosition, @@ -1631,7 +1630,7 @@ class PlayerState { String toString() => 'playing=$playing,processingState=$processingState'; @override - int get hashCode => hashValues(playing, processingState); + int get hashCode => Object.hash(playing, processingState); @override bool operator ==(Object other) => @@ -1656,7 +1655,7 @@ class IcyInfo { String toString() => 'title=$title,url=$url'; @override - int get hashCode => hashValues(title, url); + int get hashCode => Object.hash(title, url); @override bool operator ==(Object other) => @@ -1725,7 +1724,7 @@ class IcyMetadata { IcyMetadata({required this.info, required this.headers}); @override - int get hashCode => hashValues(info, headers); + int get hashCode => Object.hash(info, headers); @override bool operator ==(Object other) => diff --git a/just_audio/test/just_audio_test.dart b/just_audio/test/just_audio_test.dart index 812f28876..00730e3e9 100644 --- a/just_audio/test/just_audio_test.dart +++ b/just_audio/test/just_audio_test.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'dart:typed_data'; import 'package:audio_session/audio_session.dart'; import 'package:flutter/services.dart'; From c11726d32639f9bd261344739fcb8f8a6b77f2b8 Mon Sep 17 00:00:00 2001 From: Ryan Heise Date: Sun, 23 Oct 2022 23:27:40 +1100 Subject: [PATCH 4/5] Minor renamings. --- just_audio/lib/just_audio.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/just_audio/lib/just_audio.dart b/just_audio/lib/just_audio.dart index 979976703..af45e902c 100644 --- a/just_audio/lib/just_audio.dart +++ b/just_audio/lib/just_audio.dart @@ -2782,8 +2782,8 @@ class LockCachingAudioSource extends StreamAudioSource { partialCacheFile.existsSync() ? partialCacheFile : cacheFile; final httpClient = _createHttpClient(userAgent: _player?._userAgent); - final request = await _getUrl(httpClient, uri, headers: headers); - final response = await request.close(); + final httpRequest = await _getUrl(httpClient, uri, headers: headers); + final response = await httpRequest.close(); if (response.statusCode != 200) { httpClient.close(); throw Exception('HTTP Status Error: ${response.statusCode}'); @@ -3125,10 +3125,10 @@ _ProxyHandler _proxyHandlerForUri( // Keep redirected [Uri] to speed-up requests Uri? redirectedUri; Future handler(_ProxyHttpServer server, HttpRequest request) async { + final client = _createHttpClient(userAgent: userAgent); // Try to make normal request String? host; try { - final client = _createHttpClient(userAgent: userAgent); final requestHeaders = {if (headers != null) ...headers}; request.headers .forEach((name, value) => requestHeaders[name] = value.join(', ')); From dcbcb7457700df8a75555e63a05ac2b8bc8ef3c1 Mon Sep 17 00:00:00 2001 From: Ryan Heise Date: Sun, 23 Oct 2022 23:28:27 +1100 Subject: [PATCH 5/5] Add LockCachingAudioSource.resolve() --- just_audio/CHANGELOG.md | 1 + just_audio/example/lib/example_caching.dart | 4 +++- just_audio/lib/just_audio.dart | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/just_audio/CHANGELOG.md b/just_audio/CHANGELOG.md index a01c91fbf..ec5357428 100644 --- a/just_audio/CHANGELOG.md +++ b/just_audio/CHANGELOG.md @@ -2,6 +2,7 @@ * Upgrade ExoPlayer to 2.18.1. * Fix bug using headers with LockCachingAudioSource. +* Add LockCachingAudioSource.resolve(). ## 0.9.29 diff --git a/just_audio/example/lib/example_caching.dart b/just_audio/example/lib/example_caching.dart index 073ad8c10..f503e622b 100644 --- a/just_audio/example/lib/example_caching.dart +++ b/just_audio/example/lib/example_caching.dart @@ -48,7 +48,9 @@ class MyAppState extends State with WidgetsBindingObserver { print('A stream error occurred: $e'); }); try { - //await _audioSource.clearCache(); + // Use resolve() if you want to obtain a UriAudioSource pointing directly + // to the cache file. + // await _player.setAudioSource(await _audioSource.resolve()); await _player.setAudioSource(_audioSource); } catch (e) { print("Error loading audio source: $e"); diff --git a/just_audio/lib/just_audio.dart b/just_audio/lib/just_audio.dart index af45e902c..e126bd9b9 100644 --- a/just_audio/lib/just_audio.dart +++ b/just_audio/lib/just_audio.dart @@ -2713,6 +2713,13 @@ class LockCachingAudioSource extends StreamAudioSource { _downloadProgressSubject.add((await cacheFile.exists()) ? 1.0 : 0.0); } + /// Returns a [UriAudioSource] resolving directly to the cache file if it + /// exists, otherwise returns `this`. This can be + Future resolve() async { + final file = await cacheFile; + return await file.exists() ? AudioSource.uri(Uri.file(file.path)) : this; + } + /// Emits the current download progress as a double value from 0.0 (nothing /// downloaded) to 1.0 (download complete). Stream get downloadProgressStream => _downloadProgressSubject.stream;