mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
re-initiate OAuth process on Refresh Token revoke
YouTube & Spotify track matching accuracy increase
This commit is contained in:
parent
e1c9e6fb29
commit
93d95cb309
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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!)))
|
||||
|
66
lib/helpers/oauth-login.dart
Normal file
66
lib/helpers/oauth-login.dart
Normal 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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user