mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00

* feat: add riverpod based favorite album provider * feat: add album is saved, new releases and tracks providers * feat: add artist related providers * feat: add all categories providers * feat: add lyrics provider * feat: add playlist related providers * feat: add search provider * feat: add view and spotify friends provider * feat: add playlist create and update and favorite handlers * feat: use providers in home screen * chore: fix dart lint issues * feat: use new providers for playlist and albums screen * feat: use providers in artist page * feat: use providers on library page * feat: use provider for playlist and album card and heart button * feat: use provider in search page * feat: use providers in generate playlist * feat: use provider in lyrics screen * feat: use provider for create playlist * feat: use provider in add track dialog * feat: use providers in remaining pages and remove fl_query * fix: remove direct access to provider.value * fix: glitching when loading * fix: user album loading next page indicator * feat: make many provider autoDispose after 5 minutes of no usage * fix: ignore episodes in tracks
78 lines
2.3 KiB
Dart
78 lines
2.3 KiB
Dart
part of '../spotify.dart';
|
|
|
|
class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?>
|
|
with Persistence<SubtitleSimple> {
|
|
SyncedLyricsNotifier() {
|
|
load();
|
|
}
|
|
|
|
@override
|
|
FutureOr<SubtitleSimple> build(track) async {
|
|
final spotify = ref.watch(spotifyProvider);
|
|
if (track == null) {
|
|
throw "No track currently";
|
|
}
|
|
final token = await spotify.getCredentials();
|
|
final res = await http.get(
|
|
Uri.parse(
|
|
"https://spclient.wg.spotify.com/color-lyrics/v2/track/${track.id}?format=json&market=from_token",
|
|
),
|
|
headers: {
|
|
"User-Agent":
|
|
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36",
|
|
"App-platform": "WebPlayer",
|
|
"authorization": "Bearer ${token.accessToken}"
|
|
});
|
|
|
|
if (res.statusCode != 200) {
|
|
throw Exception("Unable to find lyrics");
|
|
}
|
|
final linesRaw = Map.castFrom<dynamic, dynamic, String, dynamic>(
|
|
jsonDecode(res.body),
|
|
)["lyrics"]?["lines"] as List?;
|
|
|
|
final lines = linesRaw?.map((line) {
|
|
return LyricSlice(
|
|
time: Duration(milliseconds: int.parse(line["startTimeMs"])),
|
|
text: line["words"] as String,
|
|
);
|
|
}).toList() ??
|
|
[];
|
|
|
|
return SubtitleSimple(
|
|
lyrics: lines,
|
|
name: track.name!,
|
|
uri: res.request!.url,
|
|
rating: 100,
|
|
);
|
|
}
|
|
|
|
@override
|
|
FutureOr<SubtitleSimple> fromJson(Map<String, dynamic> json) =>
|
|
SubtitleSimple.fromJson(json.castKeyDeep<String>());
|
|
|
|
@override
|
|
Map<String, dynamic> toJson(SubtitleSimple data) => data.toJson();
|
|
}
|
|
|
|
final syncedLyricsDelayProvider = StateProvider<int>((ref) => 0);
|
|
|
|
final syncedLyricsProvider =
|
|
AsyncNotifierProviderFamily<SyncedLyricsNotifier, SubtitleSimple, Track?>(
|
|
() => SyncedLyricsNotifier(),
|
|
);
|
|
|
|
final syncedLyricsMapProvider =
|
|
FutureProvider.family((ref, Track? track) async {
|
|
final syncedLyrics = await ref.watch(syncedLyricsProvider(track).future);
|
|
|
|
final isStaticLyrics =
|
|
syncedLyrics.lyrics.every((l) => l.time == Duration.zero);
|
|
|
|
final lyricsMap = syncedLyrics.lyrics
|
|
.map((lyric) => {lyric.time.inSeconds: lyric.text})
|
|
.reduce((accumulator, lyricSlice) => {...accumulator, ...lyricSlice});
|
|
|
|
return (static: isStaticLyrics, lyricsMap: lyricsMap);
|
|
});
|