diff --git a/ZonyLrcTools.sln.DotSettings b/ZonyLrcTools.sln.DotSettings
index 43ff6d0..4eeb8d0 100644
--- a/ZonyLrcTools.sln.DotSettings
+++ b/ZonyLrcTools.sln.DotSettings
@@ -1,2 +1,3 @@
+ QQ
True
\ No newline at end of file
diff --git a/src/ZonyLrcTools.Common/MusicScanner/CsvFileMusicScanner.cs b/src/ZonyLrcTools.Common/MusicScanner/CsvFileMusicScanner.cs
new file mode 100644
index 0000000..c222567
--- /dev/null
+++ b/src/ZonyLrcTools.Common/MusicScanner/CsvFileMusicScanner.cs
@@ -0,0 +1,23 @@
+using ZonyLrcTools.Common.Infrastructure.DependencyInject;
+
+namespace ZonyLrcTools.Common.MusicScanner;
+
+public class CsvFileMusicScanner : ITransientDependency
+{
+ public async Task> GetMusicInfoFromCsvFileAsync(string csvFilePath, ManualDownloadOptions options)
+ {
+ var csvFileContent = await File.ReadAllTextAsync(csvFilePath);
+ var csvFileLines = csvFileContent.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
+ return csvFileLines.Skip(1).Select(line => GetMusicInfoFromCsvFileLine(line, options)).ToList();
+ }
+
+ private MusicInfo GetMusicInfoFromCsvFileLine(string csvFileLine, ManualDownloadOptions options)
+ {
+ var csvFileLineItems = csvFileLine.Split(',');
+ var name = csvFileLineItems[0];
+ var artist = csvFileLineItems[1];
+ var fakeFilePath = Path.Combine(options.OutputDirectory, options.OutputFileNamePattern.Replace("{Name}", name).Replace("{Artist}", artist));
+ var musicInfo = new MusicInfo(fakeFilePath, name, artist);
+ return musicInfo;
+ }
+}
\ No newline at end of file
diff --git a/src/ZonyLrcTools.Common/MusicScanner/ManualDownloadOptions.cs b/src/ZonyLrcTools.Common/MusicScanner/ManualDownloadOptions.cs
new file mode 100644
index 0000000..113ede0
--- /dev/null
+++ b/src/ZonyLrcTools.Common/MusicScanner/ManualDownloadOptions.cs
@@ -0,0 +1,7 @@
+namespace ZonyLrcTools.Common.MusicScanner;
+
+public class ManualDownloadOptions
+{
+ public string OutputFileNamePattern { get; set; } = "{Artist} - {Name}.lrc";
+ public string OutputDirectory { get; set; } = "DownloadedLrc";
+}
\ No newline at end of file
diff --git a/tests/ZonyLrcTools.Tests/MusicScanner/CsvFileMusicScannerTests.cs b/tests/ZonyLrcTools.Tests/MusicScanner/CsvFileMusicScannerTests.cs
new file mode 100644
index 0000000..778411b
--- /dev/null
+++ b/tests/ZonyLrcTools.Tests/MusicScanner/CsvFileMusicScannerTests.cs
@@ -0,0 +1,21 @@
+using System.IO;
+using System.Threading.Tasks;
+using Shouldly;
+using Xunit;
+using ZonyLrcTools.Common.MusicScanner;
+
+namespace ZonyLrcTools.Tests.MusicScanner;
+
+public class CsvFileMusicScannerTests : TestBase
+{
+ [Fact]
+ public async Task GetMusicInfoFromCsvFileAsync_Test()
+ {
+ var musicScanner = GetService();
+ var musicInfo = await musicScanner.GetMusicInfoFromCsvFileAsync(Path.Combine("TestData", "test.csv"), new ManualDownloadOptions());
+
+ musicInfo.ShouldNotBeNull();
+ musicInfo.Count.ShouldBeGreaterThan(0);
+ musicInfo.Count.ShouldBe(5);
+ }
+}
\ No newline at end of file
diff --git a/tests/ZonyLrcTools.Tests/TestData/test.csv b/tests/ZonyLrcTools.Tests/TestData/test.csv
new file mode 100644
index 0000000..b768e4d
--- /dev/null
+++ b/tests/ZonyLrcTools.Tests/TestData/test.csv
@@ -0,0 +1,6 @@
+Song,Artist
+刀马旦,李玟
+发如雪,周杰伦
+说书人,寅子
+爱的供养,张国荣
+七里香,周杰伦
\ No newline at end of file
diff --git a/tests/ZonyLrcTools.Tests/ZonyLrcTools.Tests.csproj b/tests/ZonyLrcTools.Tests/ZonyLrcTools.Tests.csproj
index 568cceb..7dc4ed1 100644
--- a/tests/ZonyLrcTools.Tests/ZonyLrcTools.Tests.csproj
+++ b/tests/ZonyLrcTools.Tests/ZonyLrcTools.Tests.csproj
@@ -33,6 +33,9 @@
Always
+
+ PreserveNewest
+