mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
fix: amll-ttml-db lyrics searching issue (due to metadata matching calculator error)
This commit is contained in:
@@ -12,6 +12,7 @@ using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||
using BetterLyrics.WinUI3.Services.PlayHistoryService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Services.SMTCService;
|
||||
using BetterLyrics.WinUI3.Services.SongSearchMapService;
|
||||
using BetterLyrics.WinUI3.Services.TranslationService;
|
||||
using BetterLyrics.WinUI3.Services.TransliterationService;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
@@ -127,13 +128,25 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
// 初始化数据库
|
||||
await EnsureDatabasesAsync();
|
||||
await InitDatabasesAsync();
|
||||
|
||||
var settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
// Migrate MappedSongSearchQueries
|
||||
var songSearchMapService = Ioc.Default.GetRequiredService<ISongSearchMapService>();
|
||||
var obsoleteSongSearchMap = settingsService.AppSettings.MappedSongSearchQueries;
|
||||
if (obsoleteSongSearchMap.Count > 0)
|
||||
{
|
||||
foreach (var item in obsoleteSongSearchMap)
|
||||
{
|
||||
await songSearchMapService.SaveMappingAsync(item);
|
||||
}
|
||||
obsoleteSongSearchMap.Clear();
|
||||
}
|
||||
|
||||
// Start scan tasks in background
|
||||
var fileSystemService = Ioc.Default.GetRequiredService<IFileSystemService>();
|
||||
|
||||
// 开始后台扫描任务
|
||||
foreach (var item in settingsService.AppSettings.LocalMediaFolders)
|
||||
{
|
||||
if (item.LastSyncTime == null)
|
||||
@@ -143,10 +156,10 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
fileSystemService.StartAllFolderTimers();
|
||||
|
||||
// 初始化托盘
|
||||
// Init system tray
|
||||
m_window = WindowHook.OpenOrShowWindow<SystemTrayWindow>();
|
||||
|
||||
// 根据设置打开歌词窗口
|
||||
// Open lyrics window if set
|
||||
if (settingsService.AppSettings.GeneralSettings.AutoStartLyricsWindow)
|
||||
{
|
||||
var defaultStatus = settingsService.AppSettings.WindowBoundsRecords.Where(x => x.IsDefault);
|
||||
@@ -163,109 +176,39 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
}
|
||||
|
||||
// 根据设置自动打开主界面
|
||||
// Open music gallery if set
|
||||
if (settingsService.AppSettings.MusicGallerySettings.AutoOpen)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EnsureDatabasesAsync()
|
||||
private async Task InitDatabasesAsync()
|
||||
{
|
||||
// Init databases
|
||||
var playHistoryFactory = Ioc.Default.GetRequiredService<IDbContextFactory<PlayHistoryDbContext>>();
|
||||
var songSearchMapFactory = Ioc.Default.GetRequiredService<IDbContextFactory<SongSearchMapDbContext>>();
|
||||
var filesIndexFactory = Ioc.Default.GetRequiredService<IDbContextFactory<FilesIndexDbContext>>();
|
||||
var lyricsCacheFactory = Ioc.Default.GetRequiredService<IDbContextFactory<LyricsCacheDbContext>>();
|
||||
|
||||
await SafeInitDatabaseAsync(
|
||||
"PlayHistory",
|
||||
PathHelper.PlayHistoryPath,
|
||||
async () =>
|
||||
{
|
||||
using var db = await playHistoryFactory.CreateDbContextAsync();
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
},
|
||||
isCritical: true
|
||||
);
|
||||
|
||||
await SafeInitDatabaseAsync(
|
||||
"FileCache",
|
||||
PathHelper.FilesIndexPath,
|
||||
async () =>
|
||||
{
|
||||
using var db = await filesIndexFactory.CreateDbContextAsync();
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
},
|
||||
isCritical: false
|
||||
);
|
||||
|
||||
await SafeInitDatabaseAsync(
|
||||
"LyricsCache",
|
||||
PathHelper.FilesIndexPath,
|
||||
async () =>
|
||||
{
|
||||
using var db = await lyricsCacheFactory.CreateDbContextAsync();
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
},
|
||||
isCritical: false
|
||||
);
|
||||
}
|
||||
|
||||
private async Task SafeInitDatabaseAsync(string dbName, string dbPath, Func<Task> initAction, bool isCritical)
|
||||
{
|
||||
try
|
||||
using (var playHistoryDb = await playHistoryFactory.CreateDbContextAsync())
|
||||
{
|
||||
await initAction();
|
||||
await playHistoryDb.Database.EnsureCreatedAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
using (var songSearchMapDb = await songSearchMapFactory.CreateDbContextAsync())
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[DB Error] {dbName} init failed: {ex.Message}");
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(dbPath))
|
||||
{
|
||||
// 尝试清理连接池
|
||||
SqliteConnection.ClearAllPools();
|
||||
|
||||
if (isCritical)
|
||||
{
|
||||
var backupPath = dbPath + ".bak_" + DateTime.Now.ToString("yyyyMMddHHmmss");
|
||||
File.Move(dbPath, backupPath, true);
|
||||
await ShowErrorDialogAsync("Database Recovery", $"Database {dbName} is damaged, the old database has been backed up to {backupPath}, and the program will create a new database.");
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Delete(dbPath);
|
||||
}
|
||||
}
|
||||
await initAction();
|
||||
System.Diagnostics.Debug.WriteLine($"[DB Info] {dbName} recovered successfully.");
|
||||
}
|
||||
catch (Exception fatalEx)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"[] : {fatalEx.Message}");
|
||||
await ShowErrorDialogAsync("Fatal Error", $"{dbName} recovery failed, please delete the file at {dbPath} and try again by restarting the program. ({fatalEx.Message})");
|
||||
}
|
||||
await songSearchMapDb.Database.EnsureCreatedAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowErrorDialogAsync(string title, string content)
|
||||
{
|
||||
// 这里假设 m_window 已经存在。如果没有显示主窗口,这个弹窗可能无法显示。
|
||||
// 在 App 启动极早期的错误,可能需要退化为 Log 或者 System.Diagnostics.Process.Start 打开记事本报错
|
||||
if (m_window != null)
|
||||
using (var filesIndexDb = await filesIndexFactory.CreateDbContextAsync())
|
||||
{
|
||||
m_window.DispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
var dialog = new Microsoft.UI.Xaml.Controls.ContentDialog
|
||||
{
|
||||
Title = title,
|
||||
Content = content,
|
||||
CloseButtonText = "OK",
|
||||
XamlRoot = m_window.Content?.XamlRoot // 确保 Content 不为空
|
||||
};
|
||||
if (dialog.XamlRoot != null) await dialog.ShowAsync();
|
||||
});
|
||||
await filesIndexDb.Database.EnsureCreatedAsync();
|
||||
}
|
||||
|
||||
using (var lyricsCacheDb = await lyricsCacheFactory.CreateDbContextAsync())
|
||||
{
|
||||
await lyricsCacheDb.Database.EnsureCreatedAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +226,7 @@ namespace BetterLyrics.WinUI3
|
||||
.AddDbContextFactory<PlayHistoryDbContext>(options => options.UseSqlite($"Data Source={PathHelper.PlayHistoryPath}"))
|
||||
.AddDbContextFactory<FilesIndexDbContext>(options => options.UseSqlite($"Data Source={PathHelper.FilesIndexPath}"))
|
||||
.AddDbContextFactory<LyricsCacheDbContext>(options => options.UseSqlite($"Data Source={PathHelper.LyricsCachePath}"))
|
||||
.AddDbContextFactory<SongSearchMapDbContext>(options => options.UseSqlite($"Data Source={PathHelper.SongSearchMapPath}"))
|
||||
|
||||
// 日志
|
||||
.AddLogging(loggingBuilder =>
|
||||
@@ -305,6 +249,7 @@ namespace BetterLyrics.WinUI3
|
||||
.AddSingleton<IFileSystemService, FileSystemService>()
|
||||
.AddSingleton<IPlayHistoryService, PlayHistoryService>()
|
||||
.AddSingleton<ILyricsCacheService, LyricsCacheService>()
|
||||
.AddSingleton<ISongSearchMapService, SongSearchMapService>()
|
||||
|
||||
// ViewModels
|
||||
.AddSingleton<AppSettingsControlViewModel>()
|
||||
|
||||
@@ -44,9 +44,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
? $"{local.Title} {local.Artist}"
|
||||
: Path.GetFileNameWithoutExtension(local.LinkedFileName);
|
||||
|
||||
string remoteQuery = remoteHasMetadata
|
||||
string? remoteQuery = remoteHasMetadata
|
||||
? $"{remote.Title} {remote.Artist}"
|
||||
: Path.GetFileNameWithoutExtension(remote.Reference);
|
||||
: null;
|
||||
|
||||
string fp1 = CreateSortedFingerprint(localQuery);
|
||||
string fp2 = CreateSortedFingerprint(remoteQuery);
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static string PlayQueuePath => Path.Combine(LocalFolder, "play-queue.m3u");
|
||||
public static string PlayHistoryPath => Path.Combine(LocalFolder, "play-history.db");
|
||||
public static string FilesIndexPath => Path.Combine(LocalFolder, "files-index.db");
|
||||
public static string SongSearchMapPath => Path.Combine(LocalFolder, "song-search-map.db");
|
||||
public static string LyricsCachePath => Path.Combine(LyricsCacheDirectory, "lyrics-cache.db");
|
||||
|
||||
public static void EnsureDirectories()
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models.Db
|
||||
{
|
||||
public partial class SongSearchMapDbContext : DbContext
|
||||
{
|
||||
public DbSet<MappedSongSearchQuery> SongSearchMap { get; set; }
|
||||
|
||||
public SongSearchMapDbContext(DbContextOptions<SongSearchMapDbContext> options) : base(options) { }
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
[Index(nameof(Uri), IsUnique = true)] // 唯一索引
|
||||
public class FilesIndexItem
|
||||
{
|
||||
[Key] // 主键
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] // 明确指定为自增 (Identity)
|
||||
public int Id { get; set; }
|
||||
[Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; }
|
||||
|
||||
// 关联到 MediaFolder.Id
|
||||
// 注意:作为索引列,必须限制长度,否则 SQL Server 会报错 (索引最大900字节)
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class MappedSongSearchQuery : ObservableRecipient
|
||||
[Table("SongSearchMap")]
|
||||
[Index(nameof(OriginalTitle), nameof(OriginalArtist), nameof(OriginalAlbum))]
|
||||
public partial class MappedSongSearchQuery : ObservableRecipient, ICloneable
|
||||
{
|
||||
[Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)] public string Id { get; set; }
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string OriginalTitle { get; set; } = string.Empty;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string OriginalArtist { get; set; } = string.Empty;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string OriginalAlbum { get; set; } = string.Empty;
|
||||
@@ -17,7 +25,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider? LyricsSearchProvider { get; set; }
|
||||
|
||||
public MappedSongSearchQuery Clone()
|
||||
public object Clone()
|
||||
{
|
||||
return new MappedSongSearchQuery
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using BetterLyrics.WinUI3.Collections;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models.Settings
|
||||
{
|
||||
@@ -14,7 +15,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MediaFolder> LocalMediaFolders { get; set; } = [];
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; } = [];
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MappedSongSearchQuery> MappedSongSearchQueries { get; set; } = [];
|
||||
[Obsolete][ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MappedSongSearchQuery> MappedSongSearchQueries { get; set; } = [];
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<LyricsWindowStatus> WindowBoundsRecords { get; set; } = [];
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<SongsTabInfo> StarredPlaylists { get; set; } = [];
|
||||
|
||||
|
||||
@@ -110,9 +110,6 @@ namespace BetterLyrics.WinUI3.Services.GSMTCService
|
||||
|
||||
_settingsService.AppSettings.LocalMediaFolders.CollectionChanged += LocalMediaFolders_CollectionChanged;
|
||||
|
||||
_settingsService.AppSettings.MappedSongSearchQueries.CollectionChanged += MappedSongSearchQueries_CollectionChanged;
|
||||
_settingsService.AppSettings.MappedSongSearchQueries.ItemPropertyChanged += MappedSongSearchQueries_ItemPropertyChanged;
|
||||
|
||||
InitMediaManager();
|
||||
}
|
||||
|
||||
@@ -151,16 +148,6 @@ namespace BetterLyrics.WinUI3.Services.GSMTCService
|
||||
}
|
||||
}
|
||||
|
||||
private void MappedSongSearchQueries_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
|
||||
{
|
||||
UpdateLyrics();
|
||||
}
|
||||
|
||||
private void MappedSongSearchQueries_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateLyrics();
|
||||
}
|
||||
|
||||
private void LocalMediaFolders_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
UpdateAlbumArt();
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsCacheService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write or update cache to DB
|
||||
/// Write cache to DB
|
||||
/// </summary>
|
||||
public async Task SaveLyricsAsync(SongInfo songInfo, LyricsCacheItem result)
|
||||
{
|
||||
@@ -46,16 +46,17 @@ namespace BetterLyrics.WinUI3.Services.LyricsCacheService
|
||||
var existingItem = await context.LyricsCache
|
||||
.FirstOrDefaultAsync(x => x.CacheKey == key && x.Provider == result.Provider);
|
||||
|
||||
var newItem = (LyricsCacheItem)result.Clone();
|
||||
newItem.CacheKey = key;
|
||||
if (existingItem == null)
|
||||
{
|
||||
var newItem = (LyricsCacheItem)result.Clone();
|
||||
newItem.CacheKey = key;
|
||||
|
||||
if (existingItem != null)
|
||||
{
|
||||
context.LyricsCache.Update(newItem);
|
||||
await context.LyricsCache.AddAsync(newItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.LyricsCache.AddAsync(newItem);
|
||||
// No need to handle this case
|
||||
return;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
@@ -8,6 +8,7 @@ using BetterLyrics.WinUI3.Providers;
|
||||
using BetterLyrics.WinUI3.Services.FileSystemService;
|
||||
using BetterLyrics.WinUI3.Services.LyricsCacheService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Services.SongSearchMapService;
|
||||
using Lyricify.Lyrics.Helpers;
|
||||
using Lyricify.Lyrics.Searchers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -32,18 +33,21 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IFileSystemService _fileSystemService;
|
||||
private readonly ILyricsCacheService _lyricsCacheService;
|
||||
private readonly ISongSearchMapService _songSearchMapService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public LyricsSearchService(
|
||||
ISettingsService settingsService,
|
||||
IFileSystemService fileSystemService,
|
||||
ILyricsCacheService lyricsCacheService,
|
||||
ISettingsService settingsService,
|
||||
IFileSystemService fileSystemService,
|
||||
ILyricsCacheService lyricsCacheService,
|
||||
ISongSearchMapService songSearchMapService,
|
||||
ILogger<LyricsSearchService> logger
|
||||
)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_fileSystemService = fileSystemService;
|
||||
_lyricsCacheService = lyricsCacheService;
|
||||
_songSearchMapService = songSearchMapService;
|
||||
_logger = logger;
|
||||
|
||||
_lrcLibHttpClient = new();
|
||||
@@ -117,11 +121,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
_logger.LogInformation("SearchSmartlyAsync {SongInfo}", songInfo);
|
||||
|
||||
// 先检查该曲目是否已被用户映射
|
||||
var found = _settingsService.AppSettings.MappedSongSearchQueries
|
||||
.FirstOrDefault(x =>
|
||||
x.OriginalTitle == overridenTitle &&
|
||||
x.OriginalArtist == overridenArtist &&
|
||||
x.OriginalAlbum == overridenAlbum);
|
||||
var found = await _songSearchMapService.GetMappingAsync(overridenTitle, overridenArtist, overridenAlbum);
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
@@ -408,7 +408,6 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
}
|
||||
}
|
||||
|
||||
int bestScore = 0;
|
||||
string? rawLyricFile = null;
|
||||
await foreach (var line in File.ReadLinesAsync(PathHelper.AmllTtmlDbIndexPath))
|
||||
{
|
||||
@@ -445,7 +444,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
Artist = artist,
|
||||
Album = album,
|
||||
});
|
||||
if (score > bestScore)
|
||||
if (score > lyricsSearchResult.MatchPercentage)
|
||||
{
|
||||
if (root.TryGetProperty("rawLyricFile", out var rawLyricFileProp))
|
||||
{
|
||||
@@ -453,15 +452,13 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
lyricsSearchResult.Title = title;
|
||||
lyricsSearchResult.Artist = artist;
|
||||
lyricsSearchResult.Album = album;
|
||||
bestScore = score;
|
||||
lyricsSearchResult.MatchPercentage = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rawLyricFile))
|
||||
{
|
||||
return lyricsSearchResult;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.SongSearchMapService
|
||||
{
|
||||
public interface ISongSearchMapService
|
||||
{
|
||||
Task SaveMappingAsync(MappedSongSearchQuery mapping);
|
||||
Task<MappedSongSearchQuery?> GetMappingAsync(string title, string artist, string album);
|
||||
Task DeleteMappingAsync(MappedSongSearchQuery mapping);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Db;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.SongSearchMapService
|
||||
{
|
||||
public class SongSearchMapService : ISongSearchMapService
|
||||
{
|
||||
private readonly IDbContextFactory<SongSearchMapDbContext> _contextFactory;
|
||||
|
||||
public SongSearchMapService(IDbContextFactory<SongSearchMapDbContext> contextFactory)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
public async Task SaveMappingAsync(MappedSongSearchQuery mapping)
|
||||
{
|
||||
using var context = await _contextFactory.CreateDbContextAsync();
|
||||
|
||||
var existing = await context.SongSearchMap
|
||||
.FirstOrDefaultAsync(x =>
|
||||
x.OriginalTitle == mapping.OriginalTitle &&
|
||||
x.OriginalArtist == mapping.OriginalArtist &&
|
||||
x.OriginalAlbum == mapping.OriginalAlbum);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
existing.MappedTitle = mapping.MappedTitle;
|
||||
existing.MappedArtist = mapping.MappedArtist;
|
||||
existing.MappedAlbum = mapping.MappedAlbum;
|
||||
|
||||
existing.IsMarkedAsPureMusic = mapping.IsMarkedAsPureMusic;
|
||||
existing.LyricsSearchProvider = mapping.LyricsSearchProvider;
|
||||
|
||||
context.SongSearchMap.Update(existing);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newItem = (MappedSongSearchQuery)mapping.Clone();
|
||||
await context.SongSearchMap.AddAsync(newItem);
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<MappedSongSearchQuery?> GetMappingAsync(string title, string artist, string album)
|
||||
{
|
||||
using var context = await _contextFactory.CreateDbContextAsync();
|
||||
|
||||
return await context.SongSearchMap
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(x =>
|
||||
x.OriginalTitle == title &&
|
||||
x.OriginalArtist == artist &&
|
||||
x.OriginalAlbum == album);
|
||||
}
|
||||
|
||||
public async Task DeleteMappingAsync(MappedSongSearchQuery mapping)
|
||||
{
|
||||
using var context = await _contextFactory.CreateDbContextAsync();
|
||||
|
||||
var target = await context.SongSearchMap
|
||||
.FirstOrDefaultAsync(x =>
|
||||
x.OriginalTitle == mapping.OriginalTitle &&
|
||||
x.OriginalArtist == mapping.OriginalArtist &&
|
||||
x.OriginalAlbum == mapping.OriginalAlbum);
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
context.SongSearchMap.Remove(target);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Parsers.LyricsParser;
|
||||
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||
using BetterLyrics.WinUI3.Services.GSMTCService;
|
||||
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Services.SongSearchMapService;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
@@ -22,6 +23,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private readonly ILyricsSearchService _lyricsSearchService;
|
||||
private readonly IGSMTCService _gsmtcService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly ISongSearchMapService _songSearchMapService;
|
||||
|
||||
private LatestOnlyTaskRunner _lyricsSearchRunner = new();
|
||||
|
||||
@@ -43,24 +45,34 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial bool IsSearching { get; set; } = false;
|
||||
|
||||
public LyricsSearchControlViewModel(ILyricsSearchService lyricsSearchService, IGSMTCService gsmtcService, ISettingsService settingsService)
|
||||
public LyricsSearchControlViewModel(
|
||||
ILyricsSearchService lyricsSearchService,
|
||||
IGSMTCService gsmtcService,
|
||||
ISettingsService settingsService,
|
||||
ISongSearchMapService songSearchMapService
|
||||
)
|
||||
{
|
||||
_lyricsSearchService = lyricsSearchService;
|
||||
_gsmtcService = gsmtcService;
|
||||
_settingsService = settingsService;
|
||||
_songSearchMapService = songSearchMapService;
|
||||
|
||||
AppSettings = _settingsService.AppSettings;
|
||||
|
||||
InitMappedSongSearchQuery();
|
||||
_ = InitMappedSongSearchQueryAsync();
|
||||
}
|
||||
|
||||
private void InitMappedSongSearchQuery()
|
||||
private async Task InitMappedSongSearchQueryAsync()
|
||||
{
|
||||
LyricsSearchResults.Clear();
|
||||
LyricsDataArr = null;
|
||||
if (_gsmtcService.CurrentSongInfo != null)
|
||||
{
|
||||
var found = GetMappedSongSearchQueryFromSettings();
|
||||
var found = await _songSearchMapService.GetMappingAsync(
|
||||
_gsmtcService.CurrentSongInfo.Title,
|
||||
_gsmtcService.CurrentSongInfo.Artist,
|
||||
_gsmtcService.CurrentSongInfo.Album);
|
||||
|
||||
if (found == null)
|
||||
{
|
||||
MappedSongSearchQuery = new MappedSongSearchQuery
|
||||
@@ -75,27 +87,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
MappedSongSearchQuery = found.Clone();
|
||||
MappedSongSearchQuery = (MappedSongSearchQuery)found.Clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MappedSongSearchQuery? GetMappedSongSearchQueryFromSettings()
|
||||
{
|
||||
if (_gsmtcService.CurrentSongInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var found = AppSettings.MappedSongSearchQueries
|
||||
.FirstOrDefault(x =>
|
||||
x.OriginalTitle == _gsmtcService.CurrentSongInfo.Title &&
|
||||
x.OriginalArtist == _gsmtcService.CurrentSongInfo.Artist &&
|
||||
x.OriginalAlbum == _gsmtcService.CurrentSongInfo.Album);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public void PlayLyricsLine(LyricsLine? value)
|
||||
{
|
||||
if (value?.StartMs == null)
|
||||
@@ -134,32 +130,27 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Save()
|
||||
private async Task SaveAsync()
|
||||
{
|
||||
if (MappedSongSearchQuery == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var existing = GetMappedSongSearchQueryFromSettings();
|
||||
if (existing != null)
|
||||
{
|
||||
AppSettings.MappedSongSearchQueries.Remove(existing);
|
||||
}
|
||||
AppSettings.MappedSongSearchQueries.Add(MappedSongSearchQuery);
|
||||
MappedSongSearchQuery = MappedSongSearchQuery.Clone();
|
||||
await _songSearchMapService.SaveMappingAsync(MappedSongSearchQuery);
|
||||
MappedSongSearchQuery = (MappedSongSearchQuery)MappedSongSearchQuery.Clone();
|
||||
_gsmtcService.UpdateLyrics();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Reset()
|
||||
private async Task ResetAsync()
|
||||
{
|
||||
var existing = GetMappedSongSearchQueryFromSettings();
|
||||
if (existing != null)
|
||||
{
|
||||
AppSettings.MappedSongSearchQueries.Remove(existing);
|
||||
}
|
||||
InitMappedSongSearchQuery();
|
||||
if (MappedSongSearchQuery == null) return;
|
||||
|
||||
await _songSearchMapService.DeleteMappingAsync(MappedSongSearchQuery);
|
||||
await InitMappedSongSearchQueryAsync();
|
||||
SelectedLyricsSearchResult = null;
|
||||
_gsmtcService.UpdateLyrics();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -200,7 +191,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(IGSMTCService.CurrentSongInfo))
|
||||
{
|
||||
InitMappedSongSearchQuery();
|
||||
_ = InitMappedSongSearchQueryAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user