Improvement on remote track metadata

This commit is contained in:
2025-12-19 23:51:02 +08:00
parent 2f1130b424
commit cb4cca2917
7 changed files with 300 additions and 190 deletions

View File

@@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
@@ -8,10 +9,12 @@ import 'package:groovybox/data/playlist_repository.dart';
import 'package:groovybox/data/track_repository.dart';
import 'package:groovybox/logic/lyrics_parser.dart';
import 'package:groovybox/providers/audio_provider.dart';
import 'package:groovybox/providers/remote_provider.dart';
import 'package:groovybox/providers/watch_folder_provider.dart';
import 'package:groovybox/ui/screens/settings_screen.dart';
import 'package:groovybox/ui/tabs/albums_tab.dart';
import 'package:groovybox/ui/tabs/playlists_tab.dart';
import 'package:http/http.dart' as http;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:path/path.dart' as p;
import 'package:styled_widget/styled_widget.dart';
@@ -483,24 +486,7 @@ class LibraryScreen extends HookConsumerWidget {
child: ListTile(
leading: AspectRatio(
aspectRatio: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(8),
image: track.artUri != null
? DecorationImage(
image: FileImage(File(track.artUri!)),
fit: BoxFit.cover,
)
: null,
),
child: track.artUri == null
? const Icon(
Icons.music_note,
color: Colors.white54,
)
: null,
),
child: _buildAlbumArt(track, ref),
),
title: Text(
track.title,
@@ -867,6 +853,81 @@ class LibraryScreen extends HookConsumerWidget {
);
}
Widget _buildAlbumArt(Track track, WidgetRef ref) {
// Check if this is a remote track
final urlResolver = ref.watch(remoteUrlResolverProvider);
final isRemote = urlResolver.isProtocolUrl(track.path);
if (isRemote && track.artUri != null) {
// For remote tracks, fetch album art directly
return FutureBuilder<Uint8List?>(
future: _fetchRemoteAlbumArt(track.artUri!),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Container(
color: Colors.grey[800],
child: const Center(
child: SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white54),
),
),
),
);
} else if (snapshot.hasData && snapshot.data != null) {
return Container(
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(8),
image: DecorationImage(
image: MemoryImage(snapshot.data!),
fit: BoxFit.cover,
),
),
);
} else {
return Container(
color: Colors.grey[800],
child: const Icon(Icons.music_note, color: Colors.white54),
);
}
},
);
} else {
// For local tracks, use existing logic
return Container(
decoration: BoxDecoration(
color: Colors.grey[800],
borderRadius: BorderRadius.circular(8),
image: track.artUri != null
? DecorationImage(
image: FileImage(File(track.artUri!)),
fit: BoxFit.cover,
)
: null,
),
child: track.artUri == null
? const Icon(Icons.music_note, color: Colors.white54)
: null,
);
}
}
Future<Uint8List?> _fetchRemoteAlbumArt(String url) async {
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
return response.bodyBytes;
}
} catch (e) {
// Ignore errors
}
return null;
}
String _formatDuration(int? durationMs) {
if (durationMs == null) return '--:--';
final d = Duration(milliseconds: durationMs);

View File

@@ -49,7 +49,10 @@ class PlayerScreen extends HookConsumerWidget {
final media = medias[index];
final path = Uri.decodeFull(Uri.parse(media.uri).path);
final metadataAsync = ref.watch(trackMetadataProvider(path));
// For now, skip metadata loading to avoid provider issues
final AsyncValue<TrackMetadata> metadataAsync = AsyncValue.data(
TrackMetadata(),
);
// Build blurred background if cover art is available
Widget? background;
@@ -491,9 +494,10 @@ class _PlayerLyrics extends HookConsumerWidget {
? ref.watch(trackByPathProvider(trackPath!))
: const AsyncValue<db.Track?>.data(null);
final metadataAsync = trackPath != null
? ref.watch(trackMetadataProvider(trackPath!))
: const AsyncValue<TrackMetadata?>.data(null);
// For now, skip metadata loading to avoid provider issues
final AsyncValue<TrackMetadata> metadataAsync = AsyncValue.data(
TrackMetadata(),
);
final lyricsFetcher = ref.watch(lyricsFetcherProvider);
final musixmatchProviderInstance = ref.watch(musixmatchProvider);
@@ -809,7 +813,10 @@ class _LyricsAdjustButton extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final trackAsync = ref.watch(trackByPathProvider(trackPath));
final metadataAsync = ref.watch(trackMetadataProvider(trackPath));
// For now, skip metadata loading to avoid provider issues
final AsyncValue<TrackMetadata> metadataAsync = AsyncValue.data(
TrackMetadata(),
);
final musixmatchProviderInstance = ref.watch(musixmatchProvider);
final neteaseProviderInstance = ref.watch(neteaseProvider);

View File

@@ -50,12 +50,13 @@ class _MobileMiniPlayer extends HookConsumerWidget {
return const SizedBox.shrink();
}
final media = medias[index];
final path = Uri.parse(media.uri).path;
final filePath = Uri.decodeFull(path);
final devicePadding = MediaQuery.paddingOf(context);
final metadataAsync = ref.watch(trackMetadataProvider(filePath));
// For now, skip metadata loading to avoid provider issues
final AsyncValue<TrackMetadata> metadataAsync = AsyncValue.data(
TrackMetadata(),
);
Widget content = Container(
height: 72 + devicePadding.bottom,
@@ -268,12 +269,13 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
return const SizedBox.shrink();
}
final media = medias[index];
final path = Uri.parse(media.uri).path;
final filePath = Uri.decodeFull(path);
final devicePadding = MediaQuery.paddingOf(context);
final metadataAsync = ref.watch(trackMetadataProvider(filePath));
// For now, skip metadata loading to avoid provider issues
final AsyncValue<TrackMetadata> metadataAsync = AsyncValue.data(
TrackMetadata(),
);
Widget content = Container(
height: 72 + devicePadding.bottom,
@@ -631,9 +633,8 @@ class _DesktopMiniPlayer extends HookConsumerWidget {
final trackPath = Uri.decodeFull(
Uri.parse(media.uri).path,
);
final trackAsync = ref.watch(
trackByPathProvider(trackPath),
);
// For now, skip track loading to avoid provider issues
final trackAsync = AsyncValue<db.Track?>.data(null);
return trackAsync.when(
loading: () => SizedBox(