re-initiate OAuth process on Refresh Token revoke

YouTube & Spotify track matching accuracy increase
This commit is contained in:
Kingkor Roy Tirtho 2022-01-21 18:25:18 +06:00
parent e1c9e6fb29
commit 93d95cb309
5 changed files with 107 additions and 69 deletions

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart' hide Page;
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:oauth2/oauth2.dart' show AuthorizationException;
import 'package:spotify/spotify.dart' hide Image;
import 'package:spotube/components/CategoryCard.dart';
import 'package:spotube/components/Login.dart';
@ -12,6 +13,7 @@ import 'package:spotube/components/PageWindowTitleBar.dart';
import 'package:spotube/components/Player.dart' as player;
import 'package:spotube/components/Settings.dart';
import 'package:spotube/components/UserLibrary.dart';
import 'package:spotube/helpers/oauth-login.dart';
import 'package:spotube/models/LocalStorageKeys.dart';
import 'package:spotube/models/sideBarTiles.dart';
import 'package:spotube/provider/Auth.dart';
@ -42,18 +44,20 @@ class _HomeState extends State<Home> {
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
String? clientId = localStorage.getString(LocalStorageKeys.clientId);
String? clientSecret =
localStorage.getString(LocalStorageKeys.clientSecret);
String? accessToken =
localStorage.getString(LocalStorageKeys.accessToken);
String? refreshToken =
localStorage.getString(LocalStorageKeys.refreshToken);
String? expirationStr =
localStorage.getString(LocalStorageKeys.expiration);
DateTime? expiration =
expirationStr != null ? DateTime.parse(expirationStr) : null;
try {
Auth authProvider = context.read<Auth>();
SharedPreferences localStorage = await SharedPreferences.getInstance();
var clientId = localStorage.getString(LocalStorageKeys.clientId);
var clientSecret =
localStorage.getString(LocalStorageKeys.clientSecret);
var accessToken = localStorage.getString(LocalStorageKeys.accessToken);
var refreshToken =
localStorage.getString(LocalStorageKeys.refreshToken);
var expirationStr = localStorage.getString(LocalStorageKeys.expiration);
var expiration =
expirationStr != null ? DateTime.parse(expirationStr) : null;
if (clientId != null && clientSecret != null) {
SpotifyApi spotifyApi = SpotifyApi(
@ -103,8 +107,17 @@ class _HomeState extends State<Home> {
_pagingController.error = e;
}
});
} catch (e) {
print("[login state error]: $e");
} on AuthorizationException catch (e) {
if (clientId != null && clientSecret != null) {
oauthLogin(
context,
clientId: clientId,
clientSecret: clientSecret,
);
}
} catch (e, stack) {
print("[Home.initState]: $e");
print(stack);
}
});
}

View File

@ -1,16 +1,12 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotify/spotify.dart' hide Image;
import 'package:spotube/components/Home.dart';
import 'package:spotube/components/PageWindowTitleBar.dart';
import 'package:spotube/helpers/server_ipc.dart';
import 'package:spotube/models/LocalStorageKeys.dart';
import 'package:spotube/helpers/oauth-login.dart';
import 'package:spotube/provider/Auth.dart';
const redirectUri = "http://localhost:4304/auth/spotify/callback";
class Login extends StatefulWidget {
const Login({Key? key}) : super(key: key);
@override
_LoginState createState() => _LoginState();
}
@ -19,9 +15,6 @@ class _LoginState extends State<Login> {
String clientId = "";
String clientSecret = "";
bool _fieldError = false;
String? accessToken;
String? refreshToken;
DateTime? expiration;
handleLogin(Auth authState) async {
try {
@ -30,48 +23,7 @@ class _LoginState extends State<Login> {
_fieldError = true;
});
}
final credentials = SpotifyApiCredentials(clientId, clientSecret);
final grant = SpotifyApi.authorizationCodeGrant(credentials);
final authUri = grant.getAuthorizationUrl(Uri.parse(redirectUri),
scopes: spotifyScopes);
final responseUri = await connectIpc(authUri.toString(), redirectUri);
SharedPreferences localStorage = await SharedPreferences.getInstance();
if (responseUri != null) {
final SpotifyApi spotify =
SpotifyApi.fromAuthCodeGrant(grant, responseUri);
var credentials = await spotify.getCredentials();
if (credentials.accessToken != null) {
accessToken = credentials.accessToken;
await localStorage.setString(
LocalStorageKeys.accessToken, credentials.accessToken!);
}
if (credentials.refreshToken != null) {
refreshToken = credentials.refreshToken;
await localStorage.setString(
LocalStorageKeys.refreshToken, credentials.refreshToken!);
}
if (credentials.expiration != null) {
expiration = credentials.expiration;
await localStorage.setString(LocalStorageKeys.expiration,
credentials.expiration?.toString() ?? "");
}
}
await localStorage.setString(LocalStorageKeys.clientId, clientId);
await localStorage.setString(
LocalStorageKeys.clientSecret,
clientSecret,
);
authState.setAuthState(
clientId: clientId,
clientSecret: clientSecret,
accessToken: accessToken,
refreshToken: refreshToken,
expiration: expiration,
isLoggedIn: true,
);
await oauthLogin(context, clientId: clientId, clientSecret: clientSecret);
} catch (e) {
print("[Login.handleLogin] $e");
}

View File

@ -14,6 +14,7 @@ import 'package:spotube/provider/Playback.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:spotube/provider/SpotifyDI.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
class Player extends StatefulWidget {
const Player({Key? key}) : super(key: key);
@ -32,6 +33,8 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
double _volume = 0;
late YoutubeExplode youtube;
late List<GlobalKeyActions> _hotKeys;
@override
@ -39,6 +42,7 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
try {
super.initState();
player = AudioPlayer();
youtube = YoutubeExplode();
_hotKeys = [
GlobalKeyActions(
HotKey(KeyCode.space, scope: HotKeyScope.inapp),
@ -134,6 +138,7 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
player.dispose();
youtube.close();
Future.wait(_hotKeys.map((e) => hotKeyManager.unregister(e.hotKey)));
super.dispose();
}
@ -200,7 +205,7 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
});
});
}
var ytTrack = await toYoutubeTrack(currentTrack);
var ytTrack = await toYoutubeTrack(youtube, currentTrack);
if (playback.setTrackUriById(currentTrack.id!, ytTrack.uri!)) {
await player
.setAudioSource(AudioSource.uri(Uri.parse(ytTrack.uri!)))

View File

@ -0,0 +1,66 @@
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/Home.dart';
import 'package:spotube/helpers/server_ipc.dart';
import 'package:spotube/models/LocalStorageKeys.dart';
import 'package:spotube/provider/Auth.dart';
const redirectUri = "http://localhost:4304/auth/spotify/callback";
Future<void> oauthLogin(BuildContext context,
{required String clientId, required String clientSecret}) async {
try {
String? accessToken;
String? refreshToken;
DateTime? expiration;
final credentials = SpotifyApiCredentials(clientId, clientSecret);
final grant = SpotifyApi.authorizationCodeGrant(credentials);
final authUri = grant.getAuthorizationUrl(Uri.parse(redirectUri),
scopes: spotifyScopes);
final responseUri = await connectIpc(authUri.toString(), redirectUri);
SharedPreferences localStorage = await SharedPreferences.getInstance();
if (responseUri != null) {
final SpotifyApi spotify =
SpotifyApi.fromAuthCodeGrant(grant, responseUri);
var credentials = await spotify.getCredentials();
if (credentials.accessToken != null) {
accessToken = credentials.accessToken;
await localStorage.setString(
LocalStorageKeys.accessToken, credentials.accessToken!);
}
if (credentials.refreshToken != null) {
refreshToken = credentials.refreshToken;
await localStorage.setString(
LocalStorageKeys.refreshToken, credentials.refreshToken!);
}
if (credentials.expiration != null) {
expiration = credentials.expiration;
await localStorage.setString(LocalStorageKeys.expiration,
credentials.expiration?.toString() ?? "");
}
}
await localStorage.setString(LocalStorageKeys.clientId, clientId);
await localStorage.setString(
LocalStorageKeys.clientSecret,
clientSecret,
);
Provider.of<Auth>(context, listen: false).setAuthState(
clientId: clientId,
clientSecret: clientSecret,
accessToken: accessToken,
refreshToken: refreshToken,
expiration: expiration,
isLoggedIn: true,
);
} catch (e, stack) {
print("[oauthLogin()] $e");
print(stack);
rethrow;
}
}

View File

@ -1,8 +1,7 @@
import 'package:spotify/spotify.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
YoutubeExplode youtube = YoutubeExplode();
Future<Track> toYoutubeTrack(Track track) async {
Future<Track> toYoutubeTrack(YoutubeExplode youtube, Track track) async {
var artistsName = track.artists?.map((ar) => ar.name).toList() ?? [];
String queryString =
"${artistsName.first} - ${track.name}${artistsName.length > 1 ? " feat. ${artistsName.sublist(1).join(" ")}" : ""}";
@ -10,8 +9,11 @@ Future<Track> toYoutubeTrack(Track track) async {
SearchList videos = await youtube.search.getVideos(queryString);
List<Video> matchedVideos = videos.where((video) {
return video.title.contains(track.name!) &&
(track.artists?.every((artist) => video.title.contains(artist.name!)) ??
// the find should be lazy thus everything case insensitive
return video.title.toLowerCase().contains(track.name!.toLowerCase()) &&
(track.artists?.every((artist) => video.title
.toLowerCase()
.contains(artist.name!.toLowerCase())) ??
false);
}).toList();