Début implémentation MVC.
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
/!\ toutes les erreurs ne sont pas résolus !remotes/origin/mvc-implementation
parent
4528871a81
commit
b085106f28
@ -1,300 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:math';
|
|
||||||
import 'package:dafl_project_flutter/main.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:crypto/crypto.dart';
|
|
||||||
import 'dart:developer' as dev;
|
|
||||||
import '../exceptions/api_exception.dart';
|
|
||||||
|
|
||||||
class Api {
|
|
||||||
//from dashboard
|
|
||||||
final _clientId = '7ceb49d874b9404492246027e4d68cf8';
|
|
||||||
final _clientSecret = '98f9cb960bf54ebbb9ad306e7ff919cb';
|
|
||||||
|
|
||||||
//for web api
|
|
||||||
get redirectUri => 'https://daflmusic.000webhostapp.com/callback/';
|
|
||||||
final _scopes =
|
|
||||||
'user-read-playback-state user-read-currently-playing user-read-recently-played playlist-modify-public ugc-image-upload user-modify-playback-state';
|
|
||||||
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
|
|
||||||
final _playlistName = "Dafl's discovery";
|
|
||||||
|
|
||||||
//from web api
|
|
||||||
String? _code;
|
|
||||||
int? _expiresIn;
|
|
||||||
String? _refreshToken;
|
|
||||||
String? _accessToken; //use _getAccessToken() as kind of a private getter
|
|
||||||
|
|
||||||
//other
|
|
||||||
final _client = http.Client();
|
|
||||||
late Uri _urlAuthorize;
|
|
||||||
|
|
||||||
get urlAuthorize => _urlAuthorize;
|
|
||||||
DateTime? _tokenEnd;
|
|
||||||
|
|
||||||
Api() {
|
|
||||||
_state = _generateRandomString(16);
|
|
||||||
_codeVerifier = _generateRandomString(_generateRandomInt(43, 128));
|
|
||||||
_codeChallenge = _generateCodeChallenge();
|
|
||||||
_encodedLogs = base64.encode(utf8.encode("$_clientId:$_clientSecret"));
|
|
||||||
_urlAuthorize = Uri.https('accounts.spotify.com', 'authorize', {
|
|
||||||
'client_id': _clientId,
|
|
||||||
'response_type': 'code',
|
|
||||||
'redirect_uri': redirectUri,
|
|
||||||
'state': _state,
|
|
||||||
'scope': _scopes,
|
|
||||||
'show_dialog': 'false',
|
|
||||||
'code_challenge_method': 'S256',
|
|
||||||
'code_challenge': _codeChallenge
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//PKCE generations
|
|
||||||
|
|
||||||
_generateRandomInt(int min, int max) {
|
|
||||||
return min + Random().nextInt(max - min);
|
|
||||||
}
|
|
||||||
|
|
||||||
_generateRandomString(int length) {
|
|
||||||
const chars =
|
|
||||||
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
|
||||||
return String.fromCharCodes(Iterable.generate(
|
|
||||||
length, (_) => chars.codeUnitAt(Random().nextInt(chars.length))));
|
|
||||||
}
|
|
||||||
|
|
||||||
_generateCodeChallenge() {
|
|
||||||
return base64Encode(sha256.convert(utf8.encode(_codeVerifier)).bytes)
|
|
||||||
.replaceAll('+', '-')
|
|
||||||
.replaceAll('/', '_')
|
|
||||||
.replaceAll('=', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
//session management
|
|
||||||
|
|
||||||
requestUserAuthorization(Uri url) async {
|
|
||||||
if (url.queryParameters['state'] != _state.toString()) {
|
|
||||||
throw ApiException('state');
|
|
||||||
}
|
|
||||||
_code = url.queryParameters['code'];
|
|
||||||
await _requestAccessToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
_requestAccessToken() async {
|
|
||||||
var urlToken = Uri.https('accounts.spotify.com', 'api/token', {
|
|
||||||
'code': _code,
|
|
||||||
'redirect_uri': redirectUri,
|
|
||||||
'grant_type': 'authorization_code',
|
|
||||||
'client_id': _clientId,
|
|
||||||
'code_verifier': _codeVerifier
|
|
||||||
});
|
|
||||||
_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;
|
|
||||||
_accessToken = decodedResponse['access_token'];
|
|
||||||
_expiresIn = decodedResponse['expires_in'];
|
|
||||||
_tokenEnd = DateTime.now().add(Duration(seconds: _expiresIn!));
|
|
||||||
_refreshToken = decodedResponse['refresh_token'];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> _getAccessToken() async {
|
|
||||||
if (DateTime.now().isAfter(_tokenEnd!)) {
|
|
||||||
await _getRefreshedAccessToken();
|
|
||||||
}
|
|
||||||
return _accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
_setResponse(value) {
|
|
||||||
int sc = value.statusCode;
|
|
||||||
if (sc >= 300) {
|
|
||||||
dev.log(value.body.toString());
|
|
||||||
throw ApiException(sc);
|
|
||||||
}
|
|
||||||
_response = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getRefreshedAccessToken() async {
|
|
||||||
var urlToken = Uri.https('accounts.spotify.com', 'api/token', {
|
|
||||||
'grant_type': 'refresh_token',
|
|
||||||
'refresh_token': _refreshToken,
|
|
||||||
'client_id': _clientId
|
|
||||||
});
|
|
||||||
_setResponse(await _client.post(urlToken, headers: <String, String>{
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded'
|
|
||||||
}));
|
|
||||||
var decodedResponse = jsonDecode(utf8.decode(_response.bodyBytes)) as Map;
|
|
||||||
_accessToken = decodedResponse['access_token'];
|
|
||||||
_expiresIn = decodedResponse['expires_in'];
|
|
||||||
_tokenEnd = DateTime.now().add(Duration(seconds: _expiresIn!));
|
|
||||||
}
|
|
||||||
|
|
||||||
//functional methods
|
|
||||||
|
|
||||||
Future<String> getCurrentlyPlayingTrack() async {
|
|
||||||
var url = Uri.https('api.spotify.com', 'v1/me/player/currently-playing');
|
|
||||||
var token = await _getAccessToken();
|
|
||||||
var response = await _client.get(url, headers: <String, String>{
|
|
||||||
'Authorization': '$_tokenType $token',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
});
|
|
||||||
if (response.statusCode == 204) {
|
|
||||||
return _getRecentlyPlayedTrack();
|
|
||||||
}
|
|
||||||
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
|
||||||
return decodedResponse['item']['id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
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['items'][0]['track']['id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map> getTrackInfo(String id) async {
|
|
||||||
var url = Uri.https('api.spotify.com', 'v1/tracks/$id');
|
|
||||||
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;
|
|
||||||
Map<String, String> info = {
|
|
||||||
'artist': decodedResponse['artists'][0]['name'],
|
|
||||||
'name': decodedResponse['name'],
|
|
||||||
'cover': decodedResponse['album']['images'][0]['url']
|
|
||||||
};
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> _isInPlaylist(String idTrack, String idPlaylist) async {
|
|
||||||
var url = Uri.https('api.spotify.com', 'v1/playlists/$idPlaylist/tracks',
|
|
||||||
{'limit': '50', 'fields': 'items(track(id))'});
|
|
||||||
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;
|
|
||||||
var res = decodedResponse['items']
|
|
||||||
.where((element) => element['track']['id'] == idTrack)
|
|
||||||
.toList();
|
|
||||||
if (res.length >= 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
addToPLaylist(String idTrack) async {
|
|
||||||
var idPlaylist = await _getPlaylist();
|
|
||||||
if (idPlaylist == null) {
|
|
||||||
idPlaylist = await _createPlaylist();
|
|
||||||
} else {
|
|
||||||
if (await _isInPlaylist(idTrack, idPlaylist)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var token = await _getAccessToken();
|
|
||||||
var url = Uri.https('api.spotify.com', 'v1/playlists/$idPlaylist/tracks',
|
|
||||||
{'uris': 'spotify:track:$idTrack'});
|
|
||||||
_setResponse(await _client.post(url, headers: <String, String>{
|
|
||||||
'Authorization': '$_tokenType $token',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String?> _getPlaylist() 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;
|
|
||||||
var daflplaylist = decodedResponse['items']
|
|
||||||
.where((element) => element['name'] == _playlistName)
|
|
||||||
.toList();
|
|
||||||
if (daflplaylist.length == 1) {
|
|
||||||
return daflplaylist[0]['uri'].substring(
|
|
||||||
17); //17 char because format is 'spotify:playlist:MYPLAYLISTID'
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> _createPlaylist() async {
|
|
||||||
var idUser = await MyApp.controller.currentUser.getIdSpotify();
|
|
||||||
var token = await _getAccessToken();
|
|
||||||
var url = Uri.https('api.spotify.com', 'v1/users/$idUser/playlists');
|
|
||||||
_setResponse(await _client.post(url,
|
|
||||||
headers: <String, String>{
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Authorization': '$_tokenType $token',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: jsonEncode(<String, String>{
|
|
||||||
'name': _playlistName,
|
|
||||||
'description':
|
|
||||||
'Retrouvez toutes vos découvertes faites sur DaflMusic 🎵',
|
|
||||||
'public': 'true'
|
|
||||||
})));
|
|
||||||
var decodedResponse = jsonDecode(utf8.decode(_response.bodyBytes)) as Map;
|
|
||||||
var idPlaylist = decodedResponse['id'];
|
|
||||||
return idPlaylist;
|
|
||||||
}
|
|
||||||
|
|
||||||
playTrack(String idTrack) async {
|
|
||||||
var token = await _getAccessToken();
|
|
||||||
var url = Uri.https('api.spotify.com', 'v1/me/player/play');
|
|
||||||
_setResponse(await _client.put(url,
|
|
||||||
headers: <String, String>{
|
|
||||||
'Authorization': '$_tokenType $token',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: jsonEncode(<String, List>{
|
|
||||||
'uris': ['spotify:track:$idTrack']
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
removeFromPlaylist(String idTrack) async {
|
|
||||||
var idPlaylist = await _getPlaylist();
|
|
||||||
if (idPlaylist != null) {
|
|
||||||
if (await _isInPlaylist(idTrack, idPlaylist)) {
|
|
||||||
var token = await _getAccessToken();
|
|
||||||
var url =
|
|
||||||
Uri.https('api.spotify.com', 'v1/playlists/$idPlaylist/tracks');
|
|
||||||
var jsonVar = jsonEncode(<String, List>{
|
|
||||||
'tracks': [
|
|
||||||
{'uri': 'spotify:track:$idTrack'}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
_setResponse(await _client.delete(url,
|
|
||||||
headers: <String, String>{
|
|
||||||
'Authorization': '$_tokenType $token',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: jsonVar));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> getIdUser() async {
|
|
||||||
var url = Uri.https('api.spotify.com', 'v1/me');
|
|
||||||
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['id'];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import 'dart:developer' as dev;
|
|
||||||
|
|
||||||
class ApiException implements Exception {
|
|
||||||
final String mess = 'Api exception raised,';
|
|
||||||
|
|
||||||
ApiException(dynamic code) {
|
|
||||||
if (code.runtimeType == String) {
|
|
||||||
dev.log('$mess state verification failed.');
|
|
||||||
} else {
|
|
||||||
dev.log('$mess status code : $code.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
import 'dart:developer' as dev;
|
||||||
|
|
||||||
|
class ApiStateException implements Exception {
|
||||||
|
ApiStateException() {
|
||||||
|
dev.log('State verification failed.');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import 'dart:developer' as dev;
|
||||||
|
|
||||||
|
class HttpException implements Exception {
|
||||||
|
HttpException(var value) {
|
||||||
|
dev.log('Http request failed :');
|
||||||
|
dev.log('Status code : ${value.statusCode.toString()}');
|
||||||
|
dev.log('Body : ${value.body.toString()}');
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
import '../model/user.dart';
|
|
||||||
|
|
||||||
abstract class Saver{
|
|
||||||
void save(User userToSave);
|
|
||||||
}
|
|
@ -0,0 +1,20 @@
|
|||||||
|
import 'package:dafl_project_flutter/services/api/api_spotify_identification.dart';
|
||||||
|
import 'package:dafl_project_flutter/services/api/api_spotify_requests.dart';
|
||||||
|
|
||||||
|
class ApiSpotify {
|
||||||
|
late ApiSpotifyIdentification _identification;
|
||||||
|
late ApiSpotifyRequests _requests;
|
||||||
|
|
||||||
|
ApiSpotify() {
|
||||||
|
_identification = ApiSpotifyIdentification();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiSpotifyIdentification get identification => _identification;
|
||||||
|
|
||||||
|
ApiSpotifyRequests get requests => _requests;
|
||||||
|
|
||||||
|
apiAuthorization(url) async {
|
||||||
|
await _identification.setCode(url);
|
||||||
|
_requests = ApiSpotifyRequests(await _identification.createToken());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
import '../../exceptions/api_state_exception.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import '../http_response_verification.dart';
|
||||||
|
import 'token_spotify.dart';
|
||||||
|
|
||||||
|
class ApiSpotifyIdentification extends HttpResponseVerification {
|
||||||
|
static const String clientId = '7ceb49d874b9404492246027e4d68cf8';
|
||||||
|
final String _clientSecret = '98f9cb960bf54ebbb9ad306e7ff919cb';
|
||||||
|
final String _scopes =
|
||||||
|
'user-read-playback-state user-read-currently-playing user-read-recently-played playlist-modify-public ugc-image-upload user-modify-playback-state';
|
||||||
|
|
||||||
|
late String _state;
|
||||||
|
late String _codeVerifier;
|
||||||
|
late String _codeChallenge;
|
||||||
|
late String _encodedLogs;
|
||||||
|
|
||||||
|
String? _code;
|
||||||
|
|
||||||
|
String get redirectUri => 'https://daflmusic.000webhostapp.com/callback/';
|
||||||
|
|
||||||
|
Uri get urlAuthorize => Uri.https('accounts.spotify.com', 'authorize', {
|
||||||
|
'client_id': clientId,
|
||||||
|
'response_type': 'code',
|
||||||
|
'redirect_uri': redirectUri,
|
||||||
|
'state': _state,
|
||||||
|
'scope': _scopes,
|
||||||
|
'show_dialog': 'false',
|
||||||
|
'code_challenge_method': 'S256',
|
||||||
|
'code_challenge': _codeChallenge
|
||||||
|
});
|
||||||
|
|
||||||
|
ApiSpotifyIdentification() {
|
||||||
|
_state = _generateRandomString(16);
|
||||||
|
_codeVerifier = _generateRandomString(_generateRandomInt(43, 128));
|
||||||
|
_codeChallenge = _generateCodeChallenge();
|
||||||
|
_encodedLogs = base64.encode(utf8.encode("$clientId:$_clientSecret"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateRandomInt(int min, int max) {
|
||||||
|
return min + Random().nextInt(max - min);
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateRandomString(int length) {
|
||||||
|
const chars =
|
||||||
|
'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
|
||||||
|
return String.fromCharCodes(Iterable.generate(
|
||||||
|
length, (_) => chars.codeUnitAt(Random().nextInt(chars.length))));
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateCodeChallenge() {
|
||||||
|
return base64Encode(sha256.convert(utf8.encode(_codeVerifier)).bytes)
|
||||||
|
.replaceAll('+', '-')
|
||||||
|
.replaceAll('/', '_')
|
||||||
|
.replaceAll('=', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
setCode(Uri url) async {
|
||||||
|
if (url.queryParameters['state'] != _state.toString()) {
|
||||||
|
throw ApiStateException();
|
||||||
|
}
|
||||||
|
_code = url.queryParameters['code'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<TokenSpotify> createToken() async {
|
||||||
|
var urlToken = Uri.https('accounts.spotify.com', 'api/token', {
|
||||||
|
'code': _code,
|
||||||
|
'redirect_uri': redirectUri,
|
||||||
|
'grant_type': 'authorization_code',
|
||||||
|
'client_id': clientId,
|
||||||
|
'code_verifier': _codeVerifier
|
||||||
|
});
|
||||||
|
setResponse(await http.post(urlToken, headers: <String, String>{
|
||||||
|
'Authorization': 'Basic $_encodedLogs',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}));
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
return TokenSpotify(decodedResponse['access_token'],
|
||||||
|
decodedResponse['refresh_token'], decodedResponse['expires_in']);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,177 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:dafl_project_flutter/services/api/token_spotify.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../../main.dart';
|
||||||
|
import '../http_response_verification.dart';
|
||||||
|
|
||||||
|
class ApiSpotifyRequests extends HttpResponseVerification {
|
||||||
|
final String _tokenType = 'Bearer ';
|
||||||
|
final String _playlistName = "Dafl's discovery";
|
||||||
|
final TokenSpotify _token;
|
||||||
|
|
||||||
|
ApiSpotifyRequests(this._token);
|
||||||
|
|
||||||
|
Future<String> getCurrentlyPlayingTrack() async {
|
||||||
|
var url = Uri.https('api.spotify.com', 'v1/me/player/currently-playing');
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
var response = await http.get(url, headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
if (response.statusCode == 204) {
|
||||||
|
return _getRecentlyPlayedTrack();
|
||||||
|
}
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
return decodedResponse['item']['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _getRecentlyPlayedTrack() async {
|
||||||
|
var url = Uri.https(
|
||||||
|
'api.spotify.com', 'v1/me/player/recently-played', {'limit': '1'});
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
setResponse(await http.get(url, headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}));
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
return decodedResponse['items'][0]['track']['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> getTrackInfo(String id) async {
|
||||||
|
var url = Uri.https('api.spotify.com', 'v1/tracks/$id');
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
setResponse(await http.get(url, headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}));
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
Map<String, String> infos = {
|
||||||
|
'artist': decodedResponse['artists'][0]['name'],
|
||||||
|
'name': decodedResponse['name'],
|
||||||
|
'cover': decodedResponse['album']['images'][0]['url']
|
||||||
|
};
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _isInPlaylist(String idTrack, String idPlaylist) async {
|
||||||
|
var url = Uri.https('api.spotify.com', 'v1/playlists/$idPlaylist/tracks',
|
||||||
|
{'limit': '50', 'fields': 'items(track(id))'});
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
setResponse(await http.get(url, headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}));
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
var res = decodedResponse['items']
|
||||||
|
.where((element) => element['track']['id'] == idTrack)
|
||||||
|
.toList();
|
||||||
|
if (res.length >= 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
addToPLaylist(String idTrack) async {
|
||||||
|
var idPlaylist = await _getPlaylist();
|
||||||
|
if (idPlaylist == null) {
|
||||||
|
idPlaylist = await _createPlaylist();
|
||||||
|
} else {
|
||||||
|
if (await _isInPlaylist(idTrack, idPlaylist)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
var url = Uri.https('api.spotify.com', 'v1/playlists/$idPlaylist/tracks',
|
||||||
|
{'uris': 'spotify:track:$idTrack'});
|
||||||
|
setResponse(await http.post(url, headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> _getPlaylist() async {
|
||||||
|
var url = Uri.https('api.spotify.com', 'v1/me/playlists', {'limit': '50'});
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
setResponse(await http.get(url, headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}));
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
var daflplaylist = decodedResponse['items']
|
||||||
|
.where((element) => element['name'] == _playlistName)
|
||||||
|
.toList();
|
||||||
|
if (daflplaylist.length == 1) {
|
||||||
|
return daflplaylist[0]['uri'].substring(
|
||||||
|
17); //17 char because format is 'spotify:playlist:MYPLAYLISTID'
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _createPlaylist() async {
|
||||||
|
var idUser = MyApp.controller.getIdSpotify();
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
var url = Uri.https('api.spotify.com', 'v1/users/$idUser/playlists');
|
||||||
|
setResponse(await http.post(url,
|
||||||
|
headers: <String, String>{
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: jsonEncode(<String, String>{
|
||||||
|
'name': _playlistName,
|
||||||
|
'description':
|
||||||
|
'Retrouvez toutes vos découvertes faites sur DaflMusic 🎵',
|
||||||
|
'public': 'true'
|
||||||
|
})));
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
var idPlaylist = decodedResponse['id'];
|
||||||
|
return idPlaylist;
|
||||||
|
}
|
||||||
|
|
||||||
|
playTrack(String idTrack) async {
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
var url = Uri.https('api.spotify.com', 'v1/me/player/play');
|
||||||
|
setResponse(await http.put(url,
|
||||||
|
headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: jsonEncode(<String, List>{
|
||||||
|
'uris': ['spotify:track:$idTrack']
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFromPlaylist(String idTrack) async {
|
||||||
|
var idPlaylist = await _getPlaylist();
|
||||||
|
if (idPlaylist != null) {
|
||||||
|
if (await _isInPlaylist(idTrack, idPlaylist)) {
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
var url =
|
||||||
|
Uri.https('api.spotify.com', 'v1/playlists/$idPlaylist/tracks');
|
||||||
|
var jsonVar = jsonEncode(<String, List>{
|
||||||
|
'tracks': [
|
||||||
|
{'uri': 'spotify:track:$idTrack'}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
setResponse(await http.delete(url,
|
||||||
|
headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: jsonVar));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getIdUser() async {
|
||||||
|
var url = Uri.https('api.spotify.com', 'v1/me');
|
||||||
|
var token = await _token.getAccessToken();
|
||||||
|
setResponse(await http.get(url, headers: <String, String>{
|
||||||
|
'Authorization': '$_tokenType $token',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}));
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
return decodedResponse['id'];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:dafl_project_flutter/services/api/api_spotify_identification.dart';
|
||||||
|
|
||||||
|
import '../http_response_verification.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
class TokenSpotify extends HttpResponseVerification {
|
||||||
|
String _accessToken;
|
||||||
|
final String _refreshToken;
|
||||||
|
late DateTime _tokenEnd;
|
||||||
|
|
||||||
|
TokenSpotify(this._accessToken, this._refreshToken, int expiresIn) {
|
||||||
|
_setTokenEnd(expiresIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
_setTokenEnd(int expiresIn) {
|
||||||
|
_tokenEnd = DateTime.now().add(Duration(seconds: expiresIn));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getAccessToken() async {
|
||||||
|
if (DateTime.now().isAfter(_tokenEnd)) {
|
||||||
|
await _actualiseToken();
|
||||||
|
}
|
||||||
|
return _accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
_actualiseToken() async {
|
||||||
|
var urlToken = Uri.https('accounts.spotify.com', 'api/token', {
|
||||||
|
'grant_type': 'refresh_token',
|
||||||
|
'refresh_token': _refreshToken,
|
||||||
|
'client_id': ApiSpotifyIdentification.clientId
|
||||||
|
});
|
||||||
|
setResponse(await http.post(urlToken, headers: <String, String>{
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}));
|
||||||
|
var decodedResponse = jsonDecode(utf8.decode(response.bodyBytes)) as Map;
|
||||||
|
_accessToken = decodedResponse['access_token'];
|
||||||
|
_setTokenEnd(decodedResponse['expires_in']);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
import '../exceptions/http_exception.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
abstract class HttpResponseVerification {
|
||||||
|
@protected
|
||||||
|
late http.Response response;
|
||||||
|
|
||||||
|
@protected
|
||||||
|
setResponse(var value) {
|
||||||
|
if (value.statusCode >= 300) {
|
||||||
|
throw HttpException(value);
|
||||||
|
}
|
||||||
|
response = value;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'loader.dart';
|
import 'loader.dart';
|
||||||
import '../model/user.dart';
|
import '../../model/user.dart';
|
||||||
import 'database_connexion.dart';
|
import 'database_connexion.dart';
|
||||||
import 'dart:developer' as dev;
|
import 'dart:developer' as dev;
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
import 'database_connexion.dart';
|
import 'database_connexion.dart';
|
||||||
import 'saver.dart';
|
import 'saver.dart';
|
||||||
import '../model/user.dart';
|
import '../../model/user.dart';
|
||||||
|
|
||||||
class DatabaseSaver extends Saver {
|
class DatabaseSaver extends Saver {
|
||||||
// Save user in the database
|
// Save user in the database
|
@ -1,5 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import '../model/user.dart';
|
import '../../model/user.dart';
|
||||||
|
|
||||||
abstract class Loader {
|
abstract class Loader {
|
||||||
Future<User> load(String username, String password);
|
Future<User> load(String username, String password);
|
@ -0,0 +1,5 @@
|
|||||||
|
import '../../model/user.dart';
|
||||||
|
|
||||||
|
abstract class Saver {
|
||||||
|
void save(User userToSave);
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
class Location {}
|
Loading…
Reference in new issue