// lib/metadata/metadata_normalizer.dart import 'metadata_model.dart'; class MetadataNormalizer { NormalizedMetadata normalize(RawMetadata raw, {String fileName = '', String filePath = ''}) { // 1. 修剪 final title = raw.title.trim(); final artist = raw.artist.trim(); // 2. 如果 title 为空,从文件名推断 final finalTitle = title.isNotEmpty ? title : _inferTitleFromFileName(fileName); // 3. 如果 artist 为空,从路径推断 final finalArtist = artist.isNotEmpty ? artist : _inferArtistFromPath(filePath); // 4. 如果 performers 不为空且 artist 为空,使用 performers final finalArtist2 = finalArtist.isNotEmpty ? finalArtist : (raw.performers.isNotEmpty ? raw.performers.first : '未知艺术家'); return NormalizedMetadata( title: finalTitle, artist: finalArtist2, album: raw.album, genres: raw.genres, performers: raw.performers, year: raw.year, trackNumber: raw.trackNumber, trackTotal: raw.trackTotal, discNumber: raw.discNumber, totalDisc: raw.totalDisc, duration: raw.duration, artwork: raw.artwork, ); } String _inferTitleFromFileName(String fileName) { final dotIndex = fileName.lastIndexOf('.'); return dotIndex > 0 ? fileName.substring(0, dotIndex).trim() : fileName.trim(); } String _inferArtistFromPath(String filePath) { final parts = filePath.split(RegExp(r'[/\\]')); if (parts.length >= 2) { final candidate = parts[parts.length - 2].trim(); if (candidate.isNotEmpty && !candidate.startsWith('http')) { return candidate; } } return '未知艺术家'; } CandidateMetadata evaluate( NormalizedMetadata normalized, List> history) { return CandidateMetadata( metadata: normalized, confidence: 0.8, evidence: {'normalized': 0.8}, ); } FinalMetadata decide(CandidateMetadata candidate, {double threshold = 0.6}) { return FinalMetadata( title: candidate.metadata.title, artist: candidate.metadata.artist, album: candidate.metadata.album, genre: candidate.metadata.genres.isNotEmpty ? candidate.metadata.genres.first : '', genres: candidate.metadata.genres, performers: candidate.metadata.performers, year: candidate.metadata.year, trackNumber: candidate.metadata.trackNumber, trackTotal: candidate.metadata.trackTotal, discNumber: candidate.metadata.discNumber, totalDisc: candidate.metadata.totalDisc, duration: candidate.metadata.duration, artwork: candidate.metadata.artwork, source: 'normalized', ); } }