Lrclib as provider

This commit is contained in:
2025-12-20 15:06:41 +08:00
parent 276d20b0f5
commit 7fe03f90a8
4 changed files with 78 additions and 1 deletions

View File

@@ -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();

View File

@@ -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());

View File

@@ -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,
);
},
),

View File

@@ -83,8 +83,10 @@ class Shell extends HookConsumerWidget {
void saveWindowSize() {
windowManager.getBounds().then((bounds) {
final settingsNotifier = ref.read(settingsProvider.notifier);
Future(() {
settingsNotifier.setWindowSize(bounds.size);
});
});
}
// Save window size when app is about to close