crash in windows bug fix (hotkey_manager)

Player shuffle unshuffle support
Lyrics page infinite loading on no accessToken fixed
This commit is contained in:
KR Tirtho 2022-01-13 12:27:39 +06:00
parent 07b1891cb4
commit 7cbbd0346e
7 changed files with 167 additions and 91 deletions

21
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,21 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.19041.0",
"compilerPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30133\\bin\\Hostx64\\x64\\cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}

View File

@ -1 +1,3 @@
{} {
"cmake.configureOnOpen": false
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:spotube/components/Settings.dart';
import 'package:spotube/helpers/artist-to-string.dart'; import 'package:spotube/helpers/artist-to-string.dart';
import 'package:spotube/helpers/getLyrics.dart'; import 'package:spotube/helpers/getLyrics.dart';
import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/Playback.dart';
@ -20,13 +21,16 @@ class _LyricsState extends State<Lyrics> {
Playback playback = context.watch<Playback>(); Playback playback = context.watch<Playback>();
UserPreferences userPreferences = context.watch<UserPreferences>(); UserPreferences userPreferences = context.watch<UserPreferences>();
bool hasToken = (userPreferences.geniusAccessToken != null ||
(userPreferences.geniusAccessToken?.isNotEmpty ?? false));
if (playback.currentTrack != null && if (playback.currentTrack != null &&
userPreferences.geniusAccessToken != null && hasToken &&
playback.currentTrack!.id != _lyrics["id"]) { playback.currentTrack!.id != _lyrics["id"]) {
getLyrics( getLyrics(
playback.currentTrack!.name!, playback.currentTrack!.name!,
artistsToString(playback.currentTrack!.artists ?? []), artistsToString(playback.currentTrack!.artists ?? []),
apiKey: userPreferences.geniusAccessToken, apiKey: userPreferences.geniusAccessToken!,
optimizeQuery: true, optimizeQuery: true,
).then((lyrics) { ).then((lyrics) {
if (lyrics != null) { if (lyrics != null) {
@ -44,12 +48,32 @@ class _LyricsState extends State<Lyrics> {
} }
if (_lyrics["lyrics"] == null && playback.currentTrack != null) { if (_lyrics["lyrics"] == null && playback.currentTrack != null) {
if (!hasToken) {
return Expanded(
child: Center(
child: Column(
children: [
const Text("Genius lyrics API access token isn't set"),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return const Settings();
},
));
},
child: const Text("Add Access Token"))
],
),
));
}
return const Expanded( return const Expanded(
child: Center( child: Center(
child: CircularProgressIndicator.adaptive(), child: CircularProgressIndicator.adaptive(),
), ),
); );
} }
return Expanded( return Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,

View File

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:hotkey_manager/hotkey_manager.dart';
@ -30,14 +31,52 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
double _volume = 0; double _volume = 0;
final List<HotKey> _hotKeys = []; late List<GlobalKeyActions> _hotKeys;
@override @override
void initState() { void initState() {
try {
super.initState(); super.initState();
player = AudioPlayer(); player = AudioPlayer();
_hotKeys = [
GlobalKeyActions(
HotKey(KeyCode.space, scope: HotKeyScope.inapp),
_playOrPause,
),
// causaes crash in Windows for aquiring global hotkey of
// keyboard media buttons
if (!Platform.isWindows) ...[
GlobalKeyActions(
HotKey(KeyCode.mediaPlayPause),
_playOrPause,
),
GlobalKeyActions(HotKey(KeyCode.mediaTrackNext), (key) async {
_movePlaylistPositionBy(1);
}),
GlobalKeyActions(HotKey(KeyCode.mediaTrackPrevious), (key) async {
_movePlaylistPositionBy(-1);
}),
GlobalKeyActions(HotKey(KeyCode.mediaStop), (key) async {
Playback playback = context.read<Playback>();
setState(() {
_isPlaying = false;
_currentTrackId = null;
_duration = null;
_shuffled = false;
});
playback.reset();
})
]
];
WidgetsBinding.instance?.addObserver(this); WidgetsBinding.instance?.addObserver(this);
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance?.addPostFrameCallback(_init);
} catch (e, stack) {
print("[Player.initState()] $e");
print(stack);
}
}
_init(Duration timeStamp) async {
try { try {
setState(() { setState(() {
_volume = player.volume; _volume = player.volume;
@ -77,44 +116,8 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
} }
}); });
playOrPause(key) async {
try {
_isPlaying ? await player.pause() : await player.play();
} catch (e, stack) {
print("[PlayPauseShortcut] $e");
print(stack);
}
}
List<GlobalKeyActions> keyWithActions = [
GlobalKeyActions(
HotKey(KeyCode.space, scope: HotKeyScope.inapp),
playOrPause,
),
GlobalKeyActions(
HotKey(KeyCode.mediaPlayPause),
playOrPause,
),
GlobalKeyActions(HotKey(KeyCode.mediaTrackNext), (key) async {
_movePlaylistPositionBy(1);
}),
GlobalKeyActions(HotKey(KeyCode.mediaTrackPrevious), (key) async {
_movePlaylistPositionBy(-1);
}),
GlobalKeyActions(HotKey(KeyCode.mediaStop), (key) async {
Playback playback = context.read<Playback>();
setState(() {
_isPlaying = false;
_currentTrackId = null;
_duration = null;
_shuffled = false;
});
playback.reset();
})
];
await Future.wait( await Future.wait(
keyWithActions.map((e) { _hotKeys.map((e) {
return hotKeyManager.register( return hotKeyManager.register(
e.hotKey, e.hotKey,
keyDownHandler: e.onKeyDown, keyDownHandler: e.onKeyDown,
@ -122,16 +125,15 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
}), }),
); );
} catch (e) { } catch (e) {
print("[Player.initState()]: $e"); print("[Player._init()]: $e");
} }
});
} }
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance?.removeObserver(this); WidgetsBinding.instance?.removeObserver(this);
player.dispose(); player.dispose();
Future.wait(_hotKeys.map((e) => hotKeyManager.unregister(e))); Future.wait(_hotKeys.map((e) => hotKeyManager.unregister(e.hotKey)));
super.dispose(); super.dispose();
} }
@ -145,6 +147,15 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
} }
} }
_playOrPause(key) async {
try {
_isPlaying ? await player.pause() : await player.play();
} catch (e, stack) {
print("[PlayPauseShortcut] $e");
print(stack);
}
}
void _movePlaylistPositionBy(int pos) { void _movePlaylistPositionBy(int pos) {
Playback playback = context.read<Playback>(); Playback playback = context.read<Playback>();
if (playback.currentTrack != null && playback.currentPlaylist != null) { if (playback.currentTrack != null && playback.currentPlaylist != null) {
@ -303,19 +314,19 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
} }
}, },
onShuffle: () async { onShuffle: () async {
if (playback.currentTrack == null ||
playback.currentPlaylist == null) return;
try { try {
if (!_shuffled) { if (!_shuffled) {
await player.setShuffleModeEnabled(true).then( playback.currentPlaylist!.shuffle();
(value) => setState(() { setState(() {
_shuffled = true; _shuffled = true;
}), });
);
} else { } else {
await player.setShuffleModeEnabled(false).then( playback.currentPlaylist!.unshuffle();
(value) => setState(() { setState(() {
_shuffled = false; _shuffled = false;
}), });
);
} }
} catch (e, stack) { } catch (e, stack) {
print("[PlayerControls.onShuffle()] $e"); print("[PlayerControls.onShuffle()] $e");

View File

@ -5,6 +5,7 @@ import 'package:url_launcher/url_launcher.dart';
Future<String?> connectIpc(String authUri, String redirectUri) async { Future<String?> connectIpc(String authUri, String redirectUri) async {
try { try {
if (await canLaunch(authUri)) { if (await canLaunch(authUri)) {
print("[Launching]: $authUri");
await launch(authUri); await launch(authUri);
} }

View File

@ -2,18 +2,35 @@ import 'package:flutter/cupertino.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
class CurrentPlaylist { class CurrentPlaylist {
List<Track>? _tempTrack;
List<Track> tracks; List<Track> tracks;
String id; String id;
String name; String name;
String thumbnail; String thumbnail;
CurrentPlaylist({ CurrentPlaylist({
required List<Track> this.tracks, required this.tracks,
required String this.id, required this.id,
required String this.name, required this.name,
required String this.thumbnail, required this.thumbnail,
}); });
List<String> get trackIds => tracks.map((e) => e.id!).toList(); List<String> get trackIds => tracks.map((e) => e.id!).toList();
void shuffle() {
// won't shuffle if already shuffled
if (_tempTrack == null) {
_tempTrack = [...tracks];
tracks.shuffle();
}
}
void unshuffle() {
// without _tempTracks unshuffling can't be done
if (_tempTrack != null) {
tracks = [..._tempTrack!];
_tempTrack = null;
}
}
} }
class Playback extends ChangeNotifier { class Playback extends ChangeNotifier {

View File

@ -16,7 +16,7 @@ class UserPreferences extends ChangeNotifier {
} }
} }
get geniusAccessToken => _geniusAccessToken; String? get geniusAccessToken => _geniusAccessToken;
setGeniusAccessToken(String? token) { setGeniusAccessToken(String? token) {
_geniusAccessToken = token; _geniusAccessToken = token;