引入部分缓存机制

引入封面图信息

正在进入测试阶段
This commit is contained in:
2026-08-25 22:13:36 +08:00
parent 7bd23bfee6
commit 0e8921742c
11 changed files with 358 additions and 75 deletions
+89 -17
View File
@@ -1,11 +1,14 @@
// lib/metadata/metadata_service.dart
// ⭐ 移除未使用的 import 'dart:io';
import 'package:flutter/foundation.dart'; // ⭐ 添加
import 'dart:io'; // ⭐ 添加这一行
import 'dart:typed_data'; // ⭐ 如果已经有更好
import 'package:flutter/foundation.dart';
import '../database/song_database.dart';
import 'metadata_model.dart';
import 'metadata_reader.dart';
import 'metadata_normalizer.dart';
import 'metadata_cache.dart';
import 'file_provider.dart';
import '../utils/artwork_helper.dart';
class MetadataService {
static final MetadataService _instance = MetadataService._internal();
@@ -14,7 +17,13 @@ class MetadataService {
final MetadataReader _reader = MetadataReader();
final MetadataNormalizer _normalizer = MetadataNormalizer();
final MetadataCache _cache = MetadataCache();
final MetadataCache _memoryCache = MetadataCache();
final SongDatabase _db = SongDatabase();
String _generateSongKey(String url, int fileSize, int modifiedTime) {
final raw = '$url|$fileSize|$modifiedTime';
return raw.hashCode.toString();
}
FileProvider _getProvider(String url) {
if (url.startsWith('http://') || url.startsWith('https://')) {
@@ -29,11 +38,11 @@ class MetadataService {
required String fileId,
bool forceRefresh = false,
}) async {
// 1. 检查缓存
// 1. 内存缓存
if (!forceRefresh) {
final cached = await _cache.get(fileId);
final cached = await _memoryCache.get(fileId);
if (cached != null && cached.isNotEmpty) {
debugPrint('📦 [MetadataService] cache hit: ${cached.title}');
debugPrint('📦 [MetadataService] memory hit: ${cached.title}');
return cached;
}
}
@@ -43,33 +52,96 @@ class MetadataService {
final file = await provider.getFile(url);
if (file == null || !await file.exists()) {
debugPrint(
'⚠️ [MetadataService] file not available, using filename fallback');
debugPrint('⚠️ [MetadataService] file not available, using fallback');
return _fallbackFromFileName(fileName);
}
// 3. 读取 metadata
final stat = await file.stat();
final songKey =
_generateSongKey(url, stat.size, stat.modified.millisecondsSinceEpoch);
// 3. SQLite 查询(高置信度直接使用)
if (!forceRefresh) {
final dbSong = await _db.getSong(songKey);
if (dbSong != null) {
final confidence = (dbSong['confidence'] as num?)?.toDouble() ?? 0.0;
if (confidence >= 0.6) {
debugPrint(
'📦 [MetadataService] SQLite hit: ${dbSong['title']} - ${dbSong['artist']}');
// 读取封面图
final artworkPath = dbSong['artwork_path'] as String?;
Uint8List? artwork;
if (artworkPath != null && artworkPath.isNotEmpty) {
try {
final artFile = File(artworkPath);
if (await artFile.exists()) {
artwork = await artFile.readAsBytes();
}
} catch (e) {
// ignore
}
}
final metadata = FinalMetadata(
title: dbSong['title'] as String? ?? '',
artist: dbSong['artist'] as String? ?? '',
album: dbSong['album'] as String? ?? '',
genre: dbSong['genre'] as String? ?? '',
artwork: artwork,
source: 'database',
);
await _memoryCache.put(fileId, metadata);
return metadata;
}
}
}
// 4. 读取原始 metadata
final raw = await _reader.readRawMetadata(file);
// 4. 标准化
// 5. 标准化
final normalized = _normalizer.normalize(
raw,
fileName: fileName,
filePath: url,
);
// 5. 评估(第一阶段:直接接受)
final history = await _cache.getHistory(normalized.artist, limit: 10);
// 6. 评估
final history = await _db.getSongsByArtist(normalized.artist, limit: 10);
final candidate = _normalizer.evaluate(normalized, history);
final finalMetadata = _normalizer.decide(candidate);
// 6. 缓存
if (finalMetadata.isNotEmpty) {
await _cache.put(fileId, finalMetadata);
debugPrint(
'✅ [MetadataService] metadata saved: ${finalMetadata.title} - ${finalMetadata.artist}');
// 7. 保存封面图到本地
String? artworkPath;
if (finalMetadata.artwork != null && finalMetadata.artwork!.isNotEmpty) {
artworkPath =
await ArtworkHelper.saveArtwork(finalMetadata.artwork!, songKey);
}
// 8. 保存到 SQLite
await _db.insertSong({
'song_key': songKey,
'remote_path': url,
'file_size': stat.size,
'modified_time': stat.modified.millisecondsSinceEpoch,
'title': finalMetadata.title,
'artist': finalMetadata.artist,
'album': finalMetadata.album,
'genre': finalMetadata.genre,
'artwork_path': artworkPath,
'confidence': candidate.confidence,
'validation_count': 1,
'first_scan': DateTime.now().millisecondsSinceEpoch,
'last_scan': DateTime.now().millisecondsSinceEpoch,
'metadata_status': candidate.confidence >= 0.6 ? 'verified' : 'pending',
});
// 9. 内存缓存
await _memoryCache.put(fileId, finalMetadata);
debugPrint(
'✅ [MetadataService] saved: ${finalMetadata.title} - ${finalMetadata.artist} (confidence: ${candidate.confidence})');
return finalMetadata;
}