17 Commits

Author SHA1 Message Date
real-zony
559efff928 style: remove ui project from solution file. 2022-04-26 10:28:52 +08:00
real-zony
8370172aef Ignore unit test. 2022-04-26 10:26:38 +08:00
real-zony
cb8572e599 fix: Fixed the number of error hints. 2022-04-26 10:25:53 +08:00
real-zony
3b0f9fa89b fix: Fixed an issue with netease music downloader. 2022-04-25 23:24:13 +08:00
real-zony
c655ac4cbb docs: Update readme doc. 2022-04-25 13:41:40 +08:00
Zony
893b1e7918 Create dotnet.yml 2022-04-25 13:35:55 +08:00
real-zony
e900a92f37 enhanced: change the console theme color. 2022-04-25 12:26:06 +08:00
real-zony
0f84621919 fix: Fixed multi-downloader not working problem. 2022-04-25 12:16:00 +08:00
real-zony
61ca863770 feat: Support skip exist lyric files. 2022-04-25 12:03:59 +08:00
real-zony
2044a0b8fa add web ui initialize code. 2022-04-23 21:11:18 +08:00
real-zony
ffd76f5f2b enhanced: Optimized tag reader workflow.
Closes #93
2022-04-12 10:15:23 +08:00
real-zony
3705c6c8b5 perf: Optimized error log information. 2022-04-12 09:57:49 +08:00
real-zony
f8531e82ff Merge remote-tracking branch 'origin/dev' into dev 2022-04-07 16:50:05 +08:00
real-zony
969d5e9e19 Fixed tag info provider bug. 2022-04-07 16:49:49 +08:00
Zony
88ed247430 Update README.md 2022-03-24 11:49:46 +08:00
real-zony
be68b3474c fix: Fixed the error in the count of failure times. 2022-03-22 22:11:02 +08:00
real-zony
363d104dbe refactor: Configuration class unified storage. 2022-03-20 19:27:26 +08:00
29 changed files with 6300 additions and 53 deletions

25
.github/workflows/dotnet.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: .NET
on:
push:
branches: [ dev ]
pull_request:
branches: [ dev ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
# - name: Test
# run: dotnet test --no-build --verbosity normal

View File

@@ -1,5 +1,10 @@
简体中文 | [English](./docs/en_US.md)
## 免责声明
- 本工具仅作个人学习研究使用,可运行的二进制文件仅用于演示功能,不得将源码及其产物用于商业用途,否则由此造成的相关法律问题,[本人](https://github.com/real-zony) 不承担任何法律责任。
- 任何单位或个人因下载使用软件所产生的任何意外、疏忽、合约毁坏、诽谤、版权或知识产权侵犯及其造成的损失 (包括但不限于直接、间接、附带或衍生的损失等)[本人](https://github.com/real-zony) 不承担任何法律责任。
- 用户明确并同意本声明条款列举的全部内容,对使用本工具可能存在的风险和相关后果将完全由用户自行承担,[本人](https://github.com/real-zony) 不承担任何法律责任。
## 简介
ZonyLrcToolX 4 是一个基于 CEF 的跨平台歌词下载工具。
@@ -7,6 +12,10 @@ ZonyLrcToolX 4 是一个基于 CEF 的跨平台歌词下载工具。
🚧 当前版本正在开发当中。
🚧 如果你想查看可以工作的代码,请切换到 dev 分支。
## 下载
工具会执行每日构建动作,请访问 **[Release](https://github.com/real-zony/ZonyLrcToolsX/releases)** 页面进行下载。
## 用法
Windows 用户请在软件目录当中,按住 Shift + 右键呼出菜单,然后选择 PowerShell/命令提示符/Windows 终端,根据下述说明执行命令即可。
@@ -85,7 +94,7 @@ globalOption:
# 支持的歌词下载器。
plugin:
- name: NetEase # 基于网易云音乐的歌词下载器。
priority: 1 # 优先级,升序排列。
priority: 1 # 优先级,升序排列,改为 -1 时禁用
- name: QQ # 基于 QQ 音乐的歌词下载器。
priority: 2
- name: KuGou # 基于酷狗音乐的歌词下载器。
@@ -93,8 +102,9 @@ globalOption:
# 歌词下载的一些共有配置参数。
config:
isOneLine: true # 双语歌词是否合并为一行展示。
lineBreak: '\n' # 换行符的类型。
isEnableTranslation: false # 是否启用翻译歌词。
lineBreak: "\n" # 换行符的类型,记得使用双引号指定
isEnableTranslation: true # 是否启用翻译歌词。
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。
```
### 屏蔽字典

View File

@@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C29FB05C-54B
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AF8ADB2F-E46C-4DEE-8316-652D9FE1A69B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ui", "ui", "{D6E0DAF5-8171-44C0-817E-2FF9CF574E4F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Cli", "src\ZonyLrcTools.Cli\ZonyLrcTools.Cli.csproj", "{55D74323-ABFA-4A73-A3BF-F3E8D5DE6DE8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZonyLrcTools.Tests", "tests\ZonyLrcTools.Tests\ZonyLrcTools.Tests.csproj", "{FFBD3200-568F-455B-8390-5E76C51D522C}"

View File

@@ -92,11 +92,34 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
throw new ErrorCodeException(ErrorCodes.NoFilesWereScanned);
}
files = RemoveExistLyricFiles(files);
_logger.LogInformation($"已经扫描到了 {files.Count} 个音乐文件。");
return files;
}
private List<string> RemoveExistLyricFiles(List<string> filePaths)
{
if (!_options.Provider.Lyric.Config.IsSkipExistLyricFiles)
{
return filePaths;
}
return filePaths
.Where(path =>
{
if (!File.Exists(Path.ChangeExtension(path, ".lrc")))
{
return true;
}
_logger.LogWarning($"已经存在歌词文件 {path},跳过。");
return false;
})
.ToList();
}
private async Task<ImmutableList<MusicInfo>> LoadMusicInfoAsync(IReadOnlyCollection<string> files)
{
_logger.LogInformation("开始加载音乐文件的标签信息...");
@@ -125,7 +148,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
return downloader;
}
#region > <
#region > Lyric download logic <
private async ValueTask DownloadLyricFilesAsync(ImmutableList<MusicInfo> musicInfos)
{
@@ -138,30 +161,30 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
await Task.WhenAll(warpTaskList);
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count} 失败{0}。");
_logger.LogInformation($"歌词数据下载完成,成功: {musicInfos.Count} 失败{musicInfos.Count - musicInfos.Count(m => m.IsSuccessful)}。");
}
private async Task DownloadLyricTaskLogicAsync(IEnumerable<ILyricDownloader> downloaderList, MusicInfo info)
{
async Task<bool> InternalDownloadLogicAsync(ILyricDownloader downloader)
async Task InternalDownloadLogicAsync(ILyricDownloader downloader)
{
_logger.LogMusicInfoWithInformation(info);
try
{
var lyric = await downloader.DownloadAsync(info.Name, info.Artist);
var filePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!, $"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc");
if (File.Exists(filePath))
var lyricFilePath = Path.Combine(Path.GetDirectoryName(info.FilePath)!,
$"{Path.GetFileNameWithoutExtension(info.FilePath)}.lrc");
if (File.Exists(lyricFilePath))
{
return true;
File.Delete(lyricFilePath);
}
if (lyric.IsPruneMusic)
{
return true;
info.IsSuccessful = true;
}
await using var stream = new FileStream(filePath, FileMode.Create);
await using var stream = new FileStream(lyricFilePath, FileMode.Create);
await using var sw = new StreamWriter(stream);
await sw.WriteAsync(lyric.ToString());
await sw.FlushAsync();
@@ -170,7 +193,7 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
{
if (ex.ErrorCode == ErrorCodes.NoMatchingSong)
{
return false;
info.IsSuccessful = false;
}
_logger.LogWarningInfo(ex);
@@ -178,23 +201,29 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
catch (Exception ex)
{
_logger.LogError($"下载歌词文件时发生错误:{ex.Message},歌曲名: {info.Name},歌手: {info.Artist}。");
info.IsSuccessful = false;
}
finally
{
info.IsSuccessful = true;
}
return true;
}
foreach (var downloader in downloaderList)
{
if (await InternalDownloadLogicAsync(downloader))
await InternalDownloadLogicAsync(downloader);
if (info.IsSuccessful)
{
break;
_logger.LogSuccessful(info);
return;
}
}
}
#endregion
#region > <
#region > Ablum image download logic <
private async ValueTask DownloadAlbumAsync(ImmutableList<MusicInfo> musicInfos)
{
@@ -207,12 +236,12 @@ namespace ZonyLrcTools.Cli.Commands.SubCommand
await Task.WhenAll(warpTaskList);
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count} 失败{0}。");
_logger.LogInformation($"专辑数据下载完成,成功: {musicInfos.Count(m => m.IsSuccessful)} 失败{musicInfos.Count(m => !m.IsSuccessful)}。");
}
private async Task DownloadAlbumTaskLogicAsync(IAlbumDownloader downloader, MusicInfo info)
{
_logger.LogMusicInfoWithInformation(info);
_logger.LogSuccessful(info);
try
{

View File

@@ -1,4 +1,4 @@
namespace ZonyLrcTools.Cli.Infrastructure.Tag
namespace ZonyLrcTools.Cli.Config
{
/// <summary>
/// 屏蔽词选项类。

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using ZonyLrcTools.Cli.Infrastructure.Lyric;
namespace ZonyLrcTools.Cli.Infrastructure.Lyric;
namespace ZonyLrcTools.Cli.Config;
public class LyricOption
{
@@ -24,5 +25,10 @@ public class LyricConfigOption
/// <summary>
/// 是否启用歌词翻译功能。
/// </summary>
public bool IsEnableTranslation { get; set; } = false;
public bool IsEnableTranslation { get; set; } = true;
/// <summary>
/// 如果歌词文件已经存在,是否跳过这些文件
/// </summary>
public bool IsSkipExistLyricFiles { get; set; } = false;
}

View File

@@ -1,4 +1,4 @@
namespace ZonyLrcTools.Cli.Infrastructure.Lyric
namespace ZonyLrcTools.Cli.Config
{
public class LyricProviderOption
{

View File

@@ -41,13 +41,13 @@ namespace ZonyLrcTools.Cli.Infrastructure.Extensions
}
/// <summary>
/// 使用 <see cref="LogLevel.Information"/> 级别打印歌曲信息。
/// 使用 <see cref="LogLevel.Information"/> 级别打印歌曲下载成功信息。
/// </summary>
/// <param name="logger">日志记录器的实例。</param>
/// <param name="musicInfo">需要打印的歌曲信息。</param>
public static void LogMusicInfoWithInformation(this ILogger logger, MusicInfo musicInfo)
public static void LogSuccessful(this ILogger logger, MusicInfo musicInfo)
{
logger.LogInformation($"歌曲名: {musicInfo.Name}, 艺术家: {musicInfo.Artist}, 歌曲路径: {musicInfo.FilePath}");
logger.LogInformation($"歌曲名: {musicInfo.Name}, 艺术家: {musicInfo.Artist}, 下载成功.");
}
}
}

View File

@@ -0,0 +1,28 @@
using System.Collections.Generic;
using Serilog.Sinks.SystemConsole.Themes;
namespace ZonyLrcTools.Cli.Infrastructure.Logging;
public static class CustomConsoleTheme
{
public static AnsiConsoleTheme Code { get; } = new AnsiConsoleTheme(
new Dictionary<ConsoleThemeStyle, string>
{
[ConsoleThemeStyle.Text] = "\x1b[38;5;0253m",
[ConsoleThemeStyle.SecondaryText] = "\x1b[38;5;0246m",
[ConsoleThemeStyle.TertiaryText] = "\x1b[38;5;0242m",
[ConsoleThemeStyle.Invalid] = "\x1b[33;1m",
[ConsoleThemeStyle.Null] = "\x1b[38;5;0038m",
[ConsoleThemeStyle.Name] = "\x1b[38;5;0081m",
[ConsoleThemeStyle.String] = "\x1b[38;5;0216m",
[ConsoleThemeStyle.Number] = "\x1b[38;5;151m",
[ConsoleThemeStyle.Boolean] = "\x1b[38;5;0038m",
[ConsoleThemeStyle.Scalar] = "\x1b[38;5;0079m",
[ConsoleThemeStyle.LevelVerbose] = "\x1b[37m",
[ConsoleThemeStyle.LevelDebug] = "\x1b[37m",
[ConsoleThemeStyle.LevelInformation] = "\x1b[32m\x1b[48;5;0238m",
[ConsoleThemeStyle.LevelWarning] = "\x1b[38;5;0229m",
[ConsoleThemeStyle.LevelError] = "\x1b[38;5;0197m\x1b[48;5;0238m",
[ConsoleThemeStyle.LevelFatal] = "\x1b[38;5;0197m\x1b[48;5;0238m",
});
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ZonyLrcTools.Cli.Config;
using ZonyLrcTools.Cli.Infrastructure.Extensions;
namespace ZonyLrcTools.Cli.Infrastructure.Lyric

View File

@@ -59,7 +59,7 @@ namespace ZonyLrcTools.Cli.Infrastructure.Lyric.NetEase
await ValueTask.CompletedTask;
var json = JsonConvert.DeserializeObject<GetLyricResponse>(Encoding.UTF8.GetString(data));
if (json?.OriginalLyric == null)
if (json?.OriginalLyric == null || string.IsNullOrEmpty(json.OriginalLyric.Text))
{
return new LyricItemCollection(null);
}

View File

@@ -20,6 +20,11 @@ namespace ZonyLrcTools.Cli.Infrastructure
/// </summary>
public string Artist { get; set; }
/// <summary>
/// 是否下载成功?
/// </summary>
public bool IsSuccessful { get; set; } = true;
/// <summary>
/// 构建一个新的 <see cref="MusicInfo"/> 对象。
/// </summary>

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -16,10 +17,12 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
{
protected readonly IEnumerable<ITagInfoProvider> TagInfoProviders;
protected readonly IBlockWordDictionary BlockWordDictionary;
protected readonly ILogger<DefaultTagLoader> _logger;
protected readonly ILogger<DefaultTagLoader> Logger;
protected ToolOptions Options;
private readonly IEnumerable<ITagInfoProvider> _sortedTagInfoProviders;
public DefaultTagLoader(IEnumerable<ITagInfoProvider> tagInfoProviders,
IBlockWordDictionary blockWordDictionary,
IOptions<ToolOptions> options,
@@ -27,18 +30,17 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
{
TagInfoProviders = tagInfoProviders;
BlockWordDictionary = blockWordDictionary;
_logger = logger;
Logger = logger;
Options = options.Value;
_sortedTagInfoProviders = GetTagInfoProviders();
}
public virtual async ValueTask<MusicInfo> LoadTagAsync(string filePath)
{
if (!TagInfoProviders.Any())
foreach (var provider in _sortedTagInfoProviders)
{
throw new ErrorCodeException(ErrorCodes.LoadTagInfoProviderError);
}
foreach (var provider in TagInfoProviders)
try
{
var info = await provider.LoadAsync(filePath);
if (info != null)
@@ -47,12 +49,29 @@ namespace ZonyLrcTools.Cli.Infrastructure.Tag
return info;
}
}
catch (Exception)
{
// ignored
}
}
_logger.LogWarning($"{filePath} 没有找到正确的标签信息,请考虑调整正则表达式。");
Logger.LogWarning($"{filePath} 没有找到正确的标签信息,请考虑调整正则表达式。");
return null;
}
private IEnumerable<ITagInfoProvider> GetTagInfoProviders()
{
if (!TagInfoProviders.Any())
{
throw new ErrorCodeException(ErrorCodes.LoadTagInfoProviderError);
}
return Options.Provider.Tag.Plugin
.Where(x => x.Priority != -1)
.OrderBy(x => x.Priority)
.Join(TagInfoProviders, x => x.Name, y => y.Name, (x, y) => y);
}
protected void HandleBlockWord(MusicInfo info)
{
if (Options.Provider.Tag.BlockWord.IsEnable)

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using YamlDotNet.Serialization;
using ZonyLrcTools.Cli.Config;
namespace ZonyLrcTools.Cli.Infrastructure.Tag
{

View File

@@ -7,10 +7,12 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;
using ZonyLrcTools.Cli.Commands;
using ZonyLrcTools.Cli.Commands.SubCommand;
using ZonyLrcTools.Cli.Infrastructure.DependencyInject;
using ZonyLrcTools.Cli.Infrastructure.Exceptions;
using ZonyLrcTools.Cli.Infrastructure.Logging;
namespace ZonyLrcTools.Cli
{
@@ -53,7 +55,7 @@ namespace ZonyLrcTools.Cli
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("System.Net.Http.HttpClient", LogEventLevel.Error)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.Console())
.WriteTo.Async(c => c.Console(theme: CustomConsoleTheme.Code))
.WriteTo.Logger(lc =>
{
lc.Filter.ByIncludingOnly(lc => lc.Level == LogEventLevel.Warning)
@@ -92,7 +94,7 @@ namespace ZonyLrcTools.Cli
switch (ex)
{
case ErrorCodeException exception:
Log.Logger.Error($"出现了未处理的异常错误代码: {exception.ErrorCode}错误信息: {ErrorCodeHelper.GetMessage(exception.ErrorCode)}\n调用栈:\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

@@ -34,7 +34,7 @@ globalOption:
# 支持的歌词下载器。
plugin:
- name: NetEase # 基于网易云音乐的歌词下载器。
priority: 1 # 优先级,升序排列。
priority: 1 # 优先级,升序排列,改为 -1 时禁用
- name: QQ # 基于 QQ 音乐的歌词下载器。
priority: 2
- name: KuGou # 基于酷狗音乐的歌词下载器。
@@ -44,3 +44,4 @@ globalOption:
isOneLine: true # 双语歌词是否合并为一行展示。
lineBreak: "\n" # 换行符的类型,记得使用双引号指定。
isEnableTranslation: true # 是否启用翻译歌词。
isSkipExistLyricFiles: false # 如果歌词文件已经存在,是否跳过这些文件。

View File

@@ -0,0 +1,24 @@
# app-ui
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

View File

@@ -0,0 +1,43 @@
{
"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"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -0,0 +1,28 @@
<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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,58 @@
<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>

View File

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

View File

@@ -0,0 +1,4 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
using Shouldly;
using Xunit;
using ZonyLrcTools.Cli.Config;
using ZonyLrcTools.Cli.Infrastructure.Lyric;
namespace ZonyLrcTools.Tests.Infrastructure.Lyric