✨ Improvement on remote track metadata
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user