diff --git a/NLyric/Audio/Album.cs b/NLyric/Audio/Album.cs
index 3ceea1f..581af87 100644
--- a/NLyric/Audio/Album.cs
+++ b/NLyric/Audio/Album.cs
@@ -1,22 +1,27 @@
using System;
+using System.Collections.Generic;
using System.Linq;
+using TagLib;
namespace NLyric.Audio {
+ ///
+ /// 专辑
+ ///
public class Album : ITrackOrAlbum {
private readonly string _name;
private readonly string[] _artists;
- private readonly int? _trackCount;
- private readonly int? _year;
+ ///
+ /// 名称
+ ///
public string Name => _name;
- public string[] Artists => _artists;
-
- public int? TrackCount => _trackCount;
-
- public int? Year => _year;
+ ///
+ /// 艺术家
+ ///
+ public IReadOnlyList Artists => _artists;
- public Album(string name, string[] artists, int? trackCount, int? year) {
+ public Album(string name, IEnumerable artists) {
if (name is null)
throw new ArgumentNullException(nameof(name));
if (artists is null)
@@ -24,43 +29,44 @@ public Album(string name, string[] artists, int? trackCount, int? year) {
_name = name;
_artists = artists.Select(t => t.Trim()).ToArray();
- _trackCount = trackCount;
- _year = year;
+ Array.Sort(_artists, StringHelper.OrdinalComparer);
}
///
/// 构造器
///
- ///
+ ///
/// 当 为空时,是否从 获取艺术家
- public Album(ATL.Track track, bool getArtistsFromTrack) {
- if (track is null)
- throw new ArgumentNullException(nameof(track));
- if (!HasAlbumInfo(track))
- throw new ArgumentException(nameof(track) + " 中不存在专辑信息");
+ public Album(Tag tag, bool getArtistsFromTrack) {
+ if (tag is null)
+ throw new ArgumentNullException(nameof(tag));
+ if (!HasAlbumInfo(tag))
+ throw new ArgumentException(nameof(tag) + " 中不存在专辑信息");
- string artists;
+ string[] artists;
- _name = track.Album.GetSafeString();
- artists = track.AlbumArtist.GetSafeString();
+ _name = tag.Album.GetSafeString();
+ artists = tag.AlbumArtists.SelectMany(t => t.GetSafeString().SplitEx()).ToArray();
if (getArtistsFromTrack && artists.Length == 0)
- artists = track.Artist.GetSafeString();
- _artists = artists.Length == 0 ? Array.Empty() : artists.SplitEx();
- if (track.TrackTotal != 0)
- _trackCount = track.TrackTotal;
- if (track.Year != 0)
- _year = track.Year;
+ artists = tag.Performers.SelectMany(t => t.GetSafeString().SplitEx()).ToArray();
+ Array.Sort(artists, StringHelper.OrdinalComparer);
+ _artists = artists;
}
- public static bool HasAlbumInfo(ATL.Track track) {
- if (track is null)
- throw new ArgumentNullException(nameof(track));
+ ///
+ /// 是否存在专辑信息
+ ///
+ ///
+ ///
+ public static bool HasAlbumInfo(Tag tag) {
+ if (tag is null)
+ throw new ArgumentNullException(nameof(tag));
- return !string.IsNullOrWhiteSpace(track.Album);
+ return !string.IsNullOrWhiteSpace(tag.Album);
}
public override string ToString() {
- return "Name:" + _name + " | Artists:" + string.Join(",", _artists) + " | TrackCount:" + _trackCount.ToString() + " | Year:" + _year.ToString();
+ return "Name:" + _name + " | Artists:" + string.Join(",", _artists);
}
}
}
diff --git a/NLyric/Audio/ITrackOrAlbum.cs b/NLyric/Audio/ITrackOrAlbum.cs
index 75910c5..fa9a0ae 100644
--- a/NLyric/Audio/ITrackOrAlbum.cs
+++ b/NLyric/Audio/ITrackOrAlbum.cs
@@ -1,7 +1,9 @@
+using System.Collections.Generic;
+
namespace NLyric.Audio {
public interface ITrackOrAlbum {
string Name { get; }
- string[] Artists { get; }
+ IReadOnlyList Artists { get; }
}
}
diff --git a/NLyric/Audio/Track.cs b/NLyric/Audio/Track.cs
index 1966730..b994825 100644
--- a/NLyric/Audio/Track.cs
+++ b/NLyric/Audio/Track.cs
@@ -1,16 +1,27 @@
using System;
+using System.Collections.Generic;
using System.Linq;
+using TagLib;
namespace NLyric.Audio {
+ ///
+ /// 单曲
+ ///
public class Track : ITrackOrAlbum {
private readonly string _name;
private readonly string[] _artists;
+ ///
+ /// 名称
+ ///
public string Name => _name;
- public string[] Artists => _artists;
+ ///
+ /// 艺术家
+ ///
+ public IReadOnlyList Artists => _artists;
- public Track(string name, string[] artists) {
+ public Track(string name, IEnumerable artists) {
if (name is null)
throw new ArgumentNullException(nameof(name));
if (artists is null)
@@ -18,14 +29,15 @@ public Track(string name, string[] artists) {
_name = name;
_artists = artists.Select(t => t.Trim()).ToArray();
+ Array.Sort(_artists, StringHelper.OrdinalComparer);
}
- public Track(ATL.Track track) {
- if (track is null)
- throw new ArgumentNullException(nameof(track));
+ public Track(Tag tag) {
+ if (tag is null)
+ throw new ArgumentNullException(nameof(tag));
- _name = track.Title.GetSafeString();
- _artists = track.Artist.GetSafeString().SplitEx();
+ _name = tag.Title.GetSafeString();
+ _artists = tag.Performers.SelectMany(s => s.GetSafeString().SplitEx()).ToArray();
Array.Sort(_artists, StringHelper.OrdinalComparer);
}
diff --git a/NLyric/Caches/AlbumCache.cs b/NLyric/Caches/AlbumCache.cs
deleted file mode 100644
index 38055f4..0000000
--- a/NLyric/Caches/AlbumCache.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using Newtonsoft.Json;
-using NLyric.Audio;
-
-namespace NLyric.Caches {
- public sealed class AlbumCache {
- public string Name { get; set; }
-
- public int Id { get; set; }
-
- [JsonConstructor]
- [Obsolete("Deserialization only", true)]
- public AlbumCache() {
- }
-
- public AlbumCache(Album album, int id) : this(album.Name, id) {
- }
-
- public AlbumCache(string name, int id) {
- if (name is null)
- throw new ArgumentNullException(nameof(name));
-
- Name = name;
- Id = id;
- }
- }
-}
diff --git a/NLyric/Caches/AllCaches.cs b/NLyric/Caches/AllCaches.cs
deleted file mode 100644
index 6511cdc..0000000
--- a/NLyric/Caches/AllCaches.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Collections.Generic;
-
-namespace NLyric.Caches {
- public sealed class AllCaches {
- public List AlbumCaches { get; set; }
-
- public List LyricCaches { get; set; }
-
- public List TrackCaches { get; set; }
- }
-}
diff --git a/NLyric/Caches/Extensions.cs b/NLyric/Caches/Extensions.cs
deleted file mode 100644
index db78a3a..0000000
--- a/NLyric/Caches/Extensions.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using NLyric.Audio;
-
-namespace NLyric.Caches {
- public static class Extensions {
- public static AlbumCache Match(this IEnumerable caches, Album album) {
- 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 is null)
- throw new ArgumentNullException(nameof(track));
- 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 is null)
- throw new ArgumentNullException(nameof(track));
- if (fileName is null)
- throw new ArgumentNullException(nameof(fileName));
-
- return caches.FirstOrDefault(t => IsMatched(t, track, fileName));
- }
-
- public static LyricCache Match(this IEnumerable caches, int id) {
- return caches.FirstOrDefault(t => IsMatched(t, id));
- }
-
- public static bool IsMatched(this AlbumCache cache, Album album) {
- 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 is null)
- throw new ArgumentNullException(nameof(track));
- 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 is null)
- throw new ArgumentNullException(nameof(track));
- if (fileName is null)
- throw new ArgumentNullException(nameof(fileName));
-
- return cache.Name == track.Name && cache.FileName == fileName && cache.Artists.SequenceEqual(track.Artists);
- }
-
- public static bool IsMatched(this LyricCache cache, int id) {
- return cache.Id == id;
- }
- }
-}
diff --git a/NLyric/Caches/TrackCache.cs b/NLyric/Caches/TrackCache.cs
deleted file mode 100644
index ffbd561..0000000
--- a/NLyric/Caches/TrackCache.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System;
-using Newtonsoft.Json;
-using NLyric.Audio;
-
-namespace NLyric.Caches {
- public sealed class TrackCache {
- public string Name { get; set; }
-
- public string[] Artists { get; set; }
-
- public string AlbumName { get; set; }
-
- public string FileName { get; set; }
-
- public int Id { get; set; }
-
- [JsonConstructor]
- [Obsolete("Deserialization only", true)]
- public TrackCache() {
- }
-
- public TrackCache(Track track, Album album, int id) : this(track.Name, track.Artists, album.Name, null, id) {
- }
-
- public TrackCache(Track track, string fileName, int id) : this(track.Name, track.Artists, null, fileName, id) {
- }
-
- public TrackCache(string name, string[] artists, string albumName, string fileName, int id) {
- if (name is null)
- throw new ArgumentNullException(nameof(name));
- if (artists is null)
- throw new ArgumentNullException(nameof(artists));
- if (albumName is null && fileName is null)
- throw new ArgumentException($"{nameof(albumName)} 和 {nameof(fileName)} 不能同时为 null");
-
- Name = name;
- Artists = artists;
- AlbumName = albumName;
- FileName = fileName;
- Id = id;
- }
- }
-}
diff --git a/NLyric/Database/AlbumInfo.cs b/NLyric/Database/AlbumInfo.cs
new file mode 100644
index 0000000..bb7baba
--- /dev/null
+++ b/NLyric/Database/AlbumInfo.cs
@@ -0,0 +1,36 @@
+using System;
+using Newtonsoft.Json;
+using NLyric.Audio;
+
+namespace NLyric.Database {
+ ///
+ /// 专辑信息
+ ///
+ public sealed class AlbumInfo {
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 网易云音乐ID
+ ///
+ public int Id { get; set; }
+
+ [JsonConstructor]
+ [Obsolete("Deserialization only", true)]
+ public AlbumInfo() {
+ }
+
+ public AlbumInfo(Album album, int id) : this(album.Name, id) {
+ }
+
+ public AlbumInfo(string name, int id) {
+ if (name is null)
+ throw new ArgumentNullException(nameof(name));
+
+ Name = name;
+ Id = id;
+ }
+ }
+}
diff --git a/NLyric/Database/Extensions.cs b/NLyric/Database/Extensions.cs
new file mode 100644
index 0000000..111f820
--- /dev/null
+++ b/NLyric/Database/Extensions.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NLyric.Audio;
+
+namespace NLyric.Database {
+ public static class Extensions {
+ public static AlbumInfo Match(this IEnumerable caches, Album album) {
+ if (album is null)
+ throw new ArgumentNullException(nameof(album));
+
+ return caches.FirstOrDefault(t => IsMatched(t, album));
+ }
+
+ public static TrackInfo Match(this IEnumerable caches, Track track, Album album) {
+ if (track is null)
+ throw new ArgumentNullException(nameof(track));
+
+ return caches.FirstOrDefault(t => IsMatched(t, track, album));
+ }
+
+ public static bool IsMatched(this AlbumInfo cache, Album album) {
+ if (album is null)
+ throw new ArgumentNullException(nameof(album));
+
+ return cache.Name == album.Name;
+ }
+
+ public static bool IsMatched(this TrackInfo cache, Track track, Album album) {
+ if (track is null)
+ throw new ArgumentNullException(nameof(track));
+
+ return cache.Name == track.Name && (album is null ? cache.AlbumName is null : cache.AlbumName == album.Name) && cache.Artists.SequenceEqual(track.Artists);
+ // 如果album为空,要求cache中AlbumName也为空,如果album不为空,要求cache中AlbumName匹配
+ }
+ }
+}
diff --git a/NLyric/Caches/LyricCache.cs b/NLyric/Database/LyricInfo.cs
similarity index 52%
rename from NLyric/Caches/LyricCache.cs
rename to NLyric/Database/LyricInfo.cs
index 0b5e81b..b7b63e6 100644
--- a/NLyric/Caches/LyricCache.cs
+++ b/NLyric/Database/LyricInfo.cs
@@ -2,34 +2,40 @@
using Newtonsoft.Json;
using NLyric.Ncm;
-namespace NLyric.Caches {
- public sealed class LyricCache {
- public int Id { get; set; }
-
- public bool IsAbsoluteMusic { get; set; }
-
+namespace NLyric.Database {
+ ///
+ /// 歌词信息
+ ///
+ public sealed class LyricInfo {
+ ///
+ /// 原始歌词版本
+ ///
public int RawVersion { get; set; }
+ ///
+ /// 翻译歌词版本(如果有)
+ ///
public int TranslatedVersion { get; set; }
+ ///
+ /// 歌词校验值
+ ///
public string CheckSum { get; set; }
[JsonConstructor]
[Obsolete("Deserialization only", true)]
- public LyricCache() {
+ public LyricInfo() {
}
- public LyricCache(NcmLyric lyric, string checkSum) : this(lyric.Id, lyric.IsAbsoluteMusic, lyric.RawVersion, lyric.TranslatedVersion, checkSum) {
+ public LyricInfo(NcmLyric lyric, string checkSum) : this(lyric.RawVersion, lyric.TranslatedVersion, checkSum) {
if (!lyric.IsCollected)
throw new ArgumentException("未收录的歌词不能添加到缓存", nameof(lyric));
}
- public LyricCache(int id, bool isAbsoluteMusic, int rawVersion, int translatedVersion, string checkSum) {
+ public LyricInfo(int rawVersion, int translatedVersion, string checkSum) {
if (checkSum is null)
throw new ArgumentNullException(nameof(checkSum));
- Id = id;
- IsAbsoluteMusic = isAbsoluteMusic;
RawVersion = rawVersion;
TranslatedVersion = translatedVersion;
CheckSum = checkSum;
diff --git a/NLyric/Database/NLyricDatabase.cs b/NLyric/Database/NLyricDatabase.cs
new file mode 100644
index 0000000..d9daa6f
--- /dev/null
+++ b/NLyric/Database/NLyricDatabase.cs
@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+
+namespace NLyric.Database {
+ ///
+ /// NLyric数据库
+ ///
+ public sealed class NLyricDatabase {
+ ///
+ /// 专辑信息
+ ///
+ public List AlbumInfos { get; set; }
+
+ ///
+ /// 单曲信息
+ ///
+ public List TrackInfos { get; set; }
+
+ ///
+ /// 数据库格式版本
+ ///
+ public int FormatVersion { get; set; }
+
+ ///
+ /// 检查
+ ///
+ ///
+ public bool CheckFormatVersion() {
+ switch (FormatVersion) {
+ case 0:
+ case 1:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// 是否为老版本数据库
+ ///
+ ///
+ public bool IsOldFormat() {
+ return FormatVersion < 1;
+ }
+ }
+}
diff --git a/NLyric/Database/TrackInfo.cs b/NLyric/Database/TrackInfo.cs
new file mode 100644
index 0000000..e6e725b
--- /dev/null
+++ b/NLyric/Database/TrackInfo.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+using NLyric.Audio;
+
+namespace NLyric.Database {
+ ///
+ /// 单曲信息
+ ///
+ public sealed class TrackInfo {
+ ///
+ /// 名称
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 艺术家
+ ///
+ public IReadOnlyList Artists { get; set; }
+
+ ///
+ /// 专辑名
+ ///
+ public string AlbumName { get; set; }
+
+ ///
+ /// 网易云音乐ID
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 歌词缓存
+ ///
+ public LyricInfo Lyric { get; set; }
+
+ [JsonConstructor]
+ [Obsolete("Deserialization only", true)]
+ public TrackInfo() {
+ }
+
+ public TrackInfo(Track track, Album album, int id) : this(track.Name, track.Artists, album?.Name, id) {
+ }
+
+ public TrackInfo(string name, IEnumerable artists, string albumName, int id) {
+ if (name is null)
+ throw new ArgumentNullException(nameof(name));
+ if (artists is null)
+ throw new ArgumentNullException(nameof(artists));
+
+ Name = name;
+ Artists = artists.Select(t => t.Trim()).ToArray();
+ AlbumName = albumName;
+ Id = id;
+ }
+ }
+}
diff --git a/NLyric/NLyric.csproj b/NLyric/NLyric.csproj
index 6c9be46..c736131 100644
--- a/NLyric/NLyric.csproj
+++ b/NLyric/NLyric.csproj
@@ -4,8 +4,8 @@
NLyric
NLyric
Copyright © 2019 Wwh
- 2.2.6.4
- 2.2.6.4
+ 2.5.0.0
+ 2.5.0.0
..\bin\$(Configuration)
Exe
netcoreapp2.1;net472
@@ -16,9 +16,9 @@
-
-
-
+
+
+
diff --git a/NLyric/NLyricImpl.cs b/NLyric/NLyricImpl.cs
index 33a5476..bea9be0 100644
--- a/NLyric/NLyricImpl.cs
+++ b/NLyric/NLyricImpl.cs
@@ -6,10 +6,12 @@
using System.Threading.Tasks;
using Newtonsoft.Json;
using NLyric.Audio;
-using NLyric.Caches;
+using NLyric.Database;
using NLyric.Lyrics;
using NLyric.Ncm;
using NLyric.Settings;
+using TagLib;
+using File = System.IO.File;
namespace NLyric {
internal static class NLyricImpl {
@@ -17,37 +19,45 @@ internal static class NLyricImpl {
private static readonly FuzzySettings _fuzzySettings = AllSettings.Default.Fuzzy;
private static readonly MatchSettings _matchSettings = AllSettings.Default.Match;
private static readonly LyricSettings _lyricSettings = AllSettings.Default.Lyric;
- private static readonly Dictionary _cachedNcmAlbums = new Dictionary();
- // AlbumName -> Album
+ private static readonly HashSet _failMatchAlbums = new HashSet();
+ // AlbumName
private static readonly Dictionary _cachedNcmTrackses = new Dictionary();
// AlbumId -> Tracks
private static readonly Dictionary _cachedNcmLyrics = new Dictionary();
// TrackId -> Lyric
- private static string _allCachesPath;
- private static AllCaches _allCaches;
+ private static NLyricDatabase _database;
public static async Task ExecuteAsync(Arguments arguments) {
+ string databasePath;
+
await LoginIfNeedAsync(arguments);
- _allCachesPath = Path.Combine(arguments.Directory, ".nlyric");
- LoadLocalCaches();
+ databasePath = Path.Combine(arguments.Directory, ".nlyric");
+ LoadDatabase(databasePath);
foreach (string audioPath in Directory.EnumerateFiles(arguments.Directory, "*", SearchOption.AllDirectories)) {
string lrcPath;
- int? trackId;
lrcPath = Path.ChangeExtension(audioPath, ".lrc");
if (CanSkip(audioPath, lrcPath))
continue;
- Logger.Instance.LogInfo($"开始搜索文件\"{Path.GetFileName(audioPath)}\"的歌词。");
- trackId = await TryGetMusicId(audioPath);
- // 同时尝试通过163Key和专辑获取歌曲信息
- if (trackId is null)
- Logger.Instance.LogWarning($"无法找到文件\"{Path.GetFileName(audioPath)}\"的网易云音乐ID!");
- else
- await WriteLrcAsync(trackId.Value, lrcPath);
+ using (TagLib.File audioFile = TagLib.File.Create(audioPath)) {
+ Tag tag;
+ TrackInfo trackInfo;
+
+ Logger.Instance.LogInfo($"开始搜索文件\"{Path.GetFileName(audioPath)}\"的歌词。");
+ tag = audioFile.Tag;
+ trackInfo = await SearchTrackAsync(tag);
+ if (trackInfo is null)
+ Logger.Instance.LogWarning($"无法找到文件\"{Path.GetFileName(audioPath)}\"的网易云音乐ID!");
+ else {
+ Logger.Instance.LogInfo($"已获取文件\"{Path.GetFileName(audioPath)}\"的网易云音乐ID: {trackInfo.Id}。");
+ await TryDownloadLyricAsync(trackInfo, lrcPath);
+ }
+ }
+ SaveDatabaseCore(databasePath);
Logger.Instance.LogNewLine();
Logger.Instance.LogNewLine();
}
- SaveLocalCaches();
+ SaveDatabase(databasePath);
}
private static async Task LoginIfNeedAsync(Arguments arguments) {
@@ -89,56 +99,165 @@ private static bool IsAudioFile(string extension) {
return _searchSettings.AudioExtensions.Any(s => extension.Equals(s, StringComparison.OrdinalIgnoreCase));
}
- private static async Task TryGetMusicId(string audioPath) {
- int? trackId;
- ATL.Track atlTrack;
+ ///
+ /// 同时根据专辑信息以及歌曲信息获取网易云音乐上的歌曲
+ ///
+ ///
+ ///
+ private static async Task SearchTrackAsync(Tag tag) {
Track track;
Album album;
- NcmTrack ncmTrack;
- trackId = null;
- atlTrack = new ATL.Track(audioPath);
- track = new Track(atlTrack);
- album = Album.HasAlbumInfo(atlTrack) ? new Album(atlTrack, true) : null;
+ track = new Track(tag);
+ album = Album.HasAlbumInfo(tag) ? new Album(tag, true) : null;
+ // 获取Tag信息
try {
- // 歌曲无163Key,通过自己的算法匹配
- ncmTrack = await MapToAsync(track, album, audioPath);
- if (!(ncmTrack is null)) {
- trackId = ncmTrack.Id;
- Logger.Instance.LogInfo($"已获取文件\"{Path.GetFileName(audioPath)}\"的网易云音乐ID: {trackId}。");
- }
+ return await SearchTrackAsync(tag, track, album);
}
catch (Exception ex) {
Logger.Instance.LogException(ex);
}
- return trackId;
+ return null;
}
- private static async Task WriteLrcAsync(int trackId, string lrcPath) {
- LyricCache lyricCache;
+ ///
+ /// 同时根据专辑信息以及歌曲信息获取网易云音乐上的歌曲
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static async Task SearchTrackAsync(Tag tag, Track track, Album album) {
+ TrackInfo trackInfo;
+ int trackId;
+ NcmTrack ncmTrack;
+ bool byUser;
+
+ trackInfo = _database.TrackInfos.Match(track, album);
+ if (!(trackInfo is null))
+ return trackInfo;
+ // 先尝试从数据库获取歌曲
+ if (The163KeyHelper.TryGetTrackId(tag, out trackId)) {
+ // 尝试从163Key获取ID成功
+ ncmTrack = new NcmTrack(track, trackId);
+ }
+ else {
+ // 不存在163Key
+ AlbumInfo albumInfo;
+
+ albumInfo = album is null ? null : await SearchAlbumAsync(album);
+ // 尝试获取专辑信息
+ if (!(albumInfo is null)) {
+ // 网易云音乐收录了歌曲所在专辑
+ NcmTrack[] ncmTracks;
+
+ ncmTracks = (await GetAlbumTracksAsync(albumInfo)).Where(t => ComputeSimilarity(t.Name, track.Name, false) != 0).ToArray();
+ // 获取网易云音乐上专辑收录的歌曲
+ ncmTrack = MatchByUser(ncmTracks, track);
+ }
+ else
+ ncmTrack = null;
+ if (ncmTrack is null)
+ // 没有对应的专辑信息,使用无专辑匹配,或者网易云音乐上的专辑可能没收录这个歌曲,不清楚为什么,但是确实存在这个情况,比如专辑id:3094396
+ ncmTrack = await MapToAsync(track);
+ }
+ if (ncmTrack is null)
+ byUser = GetIdByUser("歌曲", out trackId);
+ else {
+ byUser = false;
+ trackId = 0;
+ }
+ if (ncmTrack is null && !byUser)
+ Logger.Instance.LogWarning("歌曲匹配失败!");
+ else {
+ trackInfo = new TrackInfo(track, album, byUser ? trackId : ncmTrack.Id);
+ _database.TrackInfos.Add(trackInfo);
+ Logger.Instance.LogInfo("歌曲匹配成功!");
+ }
+ return trackInfo;
+ }
+
+ ///
+ /// 根据专辑信息获取网易云音乐上的专辑
+ ///
+ ///
+ ///
+ private static async Task SearchAlbumAsync(Album album) {
+ AlbumInfo albumInfo;
+ string replacedAlbumName;
+ NcmAlbum ncmAlbum;
+ bool byUser;
+ int albumId;
+
+ albumInfo = _database.AlbumInfos.Match(album);
+ if (!(albumInfo is null))
+ return albumInfo;
+ // 先尝试从数据库获取专辑
+ replacedAlbumName = album.Name.ReplaceEx();
+ if (_failMatchAlbums.Contains(replacedAlbumName))
+ return null;
+ // 防止不停重复匹配一个专辑
+ ncmAlbum = await MapToAsync(album);
+ if (ncmAlbum is null)
+ byUser = GetIdByUser("专辑", out albumId);
+ else {
+ byUser = false;
+ albumId = 0;
+ }
+ if (ncmAlbum is null && !byUser) {
+ _failMatchAlbums.Add(replacedAlbumName);
+ Logger.Instance.LogWarning("专辑匹配失败!");
+ }
+ else {
+ albumInfo = new AlbumInfo(album, byUser ? albumId : ncmAlbum.Id);
+ _database.AlbumInfos.Add(albumInfo);
+ Logger.Instance.LogInfo("专辑匹配成功!");
+ }
+ return albumInfo;
+ }
+
+ private static async Task GetAlbumTracksAsync(AlbumInfo albumInfo) {
+ NcmTrack[] ncmTracks;
+
+ if (!_cachedNcmTrackses.TryGetValue(albumInfo.Id, out ncmTracks)) {
+ List list;
+
+ list = new List();
+ foreach (NcmTrack item in await CloudMusic.GetTracksAsync(albumInfo.Id))
+ if ((await GetLyricAsync(item.Id)).IsCollected)
+ list.Add(item);
+ ncmTracks = list.ToArray();
+ _cachedNcmTrackses[albumInfo.Id] = ncmTracks;
+ }
+ return ncmTracks;
+ }
+
+ private static async Task TryDownloadLyricAsync(TrackInfo trackInfo, string lrcPath) {
bool hasLrcFile;
string lyricCheckSum;
NcmLyric ncmLyric;
Lrc lrc;
- lyricCache = _allCaches.LyricCaches.Match(trackId);
hasLrcFile = File.Exists(lrcPath);
lyricCheckSum = hasLrcFile ? ComputeLyricCheckSum(File.ReadAllText(lrcPath)) : null;
try {
- ncmLyric = await GetLyricAsync(trackId);
+ ncmLyric = await GetLyricAsync(trackInfo.Id);
}
catch (Exception ex) {
Logger.Instance.LogException(ex);
- return;
+ return false;
}
if (hasLrcFile) {
// 如果歌词存在,判断是否需要覆盖或更新
- if (!(lyricCache is null) && lyricCache.CheckSum == lyricCheckSum) {
+ LyricInfo lyricInfo;
+
+ lyricInfo = trackInfo.Lyric;
+ if (!(lyricInfo is null) && lyricInfo.CheckSum == lyricCheckSum) {
// 歌词由NLyric创建
- if (ncmLyric.RawVersion <= lyricCache.RawVersion && ncmLyric.TranslatedVersion <= lyricCache.TranslatedVersion) {
+ if (ncmLyric.RawVersion <= lyricInfo.RawVersion && ncmLyric.TranslatedVersion <= lyricInfo.TranslatedVersion) {
// 是最新版本
Logger.Instance.LogInfo("本地歌词已是最新版本,正在跳过。", ConsoleColor.Yellow);
- return;
+ return false;
}
else {
// 不是最新版本
@@ -146,120 +265,40 @@ private static async Task WriteLrcAsync(int trackId, string lrcPath) {
Logger.Instance.LogInfo("本地歌词不是最新版本,正在更新。", ConsoleColor.Green);
else {
Logger.Instance.LogInfo("本地歌词不是最新版本但是自动更新被禁止,正在跳过。", ConsoleColor.Yellow);
- return;
+ return false;
}
}
}
else {
// 歌词非NLyric创建
- if (!_lyricSettings.Overwriting) {
- Logger.Instance.LogInfo("本地歌词已存在并且非NLyric创建,正在跳过。", ConsoleColor.Yellow);
- return;
+ if (_lyricSettings.Overwriting)
+ Logger.Instance.LogInfo("本地歌词非NLyric创建,正在更新。", ConsoleColor.Yellow);
+ else {
+ Logger.Instance.LogInfo("本地歌词非NLyric创建但是覆盖被禁止,正在跳过。", ConsoleColor.Yellow);
+ return false;
}
}
}
lrc = ToLrc(ncmLyric);
if (!(lrc is null)) {
+ // 歌词已收录,不是纯音乐
string lyric;
lyric = lrc.ToString();
- UpdateCache(ncmLyric, ComputeLyricCheckSum(lyric));
- File.WriteAllText(lrcPath, lyric);
- Logger.Instance.LogInfo("本地歌词下载完毕。", ConsoleColor.Magenta);
- }
- }
-
- #region mapping
- ///
- /// 同时根据专辑信息以及歌曲信息获取网易云音乐上的歌曲
- ///
- ///
- ///
- ///
- ///
- private static async Task MapToAsync(Track track, Album album, string audioPath) {
- if (track is null)
- throw new ArgumentNullException(nameof(track));
- if (audioPath is null)
- throw new ArgumentNullException(nameof(audioPath));
-
- string fileName;
- TrackCache trackCache;
- int trackId;
- NcmTrack ncmTrack;
-
- fileName = Path.GetFileName(audioPath);
- trackCache = album is null ? _allCaches.TrackCaches.Match(track, fileName) : _allCaches.TrackCaches.Match(track, album);
- // 有专辑信息就用专辑信息,没有专辑信息就用文件名
- if (!(trackCache is null))
- return new NcmTrack(track, trackCache.Id);
- // 先尝试从缓存获取歌曲
- if (The163KeyHelper.TryGetMusicId(audioPath, out trackId)) {
- // 尝试从163Key获取ID
- ncmTrack = new NcmTrack(track, trackId);
- }
- else {
- NcmAlbum ncmAlbum;
-
- ncmAlbum = null;
- if (!(album is null)) {
- // 存在专辑信息,尝试获取网易云音乐上对应的专辑
- AlbumCache albumCache;
-
- albumCache = _allCaches.AlbumCaches.Match(album);
- if (!(albumCache is null))
- ncmAlbum = new NcmAlbum(album, albumCache.Id);
- // 先尝试从缓存获取专辑
- if (ncmAlbum is null) {
- ncmAlbum = await MapToAsync(album);
- if (!(ncmAlbum is null))
- UpdateCache(album, ncmAlbum.Id);
- }
+ try {
+ File.WriteAllText(lrcPath, lyric);
}
- if (ncmAlbum is null) {
- // 没有对应的专辑信息,使用无专辑匹配
- ncmTrack = await MapToAsync(track);
+ catch (Exception ex) {
+ Logger.Instance.LogException(ex);
+ return false;
}
- else {
- // 网易云音乐收录了歌曲所在专辑
- NcmTrack[] ncmTracks;
-
- ncmTracks = (await GetTracksAsync(ncmAlbum)).Where(t => ComputeSimilarity(t.Name, track.Name, false) != 0).ToArray();
- // 获取网易云音乐上专辑收录的歌曲
- ncmTrack = MatchByUser(ncmTracks, track);
- if (ncmTrack is null)
- // 网易云音乐上的专辑可能没收录这个歌曲,不清楚为什么,但是确实存在这个情况,比如专辑id:3094396
- ncmTrack = await MapToAsync(track);
- }
- }
- if (ncmTrack is null)
- Logger.Instance.LogWarning("歌曲匹配失败!");
- else {
- Logger.Instance.LogInfo("歌曲匹配成功!");
- if (album is null)
- UpdateCache(track, fileName, ncmTrack.Id);
- else
- UpdateCache(track, album, ncmTrack.Id);
- }
- return ncmTrack;
- }
-
- private static async Task GetTracksAsync(NcmAlbum ncmAlbum) {
- NcmTrack[] ncmTracks;
-
- if (!_cachedNcmTrackses.TryGetValue(ncmAlbum.Id, out ncmTracks)) {
- List list;
-
- list = new List();
- foreach (NcmTrack item in await CloudMusic.GetTracksAsync(ncmAlbum.Id))
- if ((await GetLyricAsync(item.Id)).IsCollected)
- list.Add(item);
- ncmTracks = list.ToArray();
- _cachedNcmTrackses[ncmAlbum.Id] = ncmTracks;
+ trackInfo.Lyric = new LyricInfo(ncmLyric, ComputeLyricCheckSum(lyric));
+ Logger.Instance.LogInfo("本地歌词下载完毕。", ConsoleColor.Magenta);
}
- return ncmTracks;
+ return true;
}
+ #region map
///
/// 获取网易云音乐上的歌曲,自动尝试带艺术家与不带艺术家搜索
///
@@ -290,12 +329,8 @@ private static async Task MapToAsync(Album album) {
if (album is null)
throw new ArgumentNullException(nameof(album));
- string replacedAlbumName;
NcmAlbum ncmAlbum;
- replacedAlbumName = album.Name.ReplaceEx();
- if (_cachedNcmAlbums.TryGetValue(replacedAlbumName, out ncmAlbum))
- return ncmAlbum;
Logger.Instance.LogInfo($"开始搜索专辑\"{album}\"。");
Logger.Instance.LogWarning("正在尝试带艺术家搜索,结果可能将过少!");
ncmAlbum = await MapToAsync(album, true);
@@ -303,13 +338,6 @@ private static async Task MapToAsync(Album album) {
Logger.Instance.LogWarning("正在尝试忽略艺术家搜索,结果可能将不精确!");
ncmAlbum = await MapToAsync(album, false);
}
- if (ncmAlbum is null) {
- Logger.Instance.LogWarning("专辑匹配失败!");
- _cachedNcmAlbums[replacedAlbumName] = null;
- return null;
- }
- Logger.Instance.LogInfo("专辑匹配成功!");
- _cachedNcmAlbums[replacedAlbumName] = ncmAlbum;
return ncmAlbum;
}
@@ -320,11 +348,22 @@ private static async Task MapToAsync(Album album) {
/// 是否带艺术家搜索
///
private static async Task MapToAsync(Track track, bool withArtists) {
- List list;
NcmTrack[] ncmTracks;
+ List list;
+ try {
+ ncmTracks = await CloudMusic.SearchTrackAsync(track, _searchSettings.Limit, withArtists);
+ }
+ catch (KeywordForbiddenException ex1) {
+ Logger.Instance.LogError(ex1.Message);
+ return null;
+ }
+ catch (Exception ex2) {
+ Logger.Instance.LogException(ex2);
+ return null;
+ }
list = new List();
- foreach (NcmTrack item in (await CloudMusic.SearchTrackAsync(track, _searchSettings.Limit, withArtists)).Where(t => ComputeSimilarity(t.Name, track.Name, false) != 0))
+ foreach (NcmTrack item in ncmTracks.Where(t => ComputeSimilarity(t.Name, track.Name, false) != 0))
if ((await GetLyricAsync(item.Id)).IsCollected)
list.Add(item);
ncmTracks = list.ToArray();
@@ -340,92 +379,72 @@ private static async Task MapToAsync(Track track, bool withArtists) {
private static async Task MapToAsync(Album album, bool withArtists) {
NcmAlbum[] ncmAlbums;
- ncmAlbums = (await CloudMusic.SearchAlbumAsync(album, _searchSettings.Limit, withArtists)).Where(t => ComputeSimilarity(t.Name, album.Name, false) != 0).ToArray();
- return MatchByUser(ncmAlbums, album);
- }
- #endregion
-
- #region local cache
- private static void LoadLocalCaches() {
- if (File.Exists(_allCachesPath)) {
- _allCaches = JsonConvert.DeserializeObject(File.ReadAllText(_allCachesPath));
- NormalizeAllCaches();
- Logger.Instance.LogInfo($"搜索缓存\"{_allCachesPath}\"加载成功。");
+ try {
+ ncmAlbums = await CloudMusic.SearchAlbumAsync(album, _searchSettings.Limit, withArtists);
}
- else {
- _allCaches = new AllCaches() {
- AlbumCaches = new List(),
- LyricCaches = new List(),
- TrackCaches = new List()
- };
+ catch (KeywordForbiddenException ex1) {
+ Logger.Instance.LogError(ex1.Message);
+ return null;
}
- }
-
- private static void SaveLocalCaches() {
- NormalizeAllCaches();
- SaveLocalCachesCore(_allCachesPath);
- Logger.Instance.LogInfo($"搜索缓存\"{_allCachesPath}\"已被保存。");
- }
-
- private static void NormalizeAllCaches() {
- _allCaches.AlbumCaches.Sort((x, y) => string.CompareOrdinal(x.Name, y.Name));
- _allCaches.TrackCaches.Sort((x, y) => string.CompareOrdinal(x.Name, y.Name));
- _allCaches.LyricCaches.Sort((x, y) => x.Id.CompareTo(y.Id));
- foreach (TrackCache cache in _allCaches.TrackCaches) {
- for (int i = 0; i < cache.Artists.Length; i++)
- cache.Artists[i] = cache.Artists[i].Trim();
- Array.Sort(cache.Artists, StringHelper.OrdinalComparer);
+ catch (Exception ex2) {
+ Logger.Instance.LogException(ex2);
+ return null;
}
+ ncmAlbums = ncmAlbums.Where(t => ComputeSimilarity(t.Name, album.Name, false) != 0).ToArray();
+ return MatchByUser(ncmAlbums, album);
}
+ #endregion
- private static void SaveLocalCachesCore(string cachePath) {
- File.SetAttributes(_allCachesPath, FileAttributes.Normal);
- File.WriteAllText(cachePath, JsonConvert.SerializeObject(_allCaches));
- File.SetAttributes(_allCachesPath, FileAttributes.Hidden);
- }
-
- private static void UpdateCache(Album album, int id) {
- AlbumCache cache;
-
- cache = _allCaches.AlbumCaches.Match(album);
- if (!(cache is null))
- return;
- _allCaches.AlbumCaches.Add(new AlbumCache(album, id));
- OnCacheUpdated();
+ #region database
+ private static void LoadDatabase(string databasePath) {
+ if (File.Exists(databasePath)) {
+ _database = JsonConvert.DeserializeObject(File.ReadAllText(databasePath));
+ if (!_database.CheckFormatVersion())
+ throw new InvalidOperationException("尝试加载新格式数据库。");
+ if (_database.IsOldFormat())
+ Logger.Instance.LogWarning("不兼容的老格式数据库,将被覆盖重建!");
+ else {
+ SortDatabase();
+ Logger.Instance.LogInfo($"搜索数据库\"{databasePath}\"加载成功。");
+ return;
+ }
+ }
+ _database = new NLyricDatabase() {
+ AlbumInfos = new List(),
+ TrackInfos = new List(),
+ FormatVersion = 1
+ };
+ if (File.Exists(databasePath))
+ File.Delete(databasePath);
+ SaveDatabaseCore(databasePath);
+ File.SetAttributes(databasePath, FileAttributes.Hidden);
}
- private static void UpdateCache(Track track, Album album, int id) {
- TrackCache cache;
-
- cache = _allCaches.TrackCaches.Match(track, album);
- if (!(cache is null))
- return;
- _allCaches.TrackCaches.Add(new TrackCache(track, album, id));
- OnCacheUpdated();
+ private static void SaveDatabase(string databasePath) {
+ SortDatabase();
+ SaveDatabaseCore(databasePath);
+ Logger.Instance.LogInfo($"搜索数据库\"{databasePath}\"已被保存。");
}
- private static void UpdateCache(Track track, string fileName, int id) {
- TrackCache cache;
-
- cache = _allCaches.TrackCaches.Match(track, fileName);
- if (!(cache is null))
- return;
- _allCaches.TrackCaches.Add(new TrackCache(track, fileName, id));
- OnCacheUpdated();
+ private static void SortDatabase() {
+ _database.AlbumInfos.Sort((x, y) => string.CompareOrdinal(x.Name, y.Name));
+ _database.TrackInfos.Sort((x, y) => string.CompareOrdinal(x.Name, y.Name));
}
- private static void UpdateCache(NcmLyric lyric, string checkSum) {
- int index;
-
- index = _allCaches.LyricCaches.FindIndex(t => t.IsMatched(lyric.Id));
- if (index != -1)
- _allCaches.LyricCaches.RemoveAt(index);
- _allCaches.LyricCaches.Add(new LyricCache(lyric, checkSum));
- OnCacheUpdated();
+ private static void SaveDatabaseCore(string databasePath) {
+ using (FileStream stream = new FileStream(databasePath, FileMode.OpenOrCreate))
+ using (StreamWriter writer = new StreamWriter(stream))
+ writer.Write(FormatJson(JsonConvert.SerializeObject(_database)));
}
- private static void OnCacheUpdated() {
- SaveLocalCachesCore(_allCachesPath);
+ private static string FormatJson(string json) {
+ using (StringWriter writer = new StringWriter())
+ using (JsonTextWriter jsonWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented })
+ using (StringReader reader = new StringReader(json))
+ using (JsonTextReader jsonReader = new JsonTextReader(reader)) {
+ jsonWriter.WriteToken(jsonReader);
+ return writer.ToString();
+ }
}
#endregion
@@ -471,9 +490,9 @@ private static TSource MatchExactly(TSource[] sources, TTarget
}
if (x != y)
goto not_equal;
- if (source.Artists.Length != target.Artists.Length)
+ if (source.Artists.Count != target.Artists.Count)
goto not_equal;
- for (int i = 0; i < source.Artists.Length; i++) {
+ for (int i = 0; i < source.Artists.Count; i++) {
x = source.Artists[i];
y = target.Artists[i];
if (fuzzy) {
@@ -495,7 +514,7 @@ private static TSource Select(TSource[] sources, TTarget targe
if (sources.Length == 0)
return null;
- Logger.Instance.LogInfo("请手动输入1,2,3...选择匹配的项,若不存在,请输入P。");
+ Logger.Instance.LogInfo("请手动输入1,2,3...选择匹配的项,若不存在,请直接按下回车键。");
Logger.Instance.LogInfo("对比项:" + TrackOrAlbumToString(target));
for (int i = 0; i < sources.Length; i++) {
double nameSimilarity;
@@ -516,7 +535,7 @@ private static TSource Select(TSource[] sources, TTarget targe
int index;
userInput = Console.ReadLine().Trim();
- if (userInput.ToUpperInvariant() == "P")
+ if (userInput.Length == 0)
break;
if (int.TryParse(userInput, out index)) {
index -= 1;
@@ -532,7 +551,7 @@ private static TSource Select(TSource[] sources, TTarget targe
return result;
string TrackOrAlbumToString(ITrackOrAlbum trackOrAlbum) {
- if (trackOrAlbum.Artists.Length == 0)
+ if (trackOrAlbum.Artists.Count == 0)
return trackOrAlbum.Name;
return trackOrAlbum.Name + " by " + string.Join(",", trackOrAlbum.Artists);
}
@@ -549,9 +568,25 @@ private static double ComputeSimilarity(string x, string y, bool fuzzy) {
y = y.Trim();
return Levenshtein.Compute(x, y);
}
+
+ private static bool GetIdByUser(string s, out int id) {
+ Logger.Instance.LogInfo($"请输入{s}的网易云音乐ID,若不存在,请直接按下回车键。");
+ do {
+ string userInput;
+
+ userInput = Console.ReadLine().Trim();
+ if (userInput.Length == 0)
+ break;
+ if (int.TryParse(userInput, out id))
+ return true;
+ Logger.Instance.LogWarning("输入有误,请重新输入!");
+ } while (true);
+ id = 0;
+ return false;
+ }
#endregion
- #region lyrics
+ #region lyric
private static async Task GetLyricAsync(int trackId) {
NcmLyric lyric;
diff --git a/NLyric/Ncm/CloudMusic.cs b/NLyric/Ncm/CloudMusic.cs
index e02f33c..c6cef04 100644
--- a/NLyric/Ncm/CloudMusic.cs
+++ b/NLyric/Ncm/CloudMusic.cs
@@ -57,7 +57,7 @@ public static async Task SearchTrackAsync(Track track, int limit, bo
throw new ApplicationException(nameof(CloudMusicApiProviders.Search) + " API错误");
json = (JObject)json["result"];
if (json is null)
- throw new ArgumentException($"\"{string.Join(" ", keywords)}\" 中有关键词被屏蔽");
+ throw new KeywordForbiddenException(string.Join(" ", keywords));
songs = json["songs"] as JArray;
if (songs is null)
return Array.Empty();
@@ -94,7 +94,7 @@ public static async Task SearchAlbumAsync(Album album, int limit, bo
throw new ApplicationException(nameof(CloudMusicApiProviders.Search) + " API错误");
json = (JObject)json["result"];
if (json is null)
- throw new ArgumentException($"\"{string.Join(" ", keywords)}\" 中有关键词被屏蔽");
+ throw new KeywordForbiddenException(string.Join(" ", keywords));
albums = json["albums"] as JArray;
if (albums is null)
return Array.Empty();
@@ -156,7 +156,7 @@ private static NcmAlbum ParseAlbum(JToken json) {
Album album;
NcmAlbum ncmAlbum;
- album = new Album((string)json["name"], ParseNames(json["artists"]), (int)json["size"], TimeStampToDateTime((long)json["publishTime"]).Year);
+ album = new Album((string)json["name"], ParseNames(json["artists"]));
ncmAlbum = new NcmAlbum(album, (int)json["id"]);
return ncmAlbum;
}
@@ -185,10 +185,6 @@ private static (Lrc, int) ParseLyric(JToken json) {
return (lrc, version);
}
- private static DateTime TimeStampToDateTime(long timeStamp) {
- return new DateTime(1970, 1, 1).AddMilliseconds(timeStamp);
- }
-
internal static class NormalApi {
private const string SEARCH_URL = "http://music.163.com/api/search/pc";
private const string ALBUM_URL = "http://music.163.com/api/album";
diff --git a/NLyric/Ncm/KeywordForbiddenException.cs b/NLyric/Ncm/KeywordForbiddenException.cs
new file mode 100644
index 0000000..1195756
--- /dev/null
+++ b/NLyric/Ncm/KeywordForbiddenException.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace NLyric.Ncm {
+ ///
+ /// 关键词被禁止
+ ///
+ [Serializable]
+ public sealed class KeywordForbiddenException : Exception {
+ public KeywordForbiddenException() {
+ }
+
+ public KeywordForbiddenException(string text) : base($"\"{text}\" 中有关键词被屏蔽") {
+ }
+
+ private KeywordForbiddenException(SerializationInfo info, StreamingContext context) : base(info, context) {
+ }
+ }
+}
diff --git a/NLyric/Ncm/NcmAlbum.cs b/NLyric/Ncm/NcmAlbum.cs
index 4078781..499b83d 100644
--- a/NLyric/Ncm/NcmAlbum.cs
+++ b/NLyric/Ncm/NcmAlbum.cs
@@ -6,7 +6,7 @@ public sealed class NcmAlbum : Album {
public int Id => _id;
- public NcmAlbum(Album album, int id) : base(album.Name, album.Artists, album.TrackCount, album.Year) {
+ public NcmAlbum(Album album, int id) : base(album.Name, album.Artists) {
_id = id;
}
diff --git a/NLyric/Settings.json b/NLyric/Settings.json
index 623c947..8fba35f 100644
--- a/NLyric/Settings.json
+++ b/NLyric/Settings.json
@@ -70,6 +70,6 @@
], // 歌词模式,依次尝试每一个模式直到成功,Merged表示混合未翻译和翻译后歌词,Raw表示未翻译的歌词,Translated表示翻译后的歌词
"SimplifyTranslated": true, // 部分翻译后的歌词是繁体的,这个选项可以简体化翻译后的歌词
"AutoUpdate": true, // 是否自动更新由NLyric创建的歌词
- "Overwriting": false // 是否覆盖非NLyric创建的歌词
+ "Overwriting": true // 是否覆盖非NLyric创建的歌词
}
}
diff --git a/NLyric/StringHelper.cs b/NLyric/StringHelper.cs
index 4aa59d0..1523007 100644
--- a/NLyric/StringHelper.cs
+++ b/NLyric/StringHelper.cs
@@ -16,7 +16,7 @@ internal static class StringHelper {
public static IComparer OrdinalComparer => StringOrdinalComparer.Instance;
///
- /// 获取非空字符串,并且清楚首尾空格
+ /// 获取非空字符串,并且清除首尾空格
///
///
///
@@ -105,7 +105,7 @@ public static string[] SplitEx(this string value) {
if (value is null)
throw new ArgumentNullException(nameof(value));
- return value.Split(_searchSettings.Separators, StringSplitOptions.RemoveEmptyEntries);
+ return value.Split(_searchSettings.Separators, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
}
///
diff --git a/NLyric/The163KeyHelper.cs b/NLyric/The163KeyHelper.cs
index 864c255..ce79266 100644
--- a/NLyric/The163KeyHelper.cs
+++ b/NLyric/The163KeyHelper.cs
@@ -1,97 +1,62 @@
using System;
-using System.IO;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json.Linq;
+using TagLib;
namespace NLyric {
///
/// 通过163Key直接获取歌曲ID
///
internal static class The163KeyHelper {
- private static readonly byte[] _163Start = Encoding.UTF8.GetBytes("163 key(Don't modify):");
- private static readonly byte[] _163EndMp3 = { 0x54, 0x41, 0x4C, 0x42 };
- private static readonly byte[] _163EndFlac = { 0x0, 0x0, 0x0, 0x45 };
- private static readonly Aes _aes;
+ private static readonly Aes _aes = Create163Aes();
- static The163KeyHelper() {
- _aes = Aes.Create();
- _aes.BlockSize = 128;
- _aes.Key = Encoding.UTF8.GetBytes(@"#14ljk_!\]&0U<'(");
- _aes.Mode = CipherMode.ECB;
- _aes.Padding = PaddingMode.PKCS7;
- }
+ private static Aes Create163Aes() {
+ Aes aes;
- public static bool TryGetMusicId(string filePath, out int trackId) {
- string extension;
- byte[] byt163Key;
+ aes = Aes.Create();
+ aes.BlockSize = 128;
+ aes.Key = Encoding.UTF8.GetBytes(@"#14ljk_!\]&0U<'(");
+ aes.Mode = CipherMode.ECB;
+ aes.Padding = PaddingMode.PKCS7;
+ return aes;
+ }
- extension = Path.GetExtension(filePath);
- switch (extension.ToUpperInvariant()) {
- case ".FLAC":
- byt163Key = Get163Key(filePath, false);
- break;
- case ".MP3":
- byt163Key = Get163Key(filePath, true);
- break;
- default:
- byt163Key = null;
- break;
+ ///
+ /// 尝试获取网易云音乐ID
+ ///
+ ///
+ ///
+ ///
+ public static bool TryGetTrackId(Tag tag, out int trackId) {
+ if (tag is null)
+ throw new ArgumentNullException(nameof(tag));
+
+ string the163Key;
+
+ trackId = 0;
+ the163Key = tag.Comment;
+ if (!Is163KeyCandidate(the163Key))
+ the163Key = tag.Description;
+ if (!Is163KeyCandidate(the163Key))
+ return false;
+ try {
+ byte[] byt163Key;
+
+ the163Key = the163Key.Substring(22);
+ byt163Key = Convert.FromBase64String(the163Key);
+ using (ICryptoTransform cryptoTransform = _aes.CreateDecryptor())
+ byt163Key = cryptoTransform.TransformFinalBlock(byt163Key, 0, byt163Key.Length);
+ trackId = (int)JObject.Parse(Encoding.UTF8.GetString(byt163Key).Substring(6))["musicId"];
}
- if (byt163Key is null) {
- trackId = 0;
+ catch {
return false;
}
- trackId = GetMusicId(byt163Key);
return true;
}
- private static byte[] Get163Key(string filePath, bool isMp3) {
- byte[] bytFile;
- int startIndex;
- int endIndex;
- byte[] byt163Key;
-
- bytFile = new byte[0x4000];
- using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
- stream.Read(bytFile, 0, bytFile.Length);
- startIndex = GetIndex(bytFile, _163Start, 0);
- if (startIndex == -1)
- return null;
- if (isMp3)
- endIndex = GetIndex(bytFile, _163EndMp3, startIndex);
- else
- endIndex = GetIndex(bytFile, _163EndFlac, startIndex) - 1;
- if (endIndex == -1)
- return null;
- byt163Key = new byte[endIndex - startIndex - _163Start.Length];
- Buffer.BlockCopy(bytFile, startIndex + _163Start.Length, byt163Key, 0, byt163Key.Length);
- return byt163Key;
- }
-
- private static int GetMusicId(byte[] byt163Key) {
- byt163Key = Convert.FromBase64String(Encoding.UTF8.GetString(byt163Key));
- using (ICryptoTransform cryptoTransform = _aes.CreateDecryptor())
- byt163Key = cryptoTransform.TransformFinalBlock(byt163Key, 0, byt163Key.Length);
- return (int)JObject.Parse(Encoding.UTF8.GetString(byt163Key).Substring(6))["musicId"];
- }
-
- private static int GetIndex(byte[] src, byte[] dest, int startIndex) {
- return GetIndex(src, dest, startIndex, src.Length - dest.Length);
- }
-
- private static int GetIndex(byte[] src, byte[] dest, int startIndex, int endIndex) {
- int j;
-
- for (int i = startIndex; i < endIndex + 1; i++)
- if (src[i] == dest[0]) {
- for (j = 1; j < dest.Length; j++)
- if (src[i + j] != dest[j])
- break;
- if (j == dest.Length)
- return i;
- }
- return -1;
+ private static bool Is163KeyCandidate(string s) {
+ return !string.IsNullOrEmpty(s) && s.StartsWith("163 key(Don't modify):", StringComparison.Ordinal);
}
}
}
diff --git a/README.md b/README.md
index 14ce526..0d98975 100644
--- a/README.md
+++ b/README.md
@@ -118,7 +118,7 @@ Windows用户专属GUI。
], // 歌词模式,依次尝试每一个模式直到成功,Merged表示混合未翻译和翻译后歌词,Raw表示未翻译的歌词,Translated表示翻译后的歌词
"SimplifyTranslated": true, // 部分翻译后的歌词是繁体的,这个选项可以简体化翻译后的歌词
"AutoUpdate": true, // 是否自动更新由NLyric创建的歌词
- "Overwriting": false // 是否覆盖非NLyric创建的歌词
+ "Overwriting": true // 是否覆盖非NLyric创建的歌词
}
}
```