✨ 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
|
// Utility function
|
||||||
String formatTime(dynamic time) {
|
String formatTime(dynamic time) {
|
||||||
final seconds = time.toInt();
|
final seconds = time.toInt();
|
||||||
|
|||||||
@@ -120,3 +120,4 @@ class LyricsFetcherState {
|
|||||||
// Providers for each LRC provider
|
// Providers for each LRC provider
|
||||||
final musixmatchProvider = Provider((ref) => MusixmatchProvider());
|
final musixmatchProvider = Provider((ref) => MusixmatchProvider());
|
||||||
final neteaseProvider = Provider((ref) => NetEaseProvider());
|
final neteaseProvider = Provider((ref) => NetEaseProvider());
|
||||||
|
final lrclibProvider = Provider((ref) => LrclibProvider());
|
||||||
|
|||||||
@@ -516,6 +516,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
|||||||
final lyricsFetcher = ref.watch(lyricsFetcherProvider);
|
final lyricsFetcher = ref.watch(lyricsFetcherProvider);
|
||||||
final musixmatchProviderInstance = ref.watch(musixmatchProvider);
|
final musixmatchProviderInstance = ref.watch(musixmatchProvider);
|
||||||
final neteaseProviderInstance = ref.watch(neteaseProvider);
|
final neteaseProviderInstance = ref.watch(neteaseProvider);
|
||||||
|
final lrclibProviderInstance = ref.watch(lrclibProvider);
|
||||||
|
|
||||||
// Simulate async behavior for compatibility
|
// Simulate async behavior for compatibility
|
||||||
if (currentTrack == null) {
|
if (currentTrack == null) {
|
||||||
@@ -543,6 +544,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
|||||||
lyricsFetcher,
|
lyricsFetcher,
|
||||||
musixmatchProviderInstance,
|
musixmatchProviderInstance,
|
||||||
neteaseProviderInstance,
|
neteaseProviderInstance,
|
||||||
|
lrclibProviderInstance,
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -555,6 +557,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
|||||||
dynamic metadataObj,
|
dynamic metadataObj,
|
||||||
musixmatchProvider,
|
musixmatchProvider,
|
||||||
neteaseProvider,
|
neteaseProvider,
|
||||||
|
lrclibProvider,
|
||||||
) {
|
) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -565,6 +568,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
|||||||
ref: ref,
|
ref: ref,
|
||||||
musixmatchProvider: musixmatchProvider,
|
musixmatchProvider: musixmatchProvider,
|
||||||
neteaseProvider: neteaseProvider,
|
neteaseProvider: neteaseProvider,
|
||||||
|
lrclibProvider: lrclibProvider,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -576,6 +580,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
|||||||
dynamic lyricsFetcher,
|
dynamic lyricsFetcher,
|
||||||
dynamic musixmatchProviderInstance,
|
dynamic musixmatchProviderInstance,
|
||||||
dynamic neteaseProviderInstance,
|
dynamic neteaseProviderInstance,
|
||||||
|
dynamic lrclibProviderInstance,
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
) {
|
) {
|
||||||
// Get lyrics mode setting
|
// Get lyrics mode setting
|
||||||
@@ -614,6 +619,7 @@ class _PlayerLyrics extends HookConsumerWidget {
|
|||||||
metadataAsync.value,
|
metadataAsync.value,
|
||||||
musixmatchProviderInstance,
|
musixmatchProviderInstance,
|
||||||
neteaseProviderInstance,
|
neteaseProviderInstance,
|
||||||
|
lrclibProviderInstance,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -701,6 +707,7 @@ class _FetchLyricsDialog extends StatelessWidget {
|
|||||||
final WidgetRef ref;
|
final WidgetRef ref;
|
||||||
final LrcProvider musixmatchProvider;
|
final LrcProvider musixmatchProvider;
|
||||||
final LrcProvider neteaseProvider;
|
final LrcProvider neteaseProvider;
|
||||||
|
final LrcProvider lrclibProvider;
|
||||||
|
|
||||||
const _FetchLyricsDialog({
|
const _FetchLyricsDialog({
|
||||||
required this.track,
|
required this.track,
|
||||||
@@ -709,6 +716,7 @@ class _FetchLyricsDialog extends StatelessWidget {
|
|||||||
required this.ref,
|
required this.ref,
|
||||||
required this.musixmatchProvider,
|
required this.musixmatchProvider,
|
||||||
required this.neteaseProvider,
|
required this.neteaseProvider,
|
||||||
|
required this.lrclibProvider,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@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(
|
ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
leading: const Icon(Symbols.file_upload),
|
leading: const Icon(Symbols.file_upload),
|
||||||
@@ -857,6 +884,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
final musixmatchProviderInstance = ref.watch(musixmatchProvider);
|
final musixmatchProviderInstance = ref.watch(musixmatchProvider);
|
||||||
final neteaseProviderInstance = ref.watch(neteaseProvider);
|
final neteaseProviderInstance = ref.watch(neteaseProvider);
|
||||||
|
final lrclibProviderInstance = ref.watch(lrclibProvider);
|
||||||
|
|
||||||
// Don't show the button if there's no current track
|
// Don't show the button if there's no current track
|
||||||
if (currentTrack == null) {
|
if (currentTrack == null) {
|
||||||
@@ -874,6 +902,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
|||||||
metadataAsync,
|
metadataAsync,
|
||||||
musixmatchProviderInstance,
|
musixmatchProviderInstance,
|
||||||
neteaseProviderInstance,
|
neteaseProviderInstance,
|
||||||
|
lrclibProviderInstance,
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
);
|
);
|
||||||
@@ -887,6 +916,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
|||||||
dynamic metadataObj,
|
dynamic metadataObj,
|
||||||
musixmatchProvider,
|
musixmatchProvider,
|
||||||
neteaseProvider,
|
neteaseProvider,
|
||||||
|
lrclibProvider,
|
||||||
) {
|
) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -897,6 +927,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
|||||||
ref: ref,
|
ref: ref,
|
||||||
musixmatchProvider: musixmatchProvider,
|
musixmatchProvider: musixmatchProvider,
|
||||||
neteaseProvider: neteaseProvider,
|
neteaseProvider: neteaseProvider,
|
||||||
|
lrclibProvider: lrclibProvider,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -908,6 +939,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
|||||||
AsyncValue<TrackMetadata> metadataAsync,
|
AsyncValue<TrackMetadata> metadataAsync,
|
||||||
musixmatchProvider,
|
musixmatchProvider,
|
||||||
neteaseProvider,
|
neteaseProvider,
|
||||||
|
lrclibProvider,
|
||||||
) {
|
) {
|
||||||
// Convert CurrentTrackData to db.Track for compatibility
|
// Convert CurrentTrackData to db.Track for compatibility
|
||||||
final track = db.Track(
|
final track = db.Track(
|
||||||
@@ -952,6 +984,7 @@ class _LyricsAdjustButton extends HookConsumerWidget {
|
|||||||
metadata,
|
metadata,
|
||||||
musixmatchProvider,
|
musixmatchProvider,
|
||||||
neteaseProvider,
|
neteaseProvider,
|
||||||
|
lrclibProvider,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -83,8 +83,10 @@ class Shell extends HookConsumerWidget {
|
|||||||
void saveWindowSize() {
|
void saveWindowSize() {
|
||||||
windowManager.getBounds().then((bounds) {
|
windowManager.getBounds().then((bounds) {
|
||||||
final settingsNotifier = ref.read(settingsProvider.notifier);
|
final settingsNotifier = ref.read(settingsProvider.notifier);
|
||||||
|
Future(() {
|
||||||
settingsNotifier.setWindowSize(bounds.size);
|
settingsNotifier.setWindowSize(bounds.size);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save window size when app is about to close
|
// Save window size when app is about to close
|
||||||
|
|||||||
Reference in New Issue
Block a user