diff --git a/example/example.dart b/example/example.dart index eb69d26..108073d 100644 --- a/example/example.dart +++ b/example/example.dart @@ -12,6 +12,10 @@ void main() async { var credentials = SpotifyApiCredentials(keyMap['id'], keyMap['secret']); var spotify = SpotifyApi(credentials); + print('\nExpannd shortened spotify link of https://spotify.link/hRkBrwub9xb'); + var longLink = await spotify.expandLink('https://spotify.link/hRkBrwub9xb'); + print(longLink); + print('\nPodcast:'); await spotify.shows .get('4rOoJ6Egrf8K2IrywzwOMk') diff --git a/lib/src/spotify_base.dart b/lib/src/spotify_base.dart index 35034f1..473134d 100644 --- a/lib/src/spotify_base.dart +++ b/lib/src/spotify_base.dart @@ -158,6 +158,9 @@ abstract class SpotifyApiBase { ); } + /// Expands shortened spotify [url] + Future expandLink(String url) async => _streamedHeadImpl(url, const {}); + Future _get(String path) { return _getImpl('$_baseUrl/$path', const {}); } @@ -174,6 +177,15 @@ abstract class SpotifyApiBase { return _putImpl('$_baseUrl/$path', const {}, body); } + Future _streamedHeadImpl( + String url, Map headers) async { + return await _requestWrapper(() async { + final request = http.Request('HEAD', Uri.parse(url)); + request.headers.addAll(headers); + return (await _client).send(request); + }); + } + Future _getImpl(String url, Map headers) async { return await _requestWrapper(() async => await (await _client).get(Uri.parse(url), headers: headers)); @@ -202,14 +214,26 @@ abstract class SpotifyApiBase { .put(Uri.parse(url), headers: headers, body: body)); } - Future _requestWrapper(Future Function() request, + // the reason we are using [http.BaseResponse] is because + // otherwise we wouldn't be able to access the redirect url from + // BaseResponseWithUrl + Future _requestWrapper(Future Function() request, {retryLimit = 5}) async { for (var i = 0; i < retryLimit; i++) { while (_shouldWait) { await Future.delayed(Duration(milliseconds: 500)); } try { - return handleErrors(await request()); + var response = await request(); + + // distinguish between url redirect responses and body responses + // note, that any response that also contains a redirect url + // will be chosen instead of its body contents + // FIXME: in future releases of http2, the url is a part of the [http.Response] type + if (response case http.BaseResponseWithUrl(:final url)) { + return url.toString(); + } + return handleResponseWithBody(response as http.Response); } on ApiRateException catch (ex) { if (i == retryLimit - 1) rethrow; print( @@ -226,7 +250,7 @@ abstract class SpotifyApiBase { return SpotifyApiCredentials._fromClient(await _client); } - String handleErrors(http.Response response) { + String handleResponseWithBody(http.Response response) { final responseBody = utf8.decode(response.bodyBytes); if (response.statusCode >= 400) { final jsonMap = json.decode(responseBody); diff --git a/pubspec.yaml b/pubspec.yaml index 4b58123..ce06cd6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,12 +4,12 @@ version: 0.13.1 homepage: https://github.com/rinukkusu/spotify-dart environment: - sdk: '>=2.18.0 <4.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: - http: ^1.1.0 + http: ^1.2.0 json_annotation: ^4.8.1 - oauth2: ^2.0.0 + oauth2: ^2.0.2 dev_dependencies: lints: ">=2.1.1 <4.0.0"