|
|
|
@ -3,6 +3,7 @@ import 'dart:math';
|
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
|
import 'package:crypto/crypto.dart';
|
|
|
|
|
import 'dart:developer' as dev;
|
|
|
|
|
import '../exceptions/api_exception.dart';
|
|
|
|
|
import 'track.dart';
|
|
|
|
|
|
|
|
|
|
class Api {
|
|
|
|
@ -12,12 +13,14 @@ class Api {
|
|
|
|
|
|
|
|
|
|
//for web api
|
|
|
|
|
get redirectUri => 'https://daflmusic.000webhostapp.com/callback/';
|
|
|
|
|
final _scopes = 'user-read-playback-state user-read-currently-playing';
|
|
|
|
|
final _scopes =
|
|
|
|
|
'user-read-playback-state user-read-currently-playing user-read-recently-played';
|
|
|
|
|
late String _state;
|
|
|
|
|
dynamic _codeVerifier;
|
|
|
|
|
dynamic _codeChallenge;
|
|
|
|
|
late String _encodedLogs;
|
|
|
|
|
final _tokenType = 'Bearer ';
|
|
|
|
|
late http.Response _response; //use _setResponse() as kind of a private setter
|
|
|
|
|
|
|
|
|
|
//from web api
|
|
|
|
|
String? _code;
|
|
|
|
@ -74,11 +77,11 @@ class Api {
|
|
|
|
|
//session management
|
|
|
|
|
|
|
|
|
|
requestUserAuthorization(Uri url) async {
|
|
|
|
|
if (url.queryParameters['state'] == _state.toString()) {
|
|
|
|
|
_code = url.queryParameters['code'];
|
|
|
|
|
await _requestAccessToken();
|
|
|
|
|
if (url.queryParameters['state'] != _state.toString()) {
|
|
|
|
|
throw ApiException();
|
|
|
|
|
}
|
|
|
|
|
// TODO : implement the else
|
|
|
|
|
_code = url.queryParameters['code'];
|
|
|
|
|
await _requestAccessToken();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_requestAccessToken() async {
|
|
|
|
@ -89,18 +92,19 @@ class Api {
|
|
|
|
|
'client_id': _clientId,
|
|
|
|
|
'code_verifier': _codeVerifier
|
|
|
|
|
});
|
|
|
|
|
var response = await _client.post(urlToken, headers: <String, String>{
|
|
|
|
|
_setResponse(await _client.post(urlToken, headers: <String, String>{
|
|
|
|
|
'Authorization': 'Basic $_encodedLogs',
|
|
|
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
|
|
|
});
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
|
|
|
|
}));
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(_response.bodyBytes)) as Map;
|
|
|
|
|
_accessToken = decodedResponse['access_token'];
|
|
|
|
|
_expiresIn = decodedResponse['expires_in'];
|
|
|
|
|
_tokenEnd = DateTime.now().add(Duration(seconds: _expiresIn!));
|
|
|
|
|
_refreshToken = decodedResponse['refresh_token'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<String?> _getToken() async {
|
|
|
|
|
//handle 401 status code (bad or expired token)
|
|
|
|
|
Future<String?> _getAccessToken() async {
|
|
|
|
|
await _tokenValidity();
|
|
|
|
|
return _accessToken;
|
|
|
|
|
}
|
|
|
|
@ -111,16 +115,23 @@ class Api {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_setResponse(value) {
|
|
|
|
|
if (value.statusCode != 200) {
|
|
|
|
|
throw ApiException();
|
|
|
|
|
}
|
|
|
|
|
_response = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_getRefreshedAccessToken() async {
|
|
|
|
|
var urlToken = Uri.https('accounts.spotify.com', 'api/token', {
|
|
|
|
|
'grant_type': 'refresh_token',
|
|
|
|
|
'refresh_token': _refreshToken,
|
|
|
|
|
'client_id': _clientId
|
|
|
|
|
});
|
|
|
|
|
var response = await _client.post(urlToken, headers: <String, String>{
|
|
|
|
|
_setResponse(await _client.post(urlToken, headers: <String, String>{
|
|
|
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
|
|
|
});
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
|
|
|
|
}));
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(_response.bodyBytes)) as Map;
|
|
|
|
|
_accessToken = decodedResponse['access_token'];
|
|
|
|
|
_expiresIn = decodedResponse['expires_in'];
|
|
|
|
|
_tokenEnd = DateTime.now().add(Duration(seconds: _expiresIn!));
|
|
|
|
@ -128,29 +139,40 @@ class Api {
|
|
|
|
|
|
|
|
|
|
//functional methods
|
|
|
|
|
|
|
|
|
|
Future<String> getCurrentlyPlayingTrack() async {
|
|
|
|
|
var url = Uri.https('api.spotify.com', 'v1/me/player/currently-playing');
|
|
|
|
|
var token = await _getToken();
|
|
|
|
|
var response = await _client.get(url, headers: <String, String>{
|
|
|
|
|
Future<String> getRecentlyPlayedTrack() async {
|
|
|
|
|
var url = Uri.https(
|
|
|
|
|
'api.spotify.com', 'v1/me/player/recently-played', {'limit': '1'});
|
|
|
|
|
var token = await _getAccessToken();
|
|
|
|
|
_setResponse(await _client.get(url, headers: <String, String>{
|
|
|
|
|
'Authorization': '$_tokenType $token',
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
});
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
|
|
|
|
return decodedResponse['item']['id'];
|
|
|
|
|
}));
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(_response.bodyBytes)) as Map;
|
|
|
|
|
return decodedResponse['items'][0]['track']['id'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<Track> getTrackInfo(String id) async {
|
|
|
|
|
var url = Uri.https('api.spotify.com', 'v1/tracks/$id');
|
|
|
|
|
var token = await _getToken();
|
|
|
|
|
var response = await _client.get(url, headers: <String, String>{
|
|
|
|
|
var token = await _getAccessToken();
|
|
|
|
|
_setResponse(await _client.get(url, headers: <String, String>{
|
|
|
|
|
'Authorization': '$_tokenType $token',
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
});
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
|
|
|
|
return Track(
|
|
|
|
|
decodedResponse['artists'][0]['name'],
|
|
|
|
|
decodedResponse['name'],
|
|
|
|
|
decodedResponse['album']['images']
|
|
|
|
|
[decodedResponse['album']['images'].length - 1]['url']);
|
|
|
|
|
}));
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(_response.bodyBytes)) as Map;
|
|
|
|
|
return Track(decodedResponse['artists'][0]['name'], decodedResponse['name'],
|
|
|
|
|
decodedResponse['album']['images'][0]['url']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getPlaylists() async {
|
|
|
|
|
var url = Uri.https('api.spotify.com', 'v1/me/playlists', {'limit': '50'});
|
|
|
|
|
var token = await _getAccessToken();
|
|
|
|
|
_setResponse(await _client.get(url, headers: <String, String>{
|
|
|
|
|
'Authorization': '$_tokenType $token',
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
}));
|
|
|
|
|
var decodedResponse = jsonDecode(utf8.decode(_response.bodyBytes)) as Map;
|
|
|
|
|
dev.log(decodedResponse['items']
|
|
|
|
|
.where((element) => element['name'] == 'daflmusic')
|
|
|
|
|
.toString());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|