✨ Lrclib as provider
This commit is contained in:
@@ -226,6 +226,47 @@ class NetEaseProvider extends LrcProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/// Lrclib provider
|
||||
class LrclibProvider extends LrcProvider {
|
||||
static const String rootUrl = "https://lrclib.net";
|
||||
static const String apiEndpoint = "$rootUrl/api";
|
||||
static const String searchEndpoint = "$apiEndpoint/search";
|
||||
static const String lrcEndpoint = "$apiEndpoint/get/";
|
||||
|
||||
@override
|
||||
String get name => 'Lrclib';
|
||||
|
||||
Future<Lyrics?> getLrcById(String trackId) async {
|
||||
final url = lrcEndpoint + trackId;
|
||||
final response = await session.get(url);
|
||||
if (response.statusCode != 200) return null;
|
||||
final track = response.data;
|
||||
final synced = track['syncedLyrics'];
|
||||
final plain = track['plainLyrics'];
|
||||
return Lyrics(synced: synced, plain: plain);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Lyrics?> getLrc(String searchTerm) async {
|
||||
final response = await session.get(
|
||||
searchEndpoint,
|
||||
queryParameters: {'q': searchTerm},
|
||||
);
|
||||
if (response.statusCode != 200) return null;
|
||||
final tracks = response.data as List<dynamic>;
|
||||
if (tracks.isEmpty) return null;
|
||||
// Find first track with syncedLyrics not empty
|
||||
for (final track in tracks) {
|
||||
final synced = track['syncedLyrics'];
|
||||
if (synced != null && synced.trim().isNotEmpty) {
|
||||
final id = track['id'].toString();
|
||||
return await getLrcById(id);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function
|
||||
String formatTime(dynamic time) {
|
||||
final seconds = time.toInt();
|
||||
|
||||
@@ -120,3 +120,4 @@ class LyricsFetcherState {
|
||||
// Providers for each LRC provider
|
||||
final musixmatchProvider = Provider((ref) => MusixmatchProvider());
|
||||
final neteaseProvider = Provider((ref) => NetEaseProvider());
|
||||
final lrclibProvider = Provider((ref) => LrclibProvider());
|
||||
|
||||
@@ -516,6 +516,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
||||
final lyricsFetcher = ref.watch(lyricsFetcherProvider);
|
||||
final musixmatchProviderInstance = ref.watch(musixmatchProvider);
|
||||
final neteaseProviderInstance = ref.watch(neteaseProvider);
|
||||
final lrclibProviderInstance = ref.watch(lrclibProvider);
|
||||
|
||||
// Simulate async behavior for compatibility
|
||||
if (currentTrack == null) {
|
||||
@@ -543,6 +544,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
||||
lyricsFetcher,
|
||||
musixmatchProviderInstance,
|
||||
neteaseProviderInstance,
|
||||
lrclibProviderInstance,
|
||||
context,
|
||||
);
|
||||
}
|
||||
@@ -555,6 +557,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
||||
dynamic metadataObj,
|
||||
musixmatchProvider,
|
||||
neteaseProvider,
|
||||
lrclibProvider,
|
||||
) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -565,6 +568,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
||||
ref: ref,
|
||||
musixmatchProvider: musixmatchProvider,
|
||||
neteaseProvider: neteaseProvider,
|
||||
lrclibProvider: lrclibProvider,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -576,6 +580,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
||||
dynamic lyricsFetcher,
|
||||
dynamic musixmatchProviderInstance,
|
||||
dynamic neteaseProviderInstance,
|
||||
dynamic lrclibProviderInstance,
|
||||
BuildContext context,
|
||||
) {
|
||||
// Get lyrics mode setting
|
||||
@@ -614,6 +619,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
||||
metadataAsync.value,
|
||||
musixmatchProviderInstance,
|
||||
neteaseProviderInstance,
|
||||
lrclibProviderInstance,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -701,6 +707,7 @@ class _FetchLyricsDialog extends StatelessWidget {
|
||||
final WidgetRef ref;
|
||||
final LrcProvider musixmatchProvider;
|
||||
final LrcProvider neteaseProvider;
|
||||
final LrcProvider lrclibProvider;
|
||||
|
||||
const _FetchLyricsDialog({
|
||||
required this.track,
|
||||
@@ -709,6 +716,7 @@ class _FetchLyricsDialog extends StatelessWidget {
|
||||
required this.ref,
|
||||
required this.musixmatchProvider,
|
||||
required this.neteaseProvider,
|
||||
required this.lrclibProvider,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -780,6 +788,25 @@ class _FetchLyricsDialog extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
leading: const Icon(Symbols.library_books),
|
||||
title: const Text('Lrclib'),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
await ref
|
||||
.read(lyricsFetcherProvider.notifier)
|
||||
.fetchLyricsForTrack(
|
||||
trackId: track.id,
|
||||
searchTerm: searchTerm,
|
||||
provider: lrclibProvider,
|
||||
trackPath: trackPath,
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
dense: true,
|
||||
leading: const Icon(Symbols.file_upload),
|
||||
@@ -857,6 +884,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
||||
);
|
||||
final musixmatchProviderInstance = ref.watch(musixmatchProvider);
|
||||
final neteaseProviderInstance = ref.watch(neteaseProvider);
|
||||
final lrclibProviderInstance = ref.watch(lrclibProvider);
|
||||
|
||||
// Don't show the button if there's no current track
|
||||
if (currentTrack == null) {
|
||||
@@ -874,6 +902,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
||||
metadataAsync,
|
||||
musixmatchProviderInstance,
|
||||
neteaseProviderInstance,
|
||||
lrclibProviderInstance,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
);
|
||||
@@ -887,6 +916,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
||||
dynamic metadataObj,
|
||||
musixmatchProvider,
|
||||
neteaseProvider,
|
||||
lrclibProvider,
|
||||
) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -897,6 +927,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
||||
ref: ref,
|
||||
musixmatchProvider: musixmatchProvider,
|
||||
neteaseProvider: neteaseProvider,
|
||||
lrclibProvider: lrclibProvider,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -908,6 +939,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
||||
AsyncValue<TrackMetadata> metadataAsync,
|
||||
musixmatchProvider,
|
||||
neteaseProvider,
|
||||
lrclibProvider,
|
||||
) {
|
||||
// Convert CurrentTrackData to db.Track for compatibility
|
||||
final track = db.Track(
|
||||
@@ -952,6 +984,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
||||
metadata,
|
||||
musixmatchProvider,
|
||||
neteaseProvider,
|
||||
lrclibProvider,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -83,7 +83,9 @@ class Shell extends HookConsumerWidget {
|
||||
void saveWindowSize() {
|
||||
windowManager.getBounds().then((bounds) {
|
||||
final settingsNotifier = ref.read(settingsProvider.notifier);
|
||||
settingsNotifier.setWindowSize(bounds.size);
|
||||
Future(() {
|
||||
settingsNotifier.setWindowSize(bounds.size);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user