diff --git a/NLyric/Audio/Album.cs b/NLyric/Audio/Album.cs index 9f2bc1c..5fe03aa 100644 --- a/NLyric/Audio/Album.cs +++ b/NLyric/Audio/Album.cs @@ -16,9 +16,9 @@ public class Album : ITrackOrAlbum { public int? Year => _year; public Album(string name, string[] artists, int? trackCount, int? year) { - if (name == null) + if (name is null) throw new ArgumentNullException(nameof(name)); - if (artists == null) + if (artists is null) throw new ArgumentNullException(nameof(artists)); _name = name; @@ -33,7 +33,7 @@ public Album(string name, string[] artists, int? trackCount, int? year) { /// /// 为空时,是否从 获取艺术家 public Album(ATL.Track track, bool getArtistsFromTrack) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); if (!HasAlbumInfo(track)) throw new ArgumentException(nameof(track) + " 中不存在专辑信息"); @@ -52,7 +52,7 @@ public Album(ATL.Track track, bool getArtistsFromTrack) { } public static bool HasAlbumInfo(ATL.Track track) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); return !string.IsNullOrWhiteSpace(track.Album); diff --git a/NLyric/Audio/Track.cs b/NLyric/Audio/Track.cs index cb43bc4..a05dde1 100644 --- a/NLyric/Audio/Track.cs +++ b/NLyric/Audio/Track.cs @@ -10,9 +10,9 @@ public class Track : ITrackOrAlbum { public string[] Artists => _artists; public Track(string name, string[] artists) { - if (name == null) + if (name is null) throw new ArgumentNullException(nameof(name)); - if (artists == null) + if (artists is null) throw new ArgumentNullException(nameof(artists)); _name = name; @@ -20,7 +20,7 @@ public Track(string name, string[] artists) { } public Track(ATL.Track track) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); _name = track.Title.GetSafeString(); diff --git a/NLyric/Caches/AlbumCache.cs b/NLyric/Caches/AlbumCache.cs index 56de5c1..38055f4 100644 --- a/NLyric/Caches/AlbumCache.cs +++ b/NLyric/Caches/AlbumCache.cs @@ -17,7 +17,7 @@ public AlbumCache(Album album, int id) : this(album.Name, id) { } public AlbumCache(string name, int id) { - if (name == null) + if (name is null) throw new ArgumentNullException(nameof(name)); Name = name; diff --git a/NLyric/Caches/Extensions.cs b/NLyric/Caches/Extensions.cs index 44ea764..db78a3a 100644 --- a/NLyric/Caches/Extensions.cs +++ b/NLyric/Caches/Extensions.cs @@ -6,25 +6,25 @@ namespace NLyric.Caches { public static class Extensions { public static AlbumCache Match(this IEnumerable caches, Album album) { - if (album == null) + if (album is null) throw new ArgumentNullException(nameof(album)); return caches.FirstOrDefault(t => IsMatched(t, album)); } public static TrackCache Match(this IEnumerable caches, Track track, Album album) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); - if (album == null) + if (album is null) throw new ArgumentNullException(nameof(album)); return caches.FirstOrDefault(t => IsMatched(t, track, album)); } public static TrackCache Match(this IEnumerable caches, Track track, string fileName) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); - if (fileName == null) + if (fileName is null) throw new ArgumentNullException(nameof(fileName)); return caches.FirstOrDefault(t => IsMatched(t, track, fileName)); @@ -35,25 +35,25 @@ public static LyricCache Match(this IEnumerable caches, int id) { } public static bool IsMatched(this AlbumCache cache, Album album) { - if (album == null) + if (album is null) throw new ArgumentNullException(nameof(album)); return cache.Name == album.Name; } public static bool IsMatched(this TrackCache cache, Track track, Album album) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); - if (album == null) + if (album is null) throw new ArgumentNullException(nameof(album)); return cache.Name == track.Name && cache.AlbumName == album.Name && cache.Artists.SequenceEqual(track.Artists); } public static bool IsMatched(this TrackCache cache, Track track, string fileName) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); - if (fileName == null) + if (fileName is null) throw new ArgumentNullException(nameof(fileName)); return cache.Name == track.Name && cache.FileName == fileName && cache.Artists.SequenceEqual(track.Artists); diff --git a/NLyric/Caches/LyricCache.cs b/NLyric/Caches/LyricCache.cs index 8e427a8..0b5e81b 100644 --- a/NLyric/Caches/LyricCache.cs +++ b/NLyric/Caches/LyricCache.cs @@ -25,7 +25,7 @@ public LyricCache(NcmLyric lyric, string checkSum) : this(lyric.Id, lyric.IsAbso } public LyricCache(int id, bool isAbsoluteMusic, int rawVersion, int translatedVersion, string checkSum) { - if (checkSum == null) + if (checkSum is null) throw new ArgumentNullException(nameof(checkSum)); Id = id; diff --git a/NLyric/Caches/TrackCache.cs b/NLyric/Caches/TrackCache.cs index 3c433c0..ffbd561 100644 --- a/NLyric/Caches/TrackCache.cs +++ b/NLyric/Caches/TrackCache.cs @@ -26,11 +26,11 @@ public TrackCache(Track track, string fileName, int id) : this(track.Name, track } public TrackCache(string name, string[] artists, string albumName, string fileName, int id) { - if (name == null) + if (name is null) throw new ArgumentNullException(nameof(name)); - if (artists == null) + if (artists is null) throw new ArgumentNullException(nameof(artists)); - if (albumName == null && fileName == null) + if (albumName is null && fileName is null) throw new ArgumentException($"{nameof(albumName)} 和 {nameof(fileName)} 不能同时为 null"); Name = name; diff --git a/NLyric/ChineseConverter.cs b/NLyric/ChineseConverter.cs new file mode 100644 index 0000000..43736f5 --- /dev/null +++ b/NLyric/ChineseConverter.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; + +namespace NLyric { + internal static class ChineseConverter { + private static readonly Dictionary _traditionalToSimplifiedMap; + + static ChineseConverter() { + Assembly assembly; + + assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream("NLyric.TraditionalToSimplified.map")) + using (BinaryReader reader = new BinaryReader(stream)) { + int count; + + count = (int)stream.Length / 4; + _traditionalToSimplifiedMap = new Dictionary(count); + for (int i = 0; i < count; i++) + _traditionalToSimplifiedMap.Add((char)reader.ReadUInt16(), (char)reader.ReadUInt16()); + } + } + + public static string TraditionalToSimplified(string s) { + if (s is null) + return null; + + StringBuilder sb; + + sb = new StringBuilder(s); + for (int i = 0; i < sb.Length; i++) + if (_traditionalToSimplifiedMap.TryGetValue(sb[i], out char c)) + sb[i] = c; + return sb.ToString(); + } + } +} diff --git a/NLyric/Crc32.cs b/NLyric/Crc32.cs index 9e44344..c829157 100644 --- a/NLyric/Crc32.cs +++ b/NLyric/Crc32.cs @@ -24,7 +24,7 @@ static Crc32() { } public static uint Compute(byte[] data) { - if (data == null) + if (data is null) throw new ArgumentNullException(nameof(data)); uint crc32; diff --git a/NLyric/DictionaryComparer.cs b/NLyric/DictionaryComparer.cs index 154aeb2..f603259 100644 --- a/NLyric/DictionaryComparer.cs +++ b/NLyric/DictionaryComparer.cs @@ -6,7 +6,7 @@ internal sealed class DictionaryComparer : IComparer where T private readonly Dictionary _dictionary; public DictionaryComparer(Dictionary dictionary) { - if (dictionary == null) + if (dictionary is null) throw new ArgumentNullException(nameof(dictionary)); _dictionary = dictionary; diff --git a/NLyric/Logger.cs b/NLyric/Logger.cs index b80f5f4..6dd877a 100644 --- a/NLyric/Logger.cs +++ b/NLyric/Logger.cs @@ -12,13 +12,11 @@ private Logger() { } public void LogNewLine() { - lock (_syncRoot) - Console.WriteLine(); + LogInfo(string.Empty, ConsoleColor.Gray); } public void LogInfo(string value) { - lock (_syncRoot) - Console.WriteLine(value); + LogInfo(value, ConsoleColor.Gray); } public void LogWarning(string value) { @@ -41,14 +39,14 @@ public void LogInfo(string value, ConsoleColor color) { } public void LogException(Exception value) { - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); LogError(ExceptionToString(value)); } private static string ExceptionToString(Exception exception) { - if (exception == null) + if (exception is null) throw new ArgumentNullException(nameof(exception)); StringBuilder sb; @@ -65,7 +63,7 @@ private static void DumpException(Exception exception, StringBuilder sb) { sb.AppendLine("StackTrace: " + Environment.NewLine + exception.StackTrace); sb.AppendLine("TargetSite: " + Environment.NewLine + exception.TargetSite.ToString()); sb.AppendLine("----------------------------------------"); - if (exception.InnerException != null) + if (!(exception.InnerException is null)) DumpException(exception.InnerException, sb); } } diff --git a/NLyric/Lyrics/Lrc.cs b/NLyric/Lyrics/Lrc.cs index fa1acc7..70d16bb 100644 --- a/NLyric/Lyrics/Lrc.cs +++ b/NLyric/Lyrics/Lrc.cs @@ -16,12 +16,12 @@ public sealed class Lrc { private string _album; private string _by; private TimeSpan? _offset; - private readonly Dictionary _lyrics = new Dictionary(); + private Dictionary _lyrics = new Dictionary(); public string Title { get => _title; set { - if (value == null) { + if (value is null) { _title = value; return; } @@ -33,7 +33,7 @@ public string Title { public string Artist { get => _artist; set { - if (value == null) { + if (value is null) { _artist = value; return; } @@ -45,7 +45,7 @@ public string Artist { public string Album { get => _album; set { - if (value == null) { + if (value is null) { _album = value; return; } @@ -57,7 +57,7 @@ public string Album { public string By { get => _by; set { - if (value == null) { + if (value is null) { _by = value; return; } @@ -68,10 +68,18 @@ public string By { public TimeSpan? Offset { get => _offset; - set => _offset = (value == null || value.Value.Ticks == 0) ? null : value; + set => _offset = (value is null || value.Value.Ticks == 0) ? null : value; } - public Dictionary Lyrics => _lyrics; + public Dictionary Lyrics { + get => _lyrics; + set { + if (value is null) + throw new ArgumentNullException(nameof(value)); + + _lyrics = value; + } + } public static Lrc Parse(string text) { if (string.IsNullOrEmpty(text)) @@ -83,7 +91,7 @@ public static Lrc Parse(string text) { using (StringReader reader = new StringReader(text)) { string line; - while ((line = reader.ReadLine()) != null) + while (!((line = reader.ReadLine()) is null)) if (!TryParseLine(line.Trim(), lrc)) throw new FormatException(); } @@ -100,7 +108,7 @@ public static Lrc UnsafeParse(string text) { using (StringReader reader = new StringReader(text)) { string line; - while ((line = reader.ReadLine()) != null) + while (!((line = reader.ReadLine()) is null)) TryParseLine(line.Trim(), lrc); } return lrc; @@ -164,15 +172,15 @@ public override string ToString() { StringBuilder sb; sb = new StringBuilder(); - if (_title != null) + if (!(_title is null)) AppendLine(sb, TI, _title); - if (_artist != null) + if (!(_artist is null)) AppendLine(sb, AR, _artist); - if (_album != null) + if (!(_album is null)) AppendLine(sb, AL, _album); - if (_by != null) + if (!(_by is null)) AppendLine(sb, BY, _by); - if (_offset != null) + if (!(_offset is null)) AppendLine(sb, OFFSET, ((long)_offset.Value.TotalMilliseconds).ToString()); foreach (KeyValuePair lyric in _lyrics) sb.AppendLine($"[{TimeSpanToLyricString(lyric.Key)}]{lyric.Value}"); diff --git a/NLyric/NLyric.csproj b/NLyric/NLyric.csproj index a2e090c..77bd82b 100644 --- a/NLyric/NLyric.csproj +++ b/NLyric/NLyric.csproj @@ -1,16 +1,22 @@ - + NLyric NLyric NLyric Copyright © 2019 Wwh - 2.0.2.0 - 2.0.2.0 + 2.1.0.0 + 2.1.0.0 Exe netcoreapp2.1;net472 NLyric 7.3 + + + + + + diff --git a/NLyric/CliWorker.cs b/NLyric/NLyricImpl.cs similarity index 86% rename from NLyric/CliWorker.cs rename to NLyric/NLyricImpl.cs index b1cc286..8e02080 100644 --- a/NLyric/CliWorker.cs +++ b/NLyric/NLyricImpl.cs @@ -12,7 +12,7 @@ using NLyric.Settings; namespace NLyric { - internal static partial class CliWorker { + internal static class NLyricImpl { private static readonly SearchSettings _searchSettings = AllSettings.Default.Search; private static readonly FuzzySettings _fuzzySettings = AllSettings.Default.Fuzzy; private static readonly MatchSettings _matchSettings = AllSettings.Default.Match; @@ -23,12 +23,14 @@ internal static partial class CliWorker { // AlbumId -> Tracks private static readonly Dictionary _cachedNcmLyrics = new Dictionary(); // TrackId -> Lyric + private static string _allCachesPath; private static AllCaches _allCaches; public static async Task ExecuteAsync(Arguments arguments) { Logger.Instance.LogInfo("程序会自动过滤相似度为0的结果与歌词未被收集的结果!!!", ConsoleColor.Green); Logger.Instance.LogNewLine(); - LoadLocalCaches(arguments.Directory); + _allCachesPath = Path.Combine(arguments.Directory, ".nlyric"); + LoadLocalCaches(); foreach (string audioPath in Directory.EnumerateFiles(arguments.Directory, "*", SearchOption.AllDirectories)) { string lrcPath; int? trackId; @@ -39,14 +41,14 @@ public static async Task ExecuteAsync(Arguments arguments) { Logger.Instance.LogInfo($"开始搜索文件\"{Path.GetFileName(audioPath)}\"的歌词。"); trackId = await TryGetMusicId(audioPath); // 同时尝试通过163Key和专辑获取歌曲信息 - if (trackId == null) + if (trackId is null) Logger.Instance.LogWarning($"无法找到文件\"{Path.GetFileName(audioPath)}\"的网易云音乐ID!"); else await WriteLrcAsync(trackId.Value, lrcPath); Logger.Instance.LogNewLine(); Logger.Instance.LogNewLine(); } - SaveLocalCaches(arguments.Directory); + SaveLocalCaches(); } private static bool CanSkip(string audioPath, string lrcPath) { @@ -80,7 +82,7 @@ private static bool IsAudioFile(string extension) { try { // 歌曲无163Key,通过自己的算法匹配 ncmTrack = await MapToAsync(track, album, audioPath); - if (ncmTrack != null) { + if (!(ncmTrack is null)) { trackId = ncmTrack.Id; Logger.Instance.LogInfo($"已获取文件\"{Path.GetFileName(audioPath)}\"的网易云音乐ID: {trackId}。"); } @@ -110,16 +112,18 @@ private static async Task WriteLrcAsync(int trackId, string lrcPath) { } if (hasLrcFile) { // 如果歌词存在,判断是否需要覆盖或更新 - if (lyricCache != null && lyricCache.CheckSum == lyricCheckSum) { + if (!(lyricCache is null) && lyricCache.CheckSum == lyricCheckSum) { // 歌词由NLyric创建 if (ncmLyric.RawVersion <= lyricCache.RawVersion && ncmLyric.TranslatedVersion <= lyricCache.TranslatedVersion) { // 是最新版本 - Logger.Instance.LogInfo("本地歌词已是最新版本,正在跳过。", ConsoleColor.Green); + Logger.Instance.LogInfo("本地歌词已是最新版本,正在跳过。", ConsoleColor.Yellow); return; } else { // 不是最新版本 - if (!_lyricSettings.AutoUpdate) { + if (_lyricSettings.AutoUpdate) + Logger.Instance.LogInfo("本地歌词不是最新版本,正在更新。", ConsoleColor.Green); + else { Logger.Instance.LogInfo("本地歌词不是最新版本但是自动更新被禁止,正在跳过。", ConsoleColor.Yellow); return; } @@ -128,13 +132,13 @@ private static async Task WriteLrcAsync(int trackId, string lrcPath) { else { // 歌词非NLyric创建 if (!_lyricSettings.Overwriting) { - Logger.Instance.LogInfo("本地歌词已存在并且非NLyric创建,正在跳过。", ConsoleColor.Cyan); + Logger.Instance.LogInfo("本地歌词已存在并且非NLyric创建,正在跳过。", ConsoleColor.Yellow); return; } } } lrc = ToLrc(ncmLyric); - if (lrc != null) { + if (!(lrc is null)) { string lyric; lyric = lrc.ToString(); @@ -153,9 +157,9 @@ private static async Task WriteLrcAsync(int trackId, string lrcPath) { /// /// private static async Task MapToAsync(Track track, Album album, string audioPath) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); - if (audioPath == null) + if (audioPath is null) throw new ArgumentNullException(nameof(audioPath)); string fileName; @@ -164,9 +168,9 @@ private static async Task MapToAsync(Track track, Album album, string NcmTrack ncmTrack; fileName = Path.GetFileName(audioPath); - trackCache = album == null ? _allCaches.TrackCaches.Match(track, fileName) : _allCaches.TrackCaches.Match(track, album); + trackCache = album is null ? _allCaches.TrackCaches.Match(track, fileName) : _allCaches.TrackCaches.Match(track, album); // 有专辑信息就用专辑信息,没有专辑信息就用文件名 - if (trackCache != null) + if (!(trackCache is null)) return new NcmTrack(track, trackCache.Id); // 先尝试从缓存获取歌曲 if (The163KeyHelper.TryGetMusicId(audioPath, out trackId)) { @@ -177,21 +181,21 @@ private static async Task MapToAsync(Track track, Album album, string NcmAlbum ncmAlbum; ncmAlbum = null; - if (album != null) { + if (!(album is null)) { // 存在专辑信息,尝试获取网易云音乐上对应的专辑 AlbumCache albumCache; albumCache = _allCaches.AlbumCaches.Match(album); - if (albumCache != null) + if (!(albumCache is null)) ncmAlbum = new NcmAlbum(album, albumCache.Id); // 先尝试从缓存获取专辑 - if (ncmAlbum == null) { + if (ncmAlbum is null) { ncmAlbum = await MapToAsync(album); - if (ncmAlbum != null) + if (!(ncmAlbum is null)) UpdateCache(album, ncmAlbum.Id); } } - if (ncmAlbum == null) { + if (ncmAlbum is null) { // 没有对应的专辑信息,使用无专辑匹配 ncmTrack = await MapToAsync(track); } @@ -202,16 +206,16 @@ private static async Task MapToAsync(Track track, Album album, string ncmTracks = (await GetTracksAsync(ncmAlbum)).Where(t => ComputeSimilarity(t.Name, track.Name, false) != 0).ToArray(); // 获取网易云音乐上专辑收录的歌曲 ncmTrack = MatchByUser(ncmTracks, track); - if (ncmTrack == null) + if (ncmTrack is null) // 网易云音乐上的专辑可能没收录这个歌曲,不清楚为什么,但是确实存在这个情况,比如专辑id:3094396 ncmTrack = await MapToAsync(track); } } - if (ncmTrack == null) + if (ncmTrack is null) Logger.Instance.LogWarning("歌曲匹配失败!"); else { Logger.Instance.LogInfo("歌曲匹配成功!"); - if (album == null) + if (album is null) UpdateCache(track, fileName, ncmTrack.Id); else UpdateCache(track, album, ncmTrack.Id); @@ -241,7 +245,7 @@ private static async Task GetTracksAsync(NcmAlbum ncmAlbum) { /// /// private static async Task MapToAsync(Track track) { - if (track == null) + if (track is null) throw new ArgumentNullException(nameof(track)); NcmTrack ncmTrack; @@ -249,7 +253,7 @@ private static async Task MapToAsync(Track track) { Logger.Instance.LogInfo($"开始搜索歌曲\"{track}\"。"); Logger.Instance.LogWarning("正在尝试带艺术家搜索,结果可能将过少!"); ncmTrack = await MapToAsync(track, true); - if (ncmTrack == null && _fuzzySettings.TryIgnoringArtists) { + if (ncmTrack is null && _fuzzySettings.TryIgnoringArtists) { Logger.Instance.LogWarning("正在尝试忽略艺术家搜索,结果可能将不精确!"); ncmTrack = await MapToAsync(track, false); } @@ -262,7 +266,7 @@ private static async Task MapToAsync(Track track) { /// /// private static async Task MapToAsync(Album album) { - if (album == null) + if (album is null) throw new ArgumentNullException(nameof(album)); string replacedAlbumName; @@ -274,11 +278,11 @@ private static async Task MapToAsync(Album album) { Logger.Instance.LogInfo($"开始搜索专辑\"{album}\"。"); Logger.Instance.LogWarning("正在尝试带艺术家搜索,结果可能将过少!"); ncmAlbum = await MapToAsync(album, true); - if (ncmAlbum == null && _fuzzySettings.TryIgnoringArtists) { + if (ncmAlbum is null && _fuzzySettings.TryIgnoringArtists) { Logger.Instance.LogWarning("正在尝试忽略艺术家搜索,结果可能将不精确!"); ncmAlbum = await MapToAsync(album, false); } - if (ncmAlbum == null) { + if (ncmAlbum is null) { Logger.Instance.LogWarning("专辑匹配失败!"); _cachedNcmAlbums[replacedAlbumName] = null; return null; @@ -321,13 +325,10 @@ private static async Task MapToAsync(Album album, bool withArtists) { #endregion #region local cache - private static void LoadLocalCaches(string directoryPath) { - string cachePath; - - cachePath = Path.Combine(directoryPath, ".nlyric"); - if (File.Exists(cachePath)) { - _allCaches = JsonConvert.DeserializeObject(File.ReadAllText(cachePath)); - Logger.Instance.LogInfo($"搜索缓存\"{cachePath}\"已被加载。"); + private static void LoadLocalCaches() { + if (File.Exists(_allCachesPath)) { + _allCaches = JsonConvert.DeserializeObject(File.ReadAllText(_allCachesPath)); + Logger.Instance.LogInfo($"搜索缓存\"{_allCachesPath}\"加载成功。"); } else _allCaches = new AllCaches() { @@ -337,15 +338,16 @@ private static void LoadLocalCaches(string directoryPath) { }; } - private static void SaveLocalCaches(string directoryPath) { - string cachePath; - - cachePath = Path.Combine(directoryPath, ".nlyric"); + private static void SaveLocalCaches() { _allCaches.AlbumCaches.Sort((x, y) => x.Name.CompareTo(y.Name)); _allCaches.TrackCaches.Sort((x, y) => x.Name.CompareTo(y.Name)); _allCaches.LyricCaches.Sort((x, y) => x.Id.CompareTo(y.Id)); + SaveLocalCachesCore(_allCachesPath); + Logger.Instance.LogInfo($"搜索缓存\"{_allCachesPath}\"已被保存。"); + } + + private static void SaveLocalCachesCore(string cachePath) { File.WriteAllText(cachePath, FormatJson(JsonConvert.SerializeObject(_allCaches))); - Logger.Instance.LogInfo($"搜索缓存\"{cachePath}\"已被保存。"); } private static string FormatJson(string json) { @@ -362,24 +364,30 @@ private static void UpdateCache(Album album, int id) { AlbumCache cache; cache = _allCaches.AlbumCaches.Match(album); - if (cache == null) - _allCaches.AlbumCaches.Add(new AlbumCache(album, id)); + if (!(cache is null)) + return; + _allCaches.AlbumCaches.Add(new AlbumCache(album, id)); + OnCacheUpdated(); } private static void UpdateCache(Track track, Album album, int id) { TrackCache cache; cache = _allCaches.TrackCaches.Match(track, album); - if (cache == null) - _allCaches.TrackCaches.Add(new TrackCache(track, album, id)); + if (!(cache is null)) + return; + _allCaches.TrackCaches.Add(new TrackCache(track, album, id)); + OnCacheUpdated(); } private static void UpdateCache(Track track, string fileName, int id) { TrackCache cache; cache = _allCaches.TrackCaches.Match(track, fileName); - if (cache == null) - _allCaches.TrackCaches.Add(new TrackCache(track, fileName, id)); + if (!(cache is null)) + return; + _allCaches.TrackCaches.Add(new TrackCache(track, fileName, id)); + OnCacheUpdated(); } private static void UpdateCache(NcmLyric lyric, string checkSum) { @@ -389,6 +397,11 @@ private static void UpdateCache(NcmLyric lyric, string checkSum) { if (index != -1) _allCaches.LyricCaches.RemoveAt(index); _allCaches.LyricCaches.Add(new LyricCache(lyric, checkSum)); + OnCacheUpdated(); + } + + private static void OnCacheUpdated() { + SaveLocalCachesCore(_allCachesPath); } #endregion @@ -399,7 +412,7 @@ private static TSource MatchByUser(TSource[] sources, TTarget if (sources.Length == 0) return null; result = MatchByUser(sources, target, false); - if (result == null && _fuzzySettings.TryIgnoringExtraInfo) + if (result is null && _fuzzySettings.TryIgnoringExtraInfo) result = MatchByUser(sources, target, true); return result; } @@ -415,8 +428,8 @@ private static TSource MatchByUser(TSource[] sources, TTarget foreach (TSource source in sources) nameSimilarities[source] = ComputeSimilarity(source.Name, target.Name, fuzzy); result = Match(sources, target, nameSimilarities, out isExact); - if (result != null && (isExact || Confirm("不完全相似,是否使用自动匹配结果?"))) - // 自动匹配成功,如果是完全匹配,不需要用户再次确认,反正由用户再次确认 + if (isExact) + // 自动匹配成功,如果是完全匹配,不需要用户再次确认 return result; return fuzzy ? Select(sources.Where(t => nameSimilarities[t] > _matchSettings.MinimumSimilarityUser).OrderByDescending(t => t, new DictionaryComparer(nameSimilarities)).ToArray(), target, nameSimilarities) : null; // fuzzy为true时是第二次搜索了,再让用户再次手动从搜索结果中选择,自动匹配失败的原因可能是 Settings.Match.MinimumSimilarity 设置太大了 @@ -482,7 +495,7 @@ private static TSource Select(TSource[] sources, TTarget targe } Logger.Instance.LogWarning("输入有误,请重新输入!"); } while (true); - if (result != null) + if (!(result is null)) Logger.Instance.LogInfo("已选择:" + result.ToString()); return result; @@ -493,23 +506,6 @@ string TrackOrAlbumToString(ITrackOrAlbum trackOrAlbum) { } } - private static bool Confirm(string text) { - Logger.Instance.LogInfo(text); - Logger.Instance.LogInfo("请手动输入Yes或No。"); - do { - string userInput; - - userInput = Console.ReadLine().Trim().ToUpperInvariant(); - switch (userInput) { - case "YES": - return true; - case "NO": - return false; - } - Logger.Instance.LogWarning("输入有误,请重新输入!"); - } while (true); - } - private static double ComputeSimilarity(string x, string y, bool fuzzy) { x = x.ReplaceEx(); y = y.ReplaceEx(); @@ -543,20 +539,24 @@ private static Lrc ToLrc(NcmLyric lyric) { Logger.Instance.LogWarning("当前歌曲是纯音乐无歌词!"); return null; } + if (!(lyric.Raw is null)) + NormalizeLyric(lyric.Raw, false); + if (!(lyric.Translated is null)) + NormalizeLyric(lyric.Translated, _lyricSettings.SimplifyTranslated); foreach (string mode in _lyricSettings.Modes) { switch (mode.ToUpperInvariant()) { case "MERGED": - if (lyric.Raw == null || lyric.Translated == null) + if (lyric.Raw is null || lyric.Translated is null) continue; Logger.Instance.LogInfo("已获取混合歌词。"); return MergeLyric(lyric.Raw, lyric.Translated); case "RAW": - if (lyric.Raw == null) + if (lyric.Raw is null) continue; Logger.Instance.LogInfo("已获取原始歌词。"); return lyric.Raw; case "TRANSLATED": - if (lyric.Translated == null) + if (lyric.Translated is null) continue; Logger.Instance.LogInfo("已获取翻译歌词。"); return lyric.Translated; @@ -568,12 +568,22 @@ private static Lrc ToLrc(NcmLyric lyric) { return null; } - private static Lrc MergeLyric(Lrc rawLrc, Lrc translatedLrc) { - if (rawLrc == null) - throw new ArgumentNullException(nameof(rawLrc)); - if (translatedLrc == null) - throw new ArgumentNullException(nameof(translatedLrc)); + private static void NormalizeLyric(Lrc lrc, bool simplify) { + Dictionary newLyrics; + newLyrics = new Dictionary(lrc.Lyrics.Count); + foreach (KeyValuePair lyric in lrc.Lyrics) { + string value; + + value = lyric.Value.Trim('/', ' '); + if (simplify) + value = ChineseConverter.TraditionalToSimplified(value); + newLyrics.Add(lyric.Key, value); + } + lrc.Lyrics = newLyrics; + } + + private static Lrc MergeLyric(Lrc rawLrc, Lrc translatedLrc) { Lrc mergedLrc; mergedLrc = new Lrc { diff --git a/NLyric/Ncm/NcmApi.cs b/NLyric/Ncm/NcmApi.cs index 1cfff0b..a38c092 100644 --- a/NLyric/Ncm/NcmApi.cs +++ b/NLyric/Ncm/NcmApi.cs @@ -24,17 +24,19 @@ public enum SearchType { } public static async Task SearchAsync(IEnumerable keywords, SearchType type, int limit) { - FormUrlEncodedCollection parameters; + QueryCollection queries; - parameters = new FormUrlEncodedCollection { + queries = new QueryCollection { { "s", string.Join(" ", keywords) }, { "type", ((int)type).ToString() }, { "limit", limit.ToString() } }; using (HttpClient client = new HttpClient()) - using (HttpResponseMessage response = await client.SendAsync(HttpMethod.Get, SEARCH_URL, parameters, null)) { + using (HttpResponseMessage response = await client.SendAsync(HttpMethod.Get, SEARCH_URL, queries, null)) { JObject json; + if (!response.IsSuccessStatusCode) + throw new HttpRequestException(); json = JObject.Parse(await response.Content.ReadAsStringAsync()); if ((int)json["code"] != 200) throw new HttpRequestException(); @@ -43,15 +45,17 @@ public static async Task SearchAsync(IEnumerable keywords, Searc } public static async Task GetAlbumAsync(int id) { - FormUrlEncodedCollection parameters; + QueryCollection queries; - parameters = new FormUrlEncodedCollection { + queries = new QueryCollection { { "id", id.ToString() } }; using (HttpClient client = new HttpClient()) - using (HttpResponseMessage response = await client.SendAsync(HttpMethod.Get, ALBUM_URL, parameters, null)) { + using (HttpResponseMessage response = await client.SendAsync(HttpMethod.Get, ALBUM_URL, queries, null)) { JObject json; + if (!response.IsSuccessStatusCode) + throw new HttpRequestException(); json = JObject.Parse(await response.Content.ReadAsStringAsync()); if ((int)json["code"] != 200) throw new HttpRequestException(); @@ -60,17 +64,19 @@ public static async Task GetAlbumAsync(int id) { } public static async Task GetLyricAsync(int id) { - FormUrlEncodedCollection parameters; + QueryCollection queries; - parameters = new FormUrlEncodedCollection { + queries = new QueryCollection { { "id", id.ToString() }, { "lv", "-1" }, { "tv", "-1" } }; using (HttpClient client = new HttpClient()) - using (HttpResponseMessage response = await client.SendAsync(HttpMethod.Get, LYRIC_URL, parameters, null)) { + using (HttpResponseMessage response = await client.SendAsync(HttpMethod.Get, LYRIC_URL, queries, null)) { JObject json; + if (!response.IsSuccessStatusCode) + throw new HttpRequestException(); json = JObject.Parse(await response.Content.ReadAsStringAsync()); if ((int)json["code"] != 200) throw new HttpRequestException(); diff --git a/NLyric/Program.cs b/NLyric/Program.cs index 03907d3..fb53eb8 100644 --- a/NLyric/Program.cs +++ b/NLyric/Program.cs @@ -8,7 +8,7 @@ namespace NLyric { public static class Program { private static void Main(string[] args) { - if (args == null || args.Length == 0) { + if (args is null || args.Length == 0) { CommandLine.ShowUsage(); return; } @@ -25,7 +25,7 @@ private static void Main(string[] args) { return; } AllSettings.Default = JsonConvert.DeserializeObject(File.ReadAllText("Settings.json")); - CliWorker.ExecuteAsync(arguments).GetAwaiter().GetResult(); + NLyricImpl.ExecuteAsync(arguments).GetAwaiter().GetResult(); Logger.Instance.LogInfo("完成", ConsoleColor.Green); #if DEBUG Console.ReadKey(true); diff --git a/NLyric/FormUrlEncodedCollection.cs b/NLyric/QueryCollection.cs similarity index 65% rename from NLyric/FormUrlEncodedCollection.cs rename to NLyric/QueryCollection.cs index e0e78cc..749aee2 100644 --- a/NLyric/FormUrlEncodedCollection.cs +++ b/NLyric/QueryCollection.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; namespace NLyric { - internal sealed class FormUrlEncodedCollection : List> { + internal sealed class QueryCollection : List> { public void Add(string key, string value) { Add(new KeyValuePair(key, value)); } diff --git a/NLyric/Settings.json b/NLyric/Settings.json index 1b465ee..4ab2274 100644 --- a/NLyric/Settings.json +++ b/NLyric/Settings.json @@ -68,6 +68,7 @@ "Raw", "Translated" ], // 歌词模式,依次尝试每一个模式直到成功,Merged表示混合未翻译和翻译后歌词,Raw表示未翻译的歌词,Translated表示翻译后的歌词 + "SimplifyTranslated": true, // 部分翻译后的歌词是繁体的,这个选项可以简体化翻译后的歌词 "AutoUpdate": true, // 是否自动更新由NLyric创建的歌词 "Overwriting": false // 是否覆盖非NLyric创建的歌词 } diff --git a/NLyric/Settings/AllSettings.cs b/NLyric/Settings/AllSettings.cs index 61879fe..c061aa3 100644 --- a/NLyric/Settings/AllSettings.cs +++ b/NLyric/Settings/AllSettings.cs @@ -6,15 +6,17 @@ internal sealed class AllSettings { public static AllSettings Default { get { - if (_default == null) + if (_default is null) throw new InvalidOperationException(); + return _default; } set { - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); - if (_default != null) + if (!(_default is null)) throw new InvalidOperationException(); + _default = value; } } diff --git a/NLyric/Settings/LyricSettings.cs b/NLyric/Settings/LyricSettings.cs index a763a1e..64ff713 100644 --- a/NLyric/Settings/LyricSettings.cs +++ b/NLyric/Settings/LyricSettings.cs @@ -2,6 +2,8 @@ namespace NLyric.Settings { internal sealed class LyricSettings { public string[] Modes { get; set; } + public bool SimplifyTranslated { get; set; } + public bool AutoUpdate { get; set; } public bool Overwriting { get; set; } diff --git a/NLyric/StringHelper.cs b/NLyric/StringHelper.cs index 59df501..4bb0301 100644 --- a/NLyric/StringHelper.cs +++ b/NLyric/StringHelper.cs @@ -16,7 +16,7 @@ internal static class StringHelper { /// /// public static string GetSafeString(this string value) { - return value == null ? string.Empty : value.Trim(); + return value is null ? string.Empty : value.Trim(); } /// @@ -25,7 +25,7 @@ public static string GetSafeString(this string value) { /// /// public static string ReplaceEx(this string value) { - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); return value.ToHalfWidth().WholeWordReplace().CharReplace(); @@ -37,7 +37,7 @@ public static string ReplaceEx(this string value) { /// /// public static string WholeWordReplace(this string value) { - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); if (value.Length == 0) @@ -54,7 +54,7 @@ public static string WholeWordReplace(this string value) { /// /// public static string CharReplace(this string value) { - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); StringBuilder buffer; @@ -75,7 +75,7 @@ public static string CharReplace(this string value) { /// /// public static string Fuzzy(this string value) { - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); int fuzzyStartIndex; @@ -97,7 +97,7 @@ public static string Fuzzy(this string value) { /// /// public static string[] SplitEx(this string value) { - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); return value.Split(_searchSettings.Separators, StringSplitOptions.RemoveEmptyEntries); @@ -109,7 +109,7 @@ public static string[] SplitEx(this string value) { /// /// public static string ToHalfWidth(this string value) { - if (value == null) + if (value is null) throw new ArgumentNullException(nameof(value)); char[] chars; diff --git a/NLyric/System/Cli/ArgumentAttribute.cs b/NLyric/System/Cli/ArgumentAttribute.cs index 3c1dc46..4cf61c4 100644 --- a/NLyric/System/Cli/ArgumentAttribute.cs +++ b/NLyric/System/Cli/ArgumentAttribute.cs @@ -10,23 +10,38 @@ internal sealed class ArgumentAttribute : Attribute { private string _type; private string _description; + /// + /// 参数名 + /// public string Name => _name; + /// + /// 是否为必选参数 + /// public bool IsRequired { get => _isRequired; set => _isRequired = value; } + /// + /// 默认值,当 时, 必须为 。 + /// public object DefaultValue { get => _defaultValue; set => _defaultValue = value; } + /// + /// 参数类型,用于 显示类型来简单描述参数。若应用到返回类型为 的属性上, 必须为 。 + /// public string Type { get => _type; set => _type = value; } + /// + /// 参数介绍,用于 具体描述参数。 + /// public string Description { get => _description; set => _description = value; diff --git a/NLyric/System/Cli/CommandLine.cs b/NLyric/System/Cli/CommandLine.cs index f483a59..1fa70d1 100644 --- a/NLyric/System/Cli/CommandLine.cs +++ b/NLyric/System/Cli/CommandLine.cs @@ -6,7 +6,7 @@ namespace System.Cli { internal static class CommandLine { public static T Parse(string[] args) where T : new() { - if (args == null) + if (args is null) throw new ArgumentNullException(nameof(args)); T result; @@ -17,7 +17,7 @@ internal static class CommandLine { } public static bool TryParse(string[] args, out T result) where T : new() { - if (args == null) { + if (args is null) { result = default; return false; } @@ -102,7 +102,7 @@ public static bool ShowUsage() { if (!VerifyProperty(propertyInfo, out attribute)) return false; - if (attribute == null) + if (attribute is null) continue; argumentInfos.Add(new ArgumentInfo(attribute, propertyInfo)); } @@ -135,7 +135,7 @@ private static bool TryGetArgumentInfos(Type type, out Dictionary SendAsync(this HttpClient client, HttpMe return client.SendAsync(method, url, null, null); } - public static Task SendAsync(this HttpClient client, HttpMethod method, string url, IEnumerable> parameters, IEnumerable> headers) { - return client.SendAsync(method, url, parameters, headers, (byte[])null, "application/x-www-form-urlencoded"); + public static Task SendAsync(this HttpClient client, HttpMethod method, string url, IEnumerable> queries, IEnumerable> headers) { + return client.SendAsync(method, url, queries, headers, (byte[])null, "application/x-www-form-urlencoded"); } - public static Task SendAsync(this HttpClient client, HttpMethod method, string url, IEnumerable> parameters, IEnumerable> headers, string content, string contentType) { - return client.SendAsync(method, url, parameters, headers, content == null ? null : Encoding.UTF8.GetBytes(content), contentType); + public static Task SendAsync(this HttpClient client, HttpMethod method, string url, IEnumerable> queries, IEnumerable> headers, string content, string contentType) { + return client.SendAsync(method, url, queries, headers, content is null ? null : Encoding.UTF8.GetBytes(content), contentType); } - public static Task SendAsync(this HttpClient client, HttpMethod method, string url, IEnumerable> parameters, IEnumerable> headers, byte[] content, string contentType) { - if (client == null) + public static Task SendAsync(this HttpClient client, HttpMethod method, string url, IEnumerable> queries, IEnumerable> headers, byte[] content, string contentType) { + if (client is null) throw new ArgumentNullException(nameof(client)); - if (method == null) + if (method is null) throw new ArgumentNullException(nameof(method)); if (string.IsNullOrEmpty(url)) throw new ArgumentNullException(nameof(url)); if (string.IsNullOrEmpty(contentType)) throw new ArgumentNullException(nameof(contentType)); - if (method != HttpMethod.Get && parameters != null && content != null) - throw new NotSupportedException(); UriBuilder uriBuilder; HttpRequestMessage request; uriBuilder = new UriBuilder(url); - if (parameters != null && method == HttpMethod.Get) { + if (!(queries is null)) { string query; - query = FormToString(parameters); + query = queries.ToQueryString(); if (!string.IsNullOrEmpty(query)) if (string.IsNullOrEmpty(uriBuilder.Query)) uriBuilder.Query = query; @@ -46,29 +43,16 @@ public static Task SendAsync(this HttpClient client, HttpMe uriBuilder.Query += "&" + query; } request = new HttpRequestMessage(method, uriBuilder.Uri); - if (content != null) + if (!(content is null)) request.Content = new ByteArrayContent(content); - else if (parameters != null && method != HttpMethod.Get) - request.Content = new FormUrlEncodedContent(parameters); - if (request.Content != null) + else if (!(queries is null) && method != HttpMethod.Get) + request.Content = new FormUrlEncodedContent(queries); + if (!(request.Content is null)) request.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType); - if (headers != null) - UpdateHeaders(request.Headers, headers); + if (!(headers is null)) + foreach (KeyValuePair header in headers) + request.Headers.TryAddWithoutValidation(header.Key, header.Value); return client.SendAsync(request); } - - private static string FormToString(IEnumerable> values) { - return string.Join("&", values.Select(t => t.Key + "=" + Uri.EscapeDataString(t.Value))); - } - - private static void UpdateHeaders(HttpRequestHeaders requestHeaders, IEnumerable> headers) { - if (requestHeaders == null) - throw new ArgumentNullException(nameof(requestHeaders)); - if (headers == null) - throw new ArgumentNullException(nameof(headers)); - - foreach (KeyValuePair item in headers) - requestHeaders.TryAddWithoutValidation(item.Key, item.Value); - } } } diff --git a/NLyric/System/Extensions/HttpExtensions.cs b/NLyric/System/Extensions/HttpExtensions.cs new file mode 100644 index 0000000..02640da --- /dev/null +++ b/NLyric/System/Extensions/HttpExtensions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Linq; + +namespace System.Extensions { + internal static class HttpExtensions { + public static string ToQueryString(this IEnumerable> queries) { + if (queries is null) + throw new ArgumentNullException(nameof(queries)); + + return string.Join("&", queries.Select(t => Uri.EscapeDataString(t.Key) + "=" + Uri.EscapeDataString(t.Value))); + } + } +} diff --git a/NLyric/The163KeyHelper.cs b/NLyric/The163KeyHelper.cs index 699046d..6fd4f93 100644 --- a/NLyric/The163KeyHelper.cs +++ b/NLyric/The163KeyHelper.cs @@ -38,7 +38,7 @@ public static bool TryGetMusicId(string filePath, out int trackId) { byt163Key = null; break; } - if (byt163Key == null) { + if (byt163Key is null) { trackId = 0; return false; } diff --git a/NLyric/TraditionalToSimplified.map b/NLyric/TraditionalToSimplified.map new file mode 100644 index 0000000..54c8efe Binary files /dev/null and b/NLyric/TraditionalToSimplified.map differ