9 Commits

Author SHA1 Message Date
real-zony
d4a6a46078 feat: Restore config yaml file. 2022-07-25 20:12:19 +08:00
real-zony
0772f5888f fix: Fix the problem of QQ lyrics downloader prompting error.
The reason for this is that the old API has been deprecated.
2022-07-25 20:11:30 +08:00
real-zony
950652c040 fix: Fixed the issue of inaccurate download results. 2022-07-25 19:45:35 +08:00
real-zony
55f720c1a1 Fixed the null reference exception prompted when downloading pure music. 2022-07-24 23:48:46 +08:00
real-zony
7463709a39 feat: Deleted unused components. 2022-04-28 23:58:03 +08:00
real-zony
07e660e13b Add new frontend code. 2022-04-28 23:53:14 +08:00
real-zony
a51b399a6c Delete frontend code. 2022-04-28 23:50:18 +08:00
real-zony
279eba48f8 feat: Support configure lyrics file encoding. 2022-04-27 21:38:51 +08:00
real-zony
f3b1dacb0c fix: Improve tag loader conditions. 2022-04-27 21:25:15 +08:00
33 changed files with 414 additions and 807 deletions

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Logging;
@@ -49,7 +50,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
[Option("-d|--dir", CommandOptionType.SingleValue, Description = "指定需要扫描的目录。")]
[DirectoryExists]
public string Directory { get; set; }
public string SongsDirectory { get; set; }
[Option("-l|--lyric", CommandOptionType.NoValue, Description = "指定程序需要下载歌词文件。")]
public bool DownloadLyric { get; set; }
@@ -60,6 +61,8 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
[Option("-n|--number", CommandOptionType.SingleValue, Description = "指定下载时候的线程数量。(默认值 2)")]
public int ParallelNumber { get; set; } = 2;
[Option] public string ErrorMessage { get; set; } = Path.Combine(Directory.GetCurrentDirectory(), "error.log");
#endregion
protected override async Task<int> OnExecuteAsync(CommandLineApplication app)
@@ -82,7 +85,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
private async Task<List<string>> ScanMusicFilesAsync()
{
var files = (await _fileScanner.ScanAsync(Directory, _options.SupportFileExtensions))
var files = (await _fileScanner.ScanAsync(SongsDirectory, _options.SupportFileExtensions))
.SelectMany(t => t.FilePaths)
.ToList();
@@ -161,7 +164,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
await Task.WhenAll(warpTaskList);
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count} 失败{musicInfos.Count - musicInfos.Count(m => m.IsSuccessful)}。");
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => m.IsSuccessful == false)}。");
}
private async Task DownloadLyricTaskLogicAsync(IEnumerable<ILyricDownloader> downloaderList, MusicInfo info)
@@ -179,22 +182,22 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
File.Delete(lyricFilePath);
}
info.IsSuccessful = true;
if (lyric.IsPruneMusic)
{
info.IsSuccessful = true;
return;
}
await using var stream = new FileStream(lyricFilePath, FileMode.Create);
await using var sw = new StreamWriter(stream);
await sw.WriteAsync(lyric.ToString());
await sw.FlushAsync();
await using var sw = new BinaryWriter(stream);
sw.Write(EncodingConvert(lyric));
await stream.FlushAsync();
}
catch (ErrorCodeException ex)
{
if (ex.ErrorCode == ErrorCodes.NoMatchingSong)
{
info.IsSuccessful = false;
}
info.IsSuccessful = ex.ErrorCode == ErrorCodes.NoMatchingSong;
_logger.LogWarningInfo(ex);
}
@@ -203,10 +206,6 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
_logger.LogError($"下载歌词文件时发生错误:{ex.Message},歌曲名: {info.Name},歌手: {info.Artist}。");
info.IsSuccessful = false;
}
finally
{
info.IsSuccessful = true;
}
}
foreach (var downloader in downloaderList)
@@ -221,6 +220,17 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
}
}
private byte[] EncodingConvert(LyricItemCollection lyric)
{
var supportEncodings = Encoding.GetEncodings();
if (supportEncodings.All(x => x.Name != _options.Provider.Lyric.Config.FileEncoding))
{
throw new ErrorCodeException(ErrorCodes.NotSupportedFileEncoding);
}
return Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(_options.Provider.Lyric.Config.FileEncoding), lyric.GetUtf8Bytes());
}
#endregion
#region > Ablum image download logic <
@@ -236,7 +246,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
await Task.WhenAll(warpTaskList);
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => !m.IsSuccessful)}。");
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => m.IsSuccessful == false)}。");
}
private async Task DownloadAlbumTaskLogicAsync(IAlbumDownloader downloader, MusicInfo info)

View File

@@ -31,4 +31,9 @@ public class LyricConfigOption
/// 如果歌词文件已经存在,是否跳过这些文件
/// </summary>
public bool IsSkipExistLyricFiles { get; set; } = false;
/// <summary>
/// 歌词文件的编码格式。
/// </summary>
public string FileEncoding { get; set; } = "utf-8";
}

View File

@@ -27,6 +27,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Exceptions
/// </summary>
public const int NoFilesWereScanned = 10004;
/// <summary>
/// 文本: 指定的编码不受支持,请检查配置,所有受支持的编码名称。
/// </summary>
public const int NotSupportedFileEncoding = 10005;
#endregion
#region > <

View File

@@ -107,5 +107,10 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric
ForEach(lyric => lyricBuilder.Append(lyric).Append(Option.LineBreak));
return lyricBuilder.ToString().TrimEnd(Option.LineBreak);
}
public byte[] GetUtf8Bytes()
{
return Encoding.UTF8.GetBytes(ToString());
}
}
}

View File

@@ -6,32 +6,26 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic.JsonModel
{
public class SongSearchRequest
{
[JsonProperty("remoteplace")] public string RemotePlace { get; set; }
[JsonProperty("format")]
public string Format { get; protected set; }
[JsonProperty("p")] public int Page { get; set; }
[JsonProperty("inCharset")]
public string InCharset { get; protected set; }
[JsonProperty("n")] public int Limit { get; set; }
[JsonProperty("w")] public string Keyword { get; set; }
[JsonProperty("format")] public string ResultFormat { get; set; }
[JsonProperty("inCharset")] public string InCharset { get; set; }
[JsonProperty("outCharset")] public string OutCharset { get; set; }
[JsonProperty("platform")] public string Platform { get; set; }
[JsonProperty("outCharset")]
public string OutCharset { get; protected set; }
[JsonProperty("platform")]
public string Platform { get; protected set; }
[JsonProperty("key")]
public string Keyword { get; protected set; }
protected SongSearchRequest()
{
RemotePlace = "txt.yqq.song";
Page = 1;
Limit = 5;
ResultFormat = "json";
InCharset = "utf8";
OutCharset = "utf8";
Platform = "yqq";
Format = "json";
InCharset = OutCharset = "utf-8";
Platform = "yqq.json";
}
public SongSearchRequest(string musicName, string artistName) : this()

View File

@@ -17,11 +17,11 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic.JsonModel
public class QQMusicInnerSongModel
{
[JsonProperty("list")] public List<QQMusicInnerSongItem> SongItems { get; set; }
[JsonProperty("itemlist")] public List<QQMusicInnerSongItem> SongItems { get; set; }
}
public class QQMusicInnerSongItem
{
[JsonProperty("songmid")] public string SongId { get; set; }
[JsonProperty("mid")] public string SongId { get; set; }
}
}

View File

@@ -17,7 +17,8 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.QQMusic
private readonly IWarpHttpClient _warpHttpClient;
private readonly ILyricItemCollectionFactory _lyricItemCollectionFactory;
private const string QQSearchMusicUrl = @"https://c.y.qq.com/soso/fcgi-bin/client_search_cp";
// private const string QQSearchMusicUrl = @"https://c.y.qq.com/soso/fcgi-bin/client_search_cp";
private const string QQSearchMusicUrl = @"https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg";
private const string QQGetLyricUrl = @"https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg";
private const string QQMusicRequestReferer = @"https://y.qq.com/";

View File

@@ -43,7 +43,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
try
{
var info = await provider.LoadAsync(filePath);
if (info != null)
if (info != null && !string.IsNullOrEmpty(info.Name))
{
HandleBlockWord(info);
return info;

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using Microsoft.Extensions.Configuration;
@@ -23,6 +24,8 @@ namespace ZonyLrcTools.Cli
{
public static async Task<int> Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
ConfigureLogger();
ConfigureErrorMessage();
@@ -94,7 +97,8 @@ namespace ZonyLrcTools.Cli
switch (ex)
{
case ErrorCodeException exception:
Log.Logger.Error($"出现了未处理的异常。\n错误代码: {exception.ErrorCode}\n错误信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}\n原始信息:{exception.Message}\n调用栈:{exception.StackTrace}");
Log.Logger.Error(
$"出现了未处理的异常。\n错误代码: {exception.ErrorCode}\n错误信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}\n原始信息:{exception.Message}\n调用栈:{exception.StackTrace}");
Environment.Exit(exception.ErrorCode);
return exception.ErrorCode;
case { } unknownException:

View File

@@ -3,7 +3,8 @@
"10001": "待搜索的后缀不能为空。",
"10002": "需要扫描的目录不存在,请确认路径是否正确。",
"10003": "不能获取文件的后缀信息。",
"10004": "没有扫描到任何音乐文件。"
"10004": "没有扫描到任何音乐文件。",
"10005": "指定的编码不受支持,请检查配置,所有受支持的编码名称,请参考: https://docs.microsoft.com/en-us/dotnet/api/system.text.encodinginfo.codepage?view=net-6.0#system-text-encodinginfo-codepage。"
},
"Warning": {
"50001": "扫描文件时出现了错误。",

View File

@@ -19,6 +19,7 @@
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="TagLibSharp" Version="2.2.0" />
</ItemGroup>

View File

@@ -44,4 +44,5 @@ globalOption:
isOneLine: true # 双语歌词是否合并为一行展示。
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
isEnableTranslation: true # 是否启用翻译歌词。
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
fileEncoding: 'utf-8' # 歌词文件的编码格式。

View File

@@ -1,43 +0,0 @@
{
"name": "app-ui",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^2.6.14"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

View File

@@ -1,28 +0,0 @@
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

View File

@@ -1,58 +0,0 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

3
src/ui/.browserslistrc Normal file
View File

@@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

23
src/ui/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -1,4 +1,4 @@
# app-ui
# ui
## Project setup
```
@@ -15,10 +15,5 @@ yarn serve
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

23
src/ui/package.json Normal file
View File

@@ -0,0 +1,23 @@
{
"name": "ui",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"buefy": "^0.9.20",
"core-js": "^3.8.3",
"vue": "^2.6.14",
"vue-router": "^3.5.1",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-plugin-vuex": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"vue-template-compiler": "^2.6.14"
}
}

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

5
src/ui/src/App.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<div id="app">
<router-view/>
</div>
</template>

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,8 +1,12 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router,
store,
render: h => h(App)
}).$mount('#app')

View File

@@ -0,0 +1,15 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
const routes = [{
path: '/', name: 'home', component: HomeView
}]
const router = new VueRouter({
routes
})
export default router

17
src/ui/src/store/index.js Normal file
View File

@@ -0,0 +1,17 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})

View File

@@ -0,0 +1,12 @@
<template>
<div class="home">
</div>
</template>
<script>
// @ is an alias to /src
export default {
name: 'HomeView'
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Shouldly;
using Xunit;

View File

@@ -69,5 +69,13 @@ namespace ZonyLrcTools.Tests.Infrastructure.Lyric
lyric.ShouldNotBeNull();
}
[Fact]
public async Task UnknownIssue_Test()
{
var lyric = await _lyricDownloader.DownloadAsync("主題歌Arrietty's Song", "Cécile Corbel");
lyric.ShouldNotBeNull();
}
}
}