Compare commits
451 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
589540be29 | ||
|
|
2bcae85419 | ||
|
|
6e68927eec | ||
|
|
a4eea18fa5 | ||
|
|
fe5b1d5de8 | ||
|
|
c8e6db11c9 | ||
|
|
5bef0e44a0 | ||
|
|
d56a25eb3c | ||
|
|
a449b74ef2 | ||
|
|
ad7b504eef | ||
|
|
6048e243c7 | ||
|
|
0c74291a34 | ||
|
|
7fa0fa5221 | ||
|
|
95af222da7 | ||
|
|
9eefe62fba | ||
|
|
b621995e24 | ||
|
|
91f97ff76b | ||
|
|
cce2b96d29 | ||
|
|
a0935c74fe | ||
|
|
df5ecb6eb5 | ||
|
|
ca51020602 | ||
|
|
258828ffbd | ||
|
|
91b1ff7df9 | ||
|
|
8cc617a5f6 | ||
|
|
170ac45115 | ||
|
|
2dd45351e5 | ||
|
|
f5f0dbb222 | ||
|
|
7fca6db2a3 | ||
|
|
655473699a | ||
|
|
4d371df510 | ||
|
|
a21521cc6f | ||
|
|
01a3a7a501 | ||
|
|
e47c84e5eb | ||
|
|
54cbb84e6e | ||
|
|
f68f49973a | ||
|
|
e9fe9000f6 | ||
|
|
6d4e6ef214 | ||
|
|
2379b2c9cc | ||
|
|
8c6b69e762 | ||
|
|
ae1a7c963f | ||
|
|
2476fbd6e3 | ||
|
|
f7951ec22f | ||
|
|
33a1057de9 | ||
|
|
2e96161bd0 | ||
|
|
56b3ecfd25 | ||
|
|
54d66d05f4 | ||
|
|
b32408b44e | ||
|
|
3c792ce3cc | ||
|
|
5084da333f | ||
|
|
a8010c8ca7 | ||
|
|
e1ddffc8ae | ||
|
|
69b1e541c6 | ||
|
|
35b84f3e6a | ||
|
|
28b9fd5475 | ||
|
|
dc70fde9e4 | ||
|
|
278db37a88 | ||
|
|
2803d40dd1 | ||
|
|
54f82d384e | ||
|
|
7d1ffa603c | ||
|
|
49f7728eac | ||
|
|
890c0c86c1 | ||
|
|
15f4ea4708 | ||
|
|
dbb3fbcc09 | ||
|
|
31640bb663 | ||
|
|
10f4473c9d | ||
|
|
3297eb5ccb | ||
|
|
82a69d0b00 | ||
|
|
3d66a890c2 | ||
|
|
b3de2ae785 | ||
|
|
31ea3b7e0a | ||
|
|
b8580efb17 | ||
|
|
9cc064c01b | ||
|
|
80450349c0 | ||
|
|
9f125f88bd | ||
|
|
618c345a78 | ||
|
|
44f9709bb3 | ||
|
|
3c1a144113 | ||
|
|
8ed13d4a85 | ||
|
|
3d71a293a1 | ||
|
|
cb58abbbfd | ||
|
|
e2527c3fb8 | ||
|
|
3ca7e9a271 | ||
|
|
2f07550316 | ||
|
|
eff9328a23 | ||
|
|
5f63ab6b4a | ||
|
|
c2e08db2e4 | ||
|
|
903389e4bf | ||
|
|
327384ace5 | ||
|
|
6ffe4daed0 | ||
|
|
2b8c9bf22a | ||
|
|
c7d586407e | ||
|
|
c5af89e51f | ||
|
|
2d8770b074 | ||
|
|
4abb6a5a9f | ||
|
|
b1d515465a | ||
|
|
ea7dca7975 | ||
|
|
c98fa20a74 | ||
|
|
16d6ff39c8 | ||
|
|
159dd03a2c | ||
|
|
167c8ad493 | ||
|
|
c82ffd0c7d | ||
|
|
0128662ed2 | ||
|
|
30695149d6 | ||
|
|
bbc1bb7436 | ||
|
|
57424f9e15 | ||
|
|
32b93680b9 | ||
|
|
0a22c7b5d7 | ||
|
|
64f5fcaee4 | ||
|
|
304c24a673 | ||
|
|
a56bca98b2 | ||
|
|
1865bd95bc | ||
|
|
fd37015466 | ||
|
|
7df1c25168 | ||
|
|
ed9cf9c4c5 | ||
|
|
35b9cbfdbd | ||
|
|
df6da2eb9e | ||
|
|
2d966036bb | ||
|
|
499857a679 | ||
|
|
7624a1a71e | ||
|
|
05b85c4b7b | ||
|
|
27d5bd8f81 | ||
|
|
c5da42b67d | ||
|
|
5e484334de | ||
|
|
25b90fafdc | ||
|
|
a676136f48 | ||
|
|
76e55d4e6b | ||
|
|
b7de5fc173 | ||
|
|
7bc8405df0 | ||
|
|
a7f2045c7b | ||
|
|
b9b52f4d9f | ||
|
|
09f8837fe4 | ||
|
|
d7fea7f888 | ||
|
|
bb7d1e332f | ||
|
|
2dc907a20f | ||
|
|
ad3e52f6e1 | ||
|
|
b593ca3011 | ||
|
|
988418e8d1 | ||
|
|
1922311238 | ||
|
|
3b1488f147 | ||
|
|
5f4b53c167 | ||
|
|
e17941dfb0 | ||
|
|
9aff694116 | ||
|
|
c2983ba079 | ||
|
|
541ff2b76c | ||
|
|
55b50d764b | ||
|
|
30ff7b2930 | ||
|
|
a24f901d1d | ||
|
|
7f7d41f883 | ||
|
|
7b27cf5bc6 | ||
|
|
ad8f7af3a9 | ||
|
|
2599766e3e | ||
|
|
0f55795ca9 | ||
|
|
8d6d0527db | ||
|
|
374a7a837d | ||
|
|
e0d13057c3 | ||
|
|
23b2340169 | ||
|
|
7e826311fe | ||
|
|
cfe197c805 | ||
|
|
230132904e | ||
|
|
fb44ae45cc | ||
|
|
9ce872eebe | ||
|
|
ee6e9d43fd | ||
|
|
1a440fad09 | ||
|
|
477f8bb99b | ||
|
|
56c3ca1cce | ||
|
|
a5f694ea72 | ||
|
|
280fec1990 | ||
|
|
c5e50c9fd5 | ||
|
|
01ccad4df7 | ||
|
|
dfb8f55fba | ||
|
|
2924ad6c18 | ||
|
|
9f5bac29a0 | ||
|
|
2fe1f0c04c | ||
|
|
2a12f57cb2 | ||
|
|
4c10533a3d | ||
|
|
cda440b01a | ||
|
|
7b9e23743b | ||
|
|
e43270f35d | ||
|
|
9431faf932 | ||
|
|
be03b5f8fc | ||
|
|
8a414d0c25 | ||
|
|
f9fd9afcdd | ||
|
|
b114cf4a33 | ||
|
|
fa39d4ca55 | ||
|
|
650e4ff786 | ||
|
|
e355341596 | ||
|
|
4fa5ed0ca6 | ||
|
|
df9a1370c3 | ||
|
|
6a8813531f | ||
|
|
e5e45148c3 | ||
|
|
4a66796747 | ||
|
|
7f8ab8be7c | ||
|
|
ce276df55c | ||
|
|
ccc59ea893 | ||
|
|
0b409f38d6 | ||
|
|
f9878ed88a | ||
|
|
e43e85480d | ||
|
|
b97170d1b2 | ||
|
|
b9aa1d574a | ||
|
|
dd7b06d7e5 | ||
|
|
ddafcfba10 | ||
|
|
da5b8c408a | ||
|
|
fb35d42fc4 | ||
|
|
dfd5d4c8b7 | ||
|
|
e5309cedee | ||
|
|
d335f57a1a | ||
|
|
c703d9c197 | ||
|
|
87a0ceb5b0 | ||
|
|
891d70f3ed | ||
|
|
37b5908ddc | ||
|
|
ead017e4b1 | ||
|
|
da2a32e420 | ||
|
|
5fc6edba20 | ||
|
|
1ef47b8f1d | ||
|
|
cdaab19afa | ||
|
|
6a0b03cfe1 | ||
|
|
d449930a02 | ||
|
|
820509dbea | ||
|
|
1493ab7317 | ||
|
|
c6ca63ee11 | ||
|
|
4fa1295b84 | ||
|
|
174428b386 | ||
|
|
599b0251af | ||
|
|
25c2180247 | ||
|
|
a6ff0e7f5c | ||
|
|
2e06711600 | ||
|
|
80770d6c75 | ||
|
|
1e068df2ad | ||
|
|
4172ff9fc6 | ||
|
|
83a7df9fe8 | ||
|
|
ba95dc11fe | ||
|
|
93829acdab | ||
|
|
1f0f35dd51 | ||
|
|
be94d6ff8e | ||
|
|
1bdb8fcb4a | ||
|
|
914e043502 | ||
|
|
dfa175b8b2 | ||
|
|
a94e0efba5 | ||
|
|
fb0831f2eb | ||
|
|
573023600a | ||
|
|
041aad31b4 | ||
|
|
f652932d71 | ||
|
|
7efeb9492b | ||
|
|
055536eb5c | ||
|
|
14852fc8d3 | ||
|
|
9866e772df | ||
|
|
87ca0836b1 | ||
|
|
fa07c5a40c | ||
|
|
5dbfea240b | ||
|
|
c1344393c3 | ||
|
|
426888f77c | ||
|
|
45cbc15c0f | ||
|
|
072025a543 | ||
|
|
c6427aa3e1 | ||
|
|
632cdb1239 | ||
|
|
8ffe472605 | ||
|
|
8e86d378d0 | ||
|
|
744fd53fb1 | ||
|
|
3c64473dbb | ||
|
|
e70fed37da | ||
|
|
b749854c5e | ||
|
|
d9210cc50a | ||
|
|
f186d34885 | ||
|
|
ba992b7c33 | ||
|
|
24d7c839c7 | ||
|
|
a4f3df80c9 | ||
|
|
866fec6ee3 | ||
|
|
8f7d6fbb8d | ||
|
|
62e26cae7d | ||
|
|
ddb814da10 | ||
|
|
e266ea8ef8 | ||
|
|
a894954641 | ||
|
|
f640ab9969 | ||
|
|
9eb17fd978 | ||
|
|
020aca7384 | ||
|
|
fcc47dc0ff | ||
|
|
17ce268da6 | ||
|
|
43c64b1b43 | ||
|
|
11ced6b418 | ||
|
|
3d3992154a | ||
|
|
81e7b67c7f | ||
|
|
d7e94a342b | ||
|
|
46f8067577 | ||
|
|
1dc7d0ceca | ||
|
|
ba64631a17 | ||
|
|
cdb9524f04 | ||
|
|
5213aa13c5 | ||
|
|
d870d0198f | ||
|
|
976a9afd2f | ||
|
|
018218a5bf | ||
|
|
38a9d6ed31 | ||
|
|
8dab799939 | ||
|
|
1ddbe6f24e | ||
|
|
4d5bcba6c7 | ||
|
|
f833306b60 | ||
|
|
4d92ed9963 | ||
|
|
a22285156a | ||
|
|
d1029f16d6 | ||
|
|
4908555635 | ||
|
|
750cf7a484 | ||
|
|
a334743f6f | ||
|
|
14747cac10 | ||
|
|
cc239aeaba | ||
|
|
eeda296589 | ||
|
|
edb7ea201c | ||
|
|
17d20fa299 | ||
|
|
f8d421c9b1 | ||
|
|
dfdf02a17f | ||
|
|
abdb2bcd50 | ||
|
|
f728191a8f | ||
|
|
dfa8b51a53 | ||
|
|
b2c13121fd | ||
|
|
d28adb61a4 | ||
|
|
9a7d5a3834 | ||
|
|
2037798fbe | ||
|
|
85bd0ad015 | ||
|
|
e1557a51a3 | ||
|
|
1ecc6f136f | ||
|
|
53b3061b03 | ||
|
|
3d2f6a2330 | ||
|
|
3b1470f28f | ||
|
|
100268448a | ||
|
|
51f67bb2c2 | ||
|
|
7be126cf5f | ||
|
|
f2f5d3ac15 | ||
|
|
34c45e0105 | ||
|
|
f9333f5f78 | ||
|
|
7365daf700 | ||
|
|
cebf313075 | ||
|
|
bb99049991 | ||
|
|
df74dafbc5 | ||
|
|
721d2a9704 | ||
|
|
1e60fa9a95 | ||
|
|
f24e8232f8 | ||
|
|
a1b1d861ac | ||
|
|
f24263b416 | ||
|
|
17795e5da2 | ||
|
|
f1030d3a78 | ||
|
|
b979ce250f | ||
|
|
d0d8966875 | ||
|
|
d39ba65263 | ||
|
|
62d400827e | ||
|
|
75b99c46b5 | ||
|
|
e7ae79144c | ||
|
|
04d6cbe7f3 | ||
|
|
bea1e5751f | ||
|
|
f2ebb04fab | ||
|
|
42048764d5 | ||
|
|
e326253fd8 | ||
|
|
edf5c77ea0 | ||
|
|
8870390770 | ||
|
|
c9514e6e19 | ||
|
|
08fa160de4 | ||
|
|
5d4c4922fd | ||
|
|
c5e7c87658 | ||
|
|
f6923b4c47 | ||
|
|
4cf7598a7d | ||
|
|
81b09bef0d | ||
|
|
b21df3de25 | ||
|
|
c49d814182 | ||
|
|
1cb3c72ab7 | ||
|
|
f03372de6a | ||
|
|
d925f40303 | ||
|
|
dc12d895d8 | ||
|
|
0bb14902f2 | ||
|
|
3027a5f6ff | ||
|
|
f320f4760b | ||
|
|
e939933d6f | ||
|
|
06bffe7618 | ||
|
|
7abc087d70 | ||
|
|
eb2ea1981d | ||
|
|
6dc14ec51b | ||
|
|
36f8257a3e | ||
|
|
c55544df46 | ||
|
|
008f2183de | ||
|
|
dd3a3c3bbb | ||
|
|
941eb2e66e | ||
|
|
a98fcb43d6 | ||
|
|
791121ae06 | ||
|
|
0c156e2708 | ||
|
|
017b47fded | ||
|
|
e27ed22c16 | ||
|
|
904d8744ef | ||
|
|
800e0b7360 | ||
|
|
b6a5461a1d | ||
|
|
a4eda61a86 | ||
|
|
a79d0712a4 | ||
|
|
8f782cdc9d | ||
|
|
2f851f3172 | ||
|
|
9fcf455c08 | ||
|
|
9b14906a46 | ||
|
|
14ce428951 | ||
|
|
8c93124311 | ||
|
|
c09707867b | ||
|
|
a2af0f3904 | ||
|
|
73982f0e84 | ||
|
|
449a6fd335 | ||
|
|
32b39c7927 | ||
|
|
c6f1e0b233 | ||
|
|
7c1a3ae4bc | ||
|
|
6bd6622484 | ||
|
|
433aff385d | ||
|
|
c37ad07f93 | ||
|
|
e4c1f855fb | ||
|
|
6978656061 | ||
|
|
973d60c98f | ||
|
|
5a43ba2576 | ||
|
|
e52a02cf3c | ||
|
|
da8216e2ca | ||
|
|
bd0e2ec35c | ||
|
|
7c8598ffa5 | ||
|
|
50e594b91d | ||
|
|
a9e5bb33e4 | ||
|
|
5e8676a039 | ||
|
|
3522011224 | ||
|
|
820597e903 | ||
|
|
f8efbe8ec6 | ||
|
|
67d42a2291 | ||
|
|
7ab43d2e9e | ||
|
|
a59351adf7 | ||
|
|
ad5d5458f1 | ||
|
|
adb539fbde | ||
|
|
ecd7a56df0 | ||
|
|
2dbf5dbf03 | ||
|
|
492164d008 | ||
|
|
8da7fdabe5 | ||
|
|
a2c49d354e | ||
|
|
c7c1143cb4 | ||
|
|
f5d097e975 | ||
|
|
d04aeef40b | ||
|
|
a504b914fe | ||
|
|
62d414d659 | ||
|
|
6c57e77969 | ||
|
|
70139e3ca4 | ||
|
|
4dde40ac60 | ||
|
|
be83a79b05 | ||
|
|
a77afb57fd | ||
|
|
cd11db63eb | ||
|
|
f81127432e | ||
|
|
4466713d1a | ||
|
|
73c915d184 | ||
|
|
7e6788a057 | ||
|
|
19140cd680 | ||
|
|
a1780bc9d4 | ||
|
|
bb1b07e0b3 | ||
|
|
7cb1b5fc7c | ||
|
|
f70aa9e0a0 | ||
|
|
6c8229a21d | ||
|
|
9211dcd3bb | ||
|
|
043ad5906b | ||
|
|
cf598f1c9c |
92
.cursor/rules/music-vue-rule.mdc
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
description: 这个规则是项目描述
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
您是 TypeScript、Node.js、Vue3、Electron、naive-ui、VueUse 和 Tailwind 方面的专家。
|
||||
|
||||
项目结构
|
||||
- 这是 Electron 项目,使用 Vue3 和 Pinia 进行开发的第三方网易云音乐播放器。
|
||||
- 使用 Vue3 和 Pinia 进行开发。
|
||||
- 使用 Pinia 进行状态管理。
|
||||
- 使用 VueUse 进行状态管理。
|
||||
- 使用 naive-ui 进行 UI 设计。
|
||||
- 使用 Tailwind 进行样式设计。
|
||||
- 使用 remixicon 进行图标设计。
|
||||
- 使用 vite 进行项目构建。
|
||||
- 使用 electron-builder 进行项目打包。
|
||||
- 使用 electron-vite 进行项目开发。
|
||||
- 使用 netease-cloud-music-api 进行网易云音乐接口调用。
|
||||
- 使用 electron-store 进行本地数据存储。
|
||||
- 使用 axios 进行网络请求。
|
||||
- 使用 @unblockneteasemusic/server 进行网易云音乐解锁。
|
||||
- 使用 vue-i18n 进行国际化。目录为 src/i18n
|
||||
|
||||
代码风格和结构
|
||||
- 编写简洁、技术性的 TypeScript 代码,并提供准确示例。
|
||||
- 使用组合 API 和声明性编程模式;避免使用选项 API。
|
||||
- 优先使用迭代和模块化,而不是代码重复。
|
||||
- 使用带有助动词的描述性变量名称(例如 isLoading、hasError)。
|
||||
- 结构文件:导出的组件、可组合项、帮助程序、静态内容、类型。
|
||||
|
||||
命名约定
|
||||
- 使用带破折号的小写字母表示目录(例如 components/auth-wizard)。
|
||||
- 使用 PascalCase 表示组件名称(例如 AuthWizard.vue)。
|
||||
- 使用 camelCase 表示可组合项(例如 useAuthState.ts)。
|
||||
|
||||
TypeScript 用法
|
||||
- 对所有代码使用 TypeScript;优先使用类型而不是接口。
|
||||
- 避免使用枚举;改用 const 对象。
|
||||
- 将 Vue 3 与 TypeScript 结合使用,利用 defineComponent 和 PropType。
|
||||
|
||||
语法和格式
|
||||
- 对方法和计算属性使用箭头函数。
|
||||
- 避免在条件中使用不必要的花括号;对简单语句使用简洁的语法。
|
||||
- 使用模板语法进行声明式渲染。
|
||||
|
||||
UI 和样式
|
||||
- 使用 naive-ui 和 Tailwind 进行组件和样式设计。
|
||||
- 使用 Tailwind CSS 实现响应式设计;采用移动优先方法。
|
||||
|
||||
图标
|
||||
- 使用 remixicon 作为图标库。
|
||||
|
||||
性能优化
|
||||
- 对异步组件使用 Suspense。
|
||||
- 为路由和组件实现延迟加载。
|
||||
|
||||
关键约定
|
||||
- 对常见可组合项和实用函数使用 VueUse。
|
||||
- 使用 Pinia 进行状态管理。
|
||||
- 优化 Web Vitals(LCP、CLS、FID)。
|
||||
|
||||
|
||||
Vue 3 和 Composition API 最佳实践
|
||||
- 使用 <script setup lang="ts"> 语法进行简洁的组件定义。
|
||||
- 利用 ref、reactive 和 computed 进行反应状态管理。
|
||||
- 在适当的情况下使用 provide/inject 进行依赖注入。
|
||||
- 实现自定义可组合项以实现可重用逻辑。
|
||||
|
||||
|
||||
Electron 最佳实践
|
||||
- 使用 Electron 和 Vue.js 进行跨平台桌面应用程序开发。
|
||||
- 使用 Electron 的 API 和 Vue.js 的组合 API 进行开发。
|
||||
- 实现自定义可组合项以实现可重用逻辑。
|
||||
|
||||
|
||||
|
||||
组件导入
|
||||
- 使用 auto-import 进行组件导入。
|
||||
- naive-ui 组件自动导入 不需要手动导入。
|
||||
|
||||
关注官方 Electron 和 Vue.js 文档,了解有关数据获取、渲染和路由的最新最佳实践。
|
||||
|
||||
|
||||
|
||||
问题修复
|
||||
- 思考 5-7 种可能导致问题的来源,并根据可能性、对功能的影响以及在类似问题中的出现频率进行优先排序。仅考虑与错误日志、最近代码变更和系统约束相匹配的来源。忽略外部依赖,除非日志明确指向它们。
|
||||
- 一旦缩小到 1-2 个最可能的来源,将其与历史错误日志、相关系统状态和预期行为进行交叉验证。如果发现不一致,调整你的假设。
|
||||
- 在添加日志时,确保它们被策略性地放置,以便同时确认或排除多个潜在原因。如果日志不支持你的假设,请先提出替代的调试策略,再继续深入分析。
|
||||
- 在实施修复之前,先总结问题现象、经过验证的假设,以及预期的日志输出,以确认问题是否真正得到解决。
|
||||
|
||||
|
||||
148
.cursor/rules/project.mdc
Normal file
@@ -0,0 +1,148 @@
|
||||
---
|
||||
description: 这个规则是项目结构
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# AlgerMusicPlayer 项目结构
|
||||
|
||||
AlgerMusicPlayer 是一个基于 Electron、Vue 3、TypeScript 开发的网易云音乐第三方播放器。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **前端框架**:Vue 3 + TypeScript
|
||||
- **UI 组件库**:naive-ui
|
||||
- **样式框架**:Tailwind CSS
|
||||
- **图标库**:remixicon
|
||||
- **状态管理**:Pinia
|
||||
- **工具库**:VueUse
|
||||
- **构建工具**:Vite, electron-vite
|
||||
- **打包工具**:electron-builder
|
||||
- **国际化**:vue-i18n
|
||||
- **HTTP 客户端**:axios
|
||||
- **本地存储**:electron-store localstorage
|
||||
- **网易云音乐 API**:netease-cloud-music-api
|
||||
- **音乐解锁**:@unblockneteasemusic/server
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
AlgerMusicPlayer/
|
||||
├── build/ # 构建相关文件
|
||||
├── docs/ # 项目文档
|
||||
├── node_modules/ # 依赖包
|
||||
├── out/ # 构建输出目录
|
||||
├── resources/ # 资源文件
|
||||
├── src/ # 源代码
|
||||
│ ├── i18n/ # 国际化配置
|
||||
│ │ ├── lang/ # 语言包
|
||||
│ │ ├── main.ts # 主进程国际化入口
|
||||
│ │ └── renderer.ts # 渲染进程国际化入口
|
||||
│ ├── main/ # Electron 主进程
|
||||
│ │ ├── modules/ # 主进程模块
|
||||
│ │ ├── index.ts # 主进程入口
|
||||
│ │ ├── lyric.ts # 歌词处理
|
||||
│ │ ├── server.ts # 服务器
|
||||
│ │ ├── set.json # 设置
|
||||
│ │ └── unblockMusic.ts # 音乐解锁
|
||||
│ ├── preload/ # 预加载脚本
|
||||
│ │ ├── index.ts # 预加载脚本入口
|
||||
│ │ └── index.d.ts # 预加载脚本类型声明
|
||||
│ └── renderer/ # Vue 渲染进程
|
||||
│ ├── api/ # API 请求
|
||||
│ ├── assets/ # 静态资源
|
||||
│ ├── components/ # 组件
|
||||
│ │ ├── common/ # 通用组件
|
||||
│ │ ├── home/ # 首页组件
|
||||
│ │ ├── lyric/ # 歌词组件
|
||||
│ │ ├── settings/ # 设置组件
|
||||
│ │ └── ... # 其他组件
|
||||
│ ├── const/ # 常量定义
|
||||
│ ├── directive/ # 自定义指令
|
||||
│ ├── hooks/ # 自定义 Hooks
|
||||
│ ├── layout/ # 布局组件
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── services/ # 服务
|
||||
│ ├── store/ # Pinia 状态管理
|
||||
│ │ ├── modules/ # Pinia 模块
|
||||
│ │ └── index.ts # Pinia 入口
|
||||
│ ├── type/ # 类型定义
|
||||
│ ├── types/ # 更多类型定义
|
||||
│ ├── utils/ # 工具函数
|
||||
│ ├── views/ # 页面视图
|
||||
│ ├── App.vue # 根组件
|
||||
│ ├── index.css # 全局样式
|
||||
│ ├── index.html # HTML 模板
|
||||
│ ├── main.ts # 渲染进程入口
|
||||
│ └── ... # 其他文件
|
||||
├── .env.development # 开发环境变量
|
||||
├── .env.development.local # 本地开发环境变量
|
||||
├── .env.production.local # 本地生产环境变量
|
||||
├── .eslintrc.cjs # ESLint 配置
|
||||
├── .gitignore # Git 忽略文件
|
||||
├── .prettierrc.yaml # Prettier 配置
|
||||
├── electron-builder.yml # electron-builder 配置
|
||||
├── electron.vite.config.ts # electron-vite 配置
|
||||
├── package.json # 项目配置
|
||||
├── postcss.config.js # PostCSS 配置
|
||||
├── tailwind.config.js # Tailwind 配置
|
||||
├── tsconfig.json # TypeScript 配置
|
||||
├── tsconfig.node.json # 节点 TypeScript 配置
|
||||
└── tsconfig.web.json # Web TypeScript 配置
|
||||
```
|
||||
|
||||
## 主要组件说明
|
||||
|
||||
### 主进程 (src/main)
|
||||
|
||||
主进程负责创建窗口、处理系统层面的交互以及与渲染进程的通信。
|
||||
|
||||
- **index.ts**: 应用主入口,负责创建窗口和应用生命周期管理
|
||||
- **lyric.ts**: 歌词解析和处理
|
||||
- **unblockMusic.ts**: 网易云音乐解锁功能
|
||||
- **server.ts**: 本地服务器
|
||||
|
||||
### 预加载脚本 (src/preload)
|
||||
|
||||
预加载脚本在渲染进程加载前执行,提供了渲染进程和主进程之间的桥接功能。
|
||||
|
||||
### 渲染进程 (src/renderer)
|
||||
|
||||
渲染进程是基于 Vue 3 的前端应用,负责 UI 渲染和用户交互。
|
||||
|
||||
- **components/**: 包含各种 UI 组件
|
||||
- **common/**: 通用组件
|
||||
- **home/**: 首页相关组件
|
||||
- **lyric/**: 歌词显示组件
|
||||
- **settings/**: 设置界面组件
|
||||
- **MusicList.vue**: 音乐列表组件
|
||||
- **MvPlayer.vue**: MV 播放器
|
||||
- **EQControl.vue**: 均衡器控制
|
||||
- **...**: 其他组件
|
||||
|
||||
- **store/**: Pinia 状态管理
|
||||
- **modules/**: 各功能模块的状态管理
|
||||
- **index.ts**: 状态管理入口
|
||||
|
||||
- **views/**: 页面视图组件
|
||||
|
||||
- **router/**: 路由配置
|
||||
|
||||
- **api/**: API 请求封装
|
||||
|
||||
- **utils/**: 工具函数
|
||||
|
||||
## 开发指南
|
||||
|
||||
### 命名约定
|
||||
|
||||
- 目录使用 kebab-case (如: components/auth-wizard)
|
||||
- 组件文件名使用 PascalCase (如: AuthWizard.vue)
|
||||
- 可组合式函数使用 camelCase (如: useAuthState.ts)
|
||||
|
||||
### 代码风格
|
||||
|
||||
- 使用 Composition API 和 `<script setup>` 语法
|
||||
- 使用 TypeScript 类型系统
|
||||
- 优先使用类型而非接口
|
||||
- 避免使用枚举,使用 const 对象代替
|
||||
- 使用 tailwind 实现响应式设计
|
||||
@@ -1,4 +1,9 @@
|
||||
VITE_API = /api
|
||||
VITE_API_MT = /mt
|
||||
VITE_API_MUSIC = /music
|
||||
VITE_API_PROXY = http://110.42.251.190:9856
|
||||
# 你的接口地址 (必填)
|
||||
VITE_API = http://127.0.0.1:30488
|
||||
# 音乐破解接口地址 web端
|
||||
VITE_API_MUSIC = ***
|
||||
|
||||
|
||||
# 本地运行代理地址
|
||||
VITE_API_LOCAL = /api
|
||||
VITE_API_MUSIC_PROXY = /music
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
VITE_API = http://110.42.251.190:9898
|
||||
VITE_API_MT = http://mt.myalger.top
|
||||
VITE_API_MUSIC = http://myalger.top:4000
|
||||
VITE_API_PROXY = http://110.42.251.190:9856
|
||||
4
.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
dist
|
||||
out
|
||||
.gitignore
|
||||
137
.eslintrc.cjs
Normal file
@@ -0,0 +1,137 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution');
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'@vue/typescript/recommended',
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:vue-scoped-css/base',
|
||||
'@electron-toolkit',
|
||||
'@electron-toolkit/eslint-config-ts/eslint-recommended',
|
||||
'plugin:prettier/recommended'
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
jest: true,
|
||||
es6: true
|
||||
},
|
||||
globals: {
|
||||
defineProps: 'readonly',
|
||||
defineEmits: 'readonly'
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint', 'simple-import-sort'],
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
allowImportExportEverywhere: true,
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
}
|
||||
},
|
||||
settings: {
|
||||
'import/extensions': ['.js', '.jsx', '.ts', '.tsx']
|
||||
},
|
||||
rules: {
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-nested-ternary': 'off',
|
||||
'no-console': 'off',
|
||||
'no-await-in-loop': 'off',
|
||||
'no-continue': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'no-return-assign': 'off',
|
||||
'no-unused-expressions': 'off',
|
||||
'no-return-await': 'off',
|
||||
'no-plusplus': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-shadow': 'off',
|
||||
'guard-for-in': 'off',
|
||||
'import/extensions': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'import/first': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'vue/first-attribute-linebreak': 0,
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error'
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.vue'],
|
||||
rules: {
|
||||
'vue/component-name-in-template-casing': [2, 'kebab-case'],
|
||||
'vue/require-default-prop': 0,
|
||||
'vue/multi-word-component-names': 0,
|
||||
'vue/no-reserved-props': 0,
|
||||
'vue/no-v-html': 0,
|
||||
'vue-scoped-css/enforce-style-type': [
|
||||
'error',
|
||||
{
|
||||
allows: ['scoped']
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
// 需要行尾分号
|
||||
'prettier/prettier': ['error', { endOfLine: 'auto' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
rules: {
|
||||
'max-classes-per-file': 'off',
|
||||
'no-await-in-loop': 'off',
|
||||
'dot-notation': 'off',
|
||||
'constructor-super': 'off',
|
||||
'getter-return': 'off',
|
||||
'no-const-assign': 'off',
|
||||
'no-dupe-args': 'off',
|
||||
'no-dupe-class-members': 'off',
|
||||
'no-dupe-keys': 'off',
|
||||
'no-func-assign': 'off',
|
||||
'no-import-assign': 'off',
|
||||
'no-new-symbol': 'off',
|
||||
'no-obj-calls': 'off',
|
||||
'no-redeclare': 'off',
|
||||
'no-setter-return': 'off',
|
||||
'no-this-before-super': 'off',
|
||||
'no-undef': 'off',
|
||||
'no-unreachable': 'off',
|
||||
'no-unsafe-negation': 'off',
|
||||
'no-var': 'error',
|
||||
'prefer-const': 'error',
|
||||
'prefer-rest-params': 'error',
|
||||
'prefer-spread': 'error',
|
||||
'valid-typeof': 'off',
|
||||
'consistent-return': 'off',
|
||||
'no-promise-executor-return': 'off',
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* @algerkong
|
||||
70
.github/ISSUE_TEMPLATE/bug-report.zh-CN.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: 反馈 Bug
|
||||
description: 通过 github 模板进行 Bug 反馈。
|
||||
title: "描述问题的标题"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# 欢迎你的参与
|
||||
Issue 列表接受 bug 报告或是新功能请求。
|
||||
|
||||
在发布一个 Issue 前,请确保:
|
||||
- 在Issue中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正)
|
||||
- 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。
|
||||
|
||||
- type: input
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: 重现链接
|
||||
description: 请提供尽可能精简的 CodePen、CodeSandbox 或 GitHub 仓库的链接。请不要填无关链接,否则你的 Issue 将被关闭。
|
||||
placeholder: 请填写
|
||||
|
||||
- type: textarea
|
||||
id: reproduceSteps
|
||||
attributes:
|
||||
label: 重现步骤
|
||||
description: 请清晰的描述重现该 Issue 的步骤,这能帮助我们快速定位问题。没有清晰重现步骤将不会被修复,标有 'need reproduction' 的 Issue 在 7 天内不提供相关步骤,将被关闭。
|
||||
placeholder: 请填写
|
||||
|
||||
- type: textarea
|
||||
id: expect
|
||||
attributes:
|
||||
label: 期望结果
|
||||
placeholder: 请填写
|
||||
|
||||
- type: textarea
|
||||
id: actual
|
||||
attributes:
|
||||
label: 实际结果
|
||||
placeholder: 请填写
|
||||
|
||||
- type: input
|
||||
id: frameworkVersion
|
||||
attributes:
|
||||
label: 框架版本
|
||||
placeholder: Vue(3.3.0)
|
||||
|
||||
- type: input
|
||||
id: browsersVersion
|
||||
attributes:
|
||||
label: 浏览器版本
|
||||
placeholder: Chrome(8.213.231.123)
|
||||
|
||||
- type: input
|
||||
id: systemVersion
|
||||
attributes:
|
||||
label: 系统版本
|
||||
placeholder: MacOS(11.2.3)
|
||||
|
||||
- type: input
|
||||
id: nodeVersion
|
||||
attributes:
|
||||
label: Node版本
|
||||
placeholder: 请填写
|
||||
|
||||
- type: textarea
|
||||
id: remarks
|
||||
attributes:
|
||||
label: 补充说明
|
||||
description: 可以是遇到这个 bug 的业务场景、上下文等信息。
|
||||
placeholder: 请填写
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name:
|
||||
url:
|
||||
about:
|
||||
29
.github/ISSUE_TEMPLATE/feature-report.zh-CN.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: 反馈新功能
|
||||
description: 通过 github 模板进行新功能反馈。
|
||||
title: "描述问题的标题"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# 欢迎你的参与
|
||||
|
||||
在发布一个 Issue 前,请确保:
|
||||
- 在 Issue 中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正)
|
||||
- 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。
|
||||
|
||||
- type: textarea
|
||||
id: functionContent
|
||||
attributes:
|
||||
label: 这个功能解决了什么问题
|
||||
description: 请详尽说明这个需求的用例和场景。最重要的是:解释清楚是怎样的用户体验需求催生了这个功能上的需求。我们将考虑添加在现有 API 无法轻松实现的功能。新功能的用例也应当足够常见。
|
||||
placeholder: 请填写
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: functionalExpectations
|
||||
attributes:
|
||||
label: 你建议的方案是什么
|
||||
placeholder: 请填写
|
||||
validations:
|
||||
required: true
|
||||
51
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<!--
|
||||
首先,感谢你的贡献!😄
|
||||
PR 在维护者审核通过后会合并,谢谢!
|
||||
-->
|
||||
|
||||
### 🤔 这个 PR 的性质是?
|
||||
|
||||
- [ ] 日常 bug 修复
|
||||
- [ ] 新特性提交
|
||||
- [ ] 文档改进
|
||||
- [ ] 演示代码改进
|
||||
- [ ] 组件样式/交互改进
|
||||
- [ ] CI/CD 改进
|
||||
- [ ] 重构
|
||||
- [ ] 代码风格优化
|
||||
- [ ] 测试用例
|
||||
- [ ] 分支合并
|
||||
- [ ] 其他
|
||||
|
||||
### 🔗 相关 Issue
|
||||
|
||||
<!--
|
||||
1. 描述相关需求的来源,如相关的 issue 讨论链接。
|
||||
-->
|
||||
|
||||
### 💡 需求背景和解决方案
|
||||
|
||||
<!--
|
||||
1. 要解决的具体问题。
|
||||
2. 列出最终的 API 实现和用法。
|
||||
3. 涉及UI/交互变动需要有截图或 GIF。
|
||||
-->
|
||||
|
||||
### 📝 更新日志
|
||||
|
||||
<!--
|
||||
从用户角度描述具体变化,以及可能的 breaking change 和其他风险。
|
||||
-->
|
||||
|
||||
- fix(组件名称): 处理问题或特性描述 ...
|
||||
|
||||
- [ ] 本条 PR 不需要纳入 Changelog
|
||||
|
||||
### ☑️ 请求合并前的自查清单
|
||||
|
||||
⚠️ 请自检并全部**勾选全部选项**。⚠️
|
||||
|
||||
- [ ] 文档已补充或无须补充
|
||||
- [ ] 代码演示已提供或无须提供
|
||||
- [ ] TypeScript 定义已补充或无须补充
|
||||
- [ ] Changelog 已提供或无须提供
|
||||
20
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Basic dependabot.yml file with
|
||||
# minimum configuration for two package managers
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
# Enable version updates for npm
|
||||
- package-ecosystem: 'npm'
|
||||
# Look for `package.json` and `lock` files in the `root` directory
|
||||
directory: '/'
|
||||
# Check the npm registry for updates every day (weekdays)
|
||||
schedule:
|
||||
interval: 'monthly'
|
||||
|
||||
# Enable version updates for Docker
|
||||
- package-ecosystem: 'docker'
|
||||
# Look for a `Dockerfile` in the `root` directory
|
||||
directory: '/'
|
||||
# Check for updates once a week
|
||||
schedule:
|
||||
interval: 'monthly'
|
||||
8
.github/issue-shoot.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
## IssueShoot
|
||||
- 预估时长: {{ .duration }}
|
||||
- 期望完成时间: {{ .deadline }}
|
||||
- 开发难度: {{ .level }}
|
||||
- 参与人数: 1
|
||||
- 需求对接人: ivringpeng
|
||||
- 验收标准: 实现期望改造效果,提 PR 并通过验收无误
|
||||
- 备注: 最终激励以实际提交 `pull request` 并合并为准
|
||||
87
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
# MacOS Build
|
||||
- name: Build MacOS
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
export ELECTRON_BUILDER_EXTRA_ARGS="--universal"
|
||||
npm run build:mac
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_IDENTITY_AUTO_DISCOVERY: false
|
||||
DEBUG: electron-builder
|
||||
|
||||
# Windows Build
|
||||
- name: Build Windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
run: npm run build:win
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Linux Build
|
||||
- name: Build Linux
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
|
||||
npm run build:linux
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Get version from tag
|
||||
- name: Get version from tag
|
||||
id: get_version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
# Read release notes
|
||||
- name: Read release notes
|
||||
id: release_notes
|
||||
run: |
|
||||
NOTES=$(awk "/## \[v${{ env.VERSION }}\]/{p=1;print;next} /## \[v/{p=0}p" CHANGELOG.md)
|
||||
echo "NOTES<<EOF" >> $GITHUB_ENV
|
||||
echo "$NOTES" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
# Upload artifacts
|
||||
- name: Upload artifacts
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
dist/*.dmg
|
||||
dist/*.exe
|
||||
dist/*.deb
|
||||
dist/*.AppImage
|
||||
dist/latest*.yml
|
||||
dist/*.blockmap
|
||||
body: ${{ env.NOTES }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
51
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Deploy Web
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main # 或者您的主分支名称
|
||||
workflow_dispatch: # 允许手动触发
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: 创建环境变量文件
|
||||
run: |
|
||||
echo "VITE_API=${{ secrets.VITE_API }}" > .env.production.local
|
||||
echo "VITE_API_MUSIC=${{ secrets.VITE_API_MUSIC }}" >> .env.production.local
|
||||
# 添加其他需要的环境变量
|
||||
cat .env.production.local # 查看创建的文件内容,调试用
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Deploy to Server
|
||||
uses: appleboy/scp-action@master
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SERVER_USERNAME }}
|
||||
key: ${{ secrets.DEPLOY_KEY }}
|
||||
source: "out/renderer/*"
|
||||
target: ${{ secrets.DEPLOY_PATH }}
|
||||
strip_components: 2
|
||||
|
||||
- name: Execute Remote Commands
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.SERVER_HOST }}
|
||||
username: ${{ secrets.SERVER_USERNAME }}
|
||||
key: ${{ secrets.DEPLOY_KEY }}
|
||||
script: |
|
||||
cd ${{ secrets.DEPLOY_PATH }}
|
||||
echo "部署完成于 $(date)"
|
||||
23
.gitignore
vendored
@@ -1,8 +1,29 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
dist_electron
|
||||
.idea
|
||||
|
||||
# lock
|
||||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
dist.zip
|
||||
package-lock.json
|
||||
dist.zip
|
||||
|
||||
.vscode
|
||||
|
||||
bun.lockb
|
||||
bun.lock
|
||||
|
||||
.env.*.local
|
||||
|
||||
out
|
||||
|
||||
.cursorrules
|
||||
|
||||
.github/deploy_keys
|
||||
|
||||
resources/android/**/*
|
||||
2
.husky/pre-commit
Normal file
@@ -0,0 +1,2 @@
|
||||
echo "运行类型检查..."
|
||||
npm run typecheck
|
||||
2
.husky/pre-push
Executable file
@@ -0,0 +1,2 @@
|
||||
echo "运行类型检查..."
|
||||
npm run typecheck
|
||||
6
.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
||||
out
|
||||
dist
|
||||
pnpm-lock.yaml
|
||||
LICENSE.md
|
||||
tsconfig.json
|
||||
tsconfig.*.json
|
||||
5
.prettierrc.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
singleQuote: true
|
||||
semi: true
|
||||
printWidth: 100
|
||||
trailingComma: none
|
||||
endOfLine: auto
|
||||
3
.vscode/settings.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"compile-hero.disable-compile-files-on-did-save-code": true
|
||||
}
|
||||
58
CHANGELOG.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# 更新日志
|
||||
|
||||
## v4.7.0
|
||||
> 如果更新遇到问题,请前往 <a href="http://donate.alger.fun/download" target="_blank">下载 AlgerMusicPlayer</a>
|
||||
|
||||
> 请我喝咖啡(支持作者) ☕️ <a href="http://donate.alger.fun/donate" target="_blank" style="color: red; font-weight: bold;">赏你</a>
|
||||
|
||||
> 帮我点个 star <a href="https://github.com/algerkong/AlgerMusicPlayer" target="_blank">github star</a>
|
||||
|
||||
> QQ频道 <a href="https://pd.qq.com/s/cs056n33q?b=5" target="_blank">加入频道</a>
|
||||
|
||||
### ✨ 新功能
|
||||
- 替换扁平风格图标 ([c8e6db1](https://github.com/algerkong/AlgerMusicPlayer/commit/c8e6db1))
|
||||
- 添加双击歌曲播放功能 ([258828f](https://github.com/algerkong/AlgerMusicPlayer/commit/258828f))
|
||||
- 添加排行榜页面 ([a8010c8](https://github.com/algerkong/AlgerMusicPlayer/commit/a8010c8))
|
||||
- 添加播放速度控制功能 ([6554736](https://github.com/algerkong/AlgerMusicPlayer/commit/6554736)) (#241) [感谢Java-wyx的 PR](https://github.com/Java-wyx)
|
||||
- 修改播放列表展示形式,添加清空播放列表功能 ([2e96161](https://github.com/algerkong/AlgerMusicPlayer/commit/2e96161))
|
||||
- 歌单列表添加布局切换、播放全部、收藏、添加到播放列表功能 ([b32408b](https://github.com/algerkong/AlgerMusicPlayer/commit/b32408b))
|
||||
- 优化播放栏,整合高级控制菜单([f5f0dbb](https://github.com/algerkong/AlgerMusicPlayer/commit/f5f0dbb))
|
||||
- 添加顶部定时显示,定时器过期检查功能, ([2dd4535](https://github.com/algerkong/AlgerMusicPlayer/commit/2dd4535)),([170ac45](https://github.com/algerkong/AlgerMusicPlayer/commit/170ac45))
|
||||
- 添加 mac 状态栏播放按键控制功能开关 ([2476fbd](https://github.com/algerkong/AlgerMusicPlayer/commit/2476fbd))
|
||||
- 收藏列表添加升序降序排列 ([2803d40](https://github.com/algerkong/AlgerMusicPlayer/commit/2803d40))
|
||||
- 为歌曲下拉菜单添加圆角样式,优化歌曲预览布局 ([5bef0e4](https://github.com/algerkong/AlgerMusicPlayer/commit/5bef0e4))
|
||||
- 在用户歌单中添加"我创建的"标签,优化获取用户歌单的逻辑 ([d56a25e](https://github.com/algerkong/AlgerMusicPlayer/commit/d56a25e))
|
||||
- 在歌手详情页添加歌曲操作工具栏,支持播放全部、添加到播放列表和搜索功能 ([6048e24](https://github.com/algerkong/AlgerMusicPlayer/commit/6048e24))
|
||||
- 添加所有用户的关注和粉丝列表点击,优化播放排行获取和无权限展示 ([0c74291](https://github.com/algerkong/AlgerMusicPlayer/commit/0c74291))
|
||||
- 添加 macOS 下点击 Dock 图标激活主窗口的功能 ([7fa0fa5](https://github.com/algerkong/AlgerMusicPlayer/commit/7fa0fa5))
|
||||
- 添加鼠标滚轮调整音量功能,并显示音量百分比 ([95af222](https://github.com/algerkong/AlgerMusicPlayer/commit/95af222))
|
||||
- 添加歌曲右键不喜欢功能以过滤每日推荐歌曲 ([a0935c7](https://github.com/algerkong/AlgerMusicPlayer/commit/a0935c7))
|
||||
- 添加音乐平台链接,优化移动端样式 ([01a3a7a](https://github.com/algerkong/AlgerMusicPlayer/commit/01a3a7a))
|
||||
- 在应用菜单中添加工具提示功能 ([5084da3](https://github.com/algerkong/AlgerMusicPlayer/commit/5084da3))
|
||||
|
||||
### 🐛 Bug 修复
|
||||
- 修复播放音乐时 URL 未正确更新的问题 ([91b1ff7](https://github.com/algerkong/AlgerMusicPlayer/commit/91b1ff7))
|
||||
- 修复播放状态判断逻辑 ([e9fe900](https://github.com/algerkong/AlgerMusicPlayer/commit/e9fe900))
|
||||
- 修复收藏歌曲功能未同步问题 ([69b1e54](https://github.com/algerkong/AlgerMusicPlayer/commit/69b1e54))
|
||||
- 优化下载列表显示 ([df5ecb6](https://github.com/algerkong/AlgerMusicPlayer/commit/df5ecb6))
|
||||
|
||||
### 🎨 优化
|
||||
- 添加 husky 预提交和预推送钩子,运行类型检查以确保代码质量 ([a449b74](https://github.com/algerkong/AlgerMusicPlayer/commit/a449b74))
|
||||
- 重构歌曲组件,添加基础组件和多种样式,优化播放列表抽屉功能 ([ad7b504](https://github.com/algerkong/AlgerMusicPlayer/commit/ad7b504))
|
||||
- 将下载逻辑提取优化([ca51020](https://github.com/algerkong/AlgerMusicPlayer/commit/ca51020))
|
||||
- 优化B站音频解析功能 ([e47c84e](https://github.com/algerkong/AlgerMusicPlayer/commit/e47c84e))
|
||||
- 统一音源选项的标签格式 ([54cbb84](https://github.com/algerkong/AlgerMusicPlayer/commit/54cbb84))
|
||||
- 移动端去除定时关闭功能 ([f7951ec](https://github.com/algerkong/AlgerMusicPlayer/commit/f7951ec))
|
||||
- 优化网页端下载程序功能 ([56b3ecf](https://github.com/algerkong/AlgerMusicPlayer/commit/56b3ecf))
|
||||
- 退出登录时刷新页面 ([54f82d3](https://github.com/algerkong/AlgerMusicPlayer/commit/54f82d3))
|
||||
|
||||
### 其他优化
|
||||
([ae1a7c9](https://github.com/algerkong/AlgerMusicPlayer/commit/ae1a7c9))
|
||||
([f68f499](https://github.com/algerkong/AlgerMusicPlayer/commit/f68f499))
|
||||
([6d4e6ef](https://github.com/algerkong/AlgerMusicPlayer/commit/6d4e6ef))
|
||||
([2379b2c](https://github.com/algerkong/AlgerMusicPlayer/commit/2379b2c))
|
||||
([33a1057](https://github.com/algerkong/AlgerMusicPlayer/commit/33a1057))
|
||||
([54d66d0](https://github.com/algerkong/AlgerMusicPlayer/commit/54d66d0))
|
||||
([3c792ce](https://github.com/algerkong/AlgerMusicPlayer/commit/3c792ce))
|
||||
([35b84f3](https://github.com/algerkong/AlgerMusicPlayer/commit/35b84f3))
|
||||
([278db37](https://github.com/algerkong/AlgerMusicPlayer/commit/278db37))
|
||||
107
README.md
@@ -1,5 +1,104 @@
|
||||
# Vue 3 + Typescript + Vite
|
||||
|
||||
vue3 + TypeScript + NaiveUI + animateCss + Vuex + VueRouter + Axios等实现音乐桌面web端
|
||||
实现各项功能
|
||||
网站地址:http://mc.myalger.top/
|
||||
<h2 align="center">🎵 Alger Music Player</h2>
|
||||
<div align="center">
|
||||
<div align="center">
|
||||
<a href="https://github.com/algerkong/AlgerMusicPlayer/stargazers">
|
||||
<img src="https://img.shields.io/github/stars/algerkong/AlgerMusicPlayer?style=for-the-badge&logo=github&label=Stars&logoColor=white&color=22c55e" alt="GitHub stars">
|
||||
</a>
|
||||
<a href="https://github.com/algerkong/AlgerMusicPlayer/releases">
|
||||
<img src="https://img.shields.io/github/v/release/algerkong/AlgerMusicPlayer?style=for-the-badge&logo=github&label=Release&logoColor=white&color=1a67af" alt="GitHub release">
|
||||
</a>
|
||||
<a href="https://pd.qq.com/s/cs056n33q?b=5">
|
||||
<img src="https://img.shields.io/badge/QQ频道-algermusic-blue?style=for-the-badge&color=yellow" alt="加入频道">
|
||||
</a>
|
||||
<a href="https://t.me/+9efsKRuvKBk2NWVl">
|
||||
<img src="https://img.shields.io/badge/AlgerMusic-blue?style=for-the-badge&logo=telegram&logoColor=white&label=Telegram" alt="Telegram">
|
||||
</a>
|
||||
<a href="https://donate.alger.fun/">
|
||||
<img src="https://img.shields.io/badge/%E9%A1%B9%E7%9B%AE%E6%8D%90%E8%B5%A0-blue?style=for-the-badge&logo=telegram&logoColor=pink&color=pink&label=%E8%B5%9E%E5%8A%A9" alt="赞助">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div align="center">
|
||||
<a href="https://hellogithub.com/repository/607b849c598d48e08fe38789d156ebdc" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=607b849c598d48e08fe38789d156ebdc&claim_uid=ObuMXUfeHBmk9TI&theme=neutral" alt="Featured|HelloGitHub" width="160" height="32" /></a>
|
||||
</div>
|
||||
|
||||
主要功能如下
|
||||
- 🎵 音乐推荐
|
||||
- 🔐 网易云账号登录与同步
|
||||
- 📝 功能
|
||||
- 播放历史记录
|
||||
- 歌曲收藏管理
|
||||
- 歌单 MV 排行榜 每日推荐
|
||||
- 自定义快捷键配置(全局或应用内)
|
||||
- 🎨 界面与交互
|
||||
- 沉浸式歌词显示(点击左下角封面进入)
|
||||
- 独立桌面歌词窗口
|
||||
- 明暗主题切换
|
||||
- 迷你模式
|
||||
- 状态栏控制
|
||||
- 多语言支持
|
||||
|
||||
- 🎼 音乐功能
|
||||
- 支持歌单、MV、专辑等完整音乐服务
|
||||
- 灰色音乐资源解析(基于 @unblockneteasemusic/server)
|
||||
- 音乐单独解析
|
||||
- EQ均衡器
|
||||
- 定时播放 远程控制播放 倍速播放
|
||||
- 高品质音乐
|
||||
- 音乐文件下载(支持右键下载和批量下载, 附带歌词封面等信息)
|
||||
- 搜索 MV 音乐 专辑 歌单 bilibili
|
||||
- 音乐单独选择音源解析
|
||||
- 🚀 技术特性
|
||||
- 本地化服务,无需依赖在线API (基于 netease-cloud-music-api)
|
||||
- 全平台适配(Desktop & Web & Mobile Web & Android<测试> & ios<后续>)
|
||||
|
||||
## 项目简介
|
||||
一个第三方音乐播放器、本地服务、桌面歌词、音乐下载、最高音质
|
||||
|
||||
## 预览地址
|
||||
[http://music.alger.fun/](http://music.alger.fun/)
|
||||
|
||||
## 软件截图
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 项目启动
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
## 项目打包
|
||||
```bash
|
||||
# web
|
||||
npm run build
|
||||
# win
|
||||
npm run build:win
|
||||
# mac
|
||||
npm run build:mac
|
||||
# linux
|
||||
npm run build:linux
|
||||
```
|
||||
|
||||
|
||||
## 赞赏☕️
|
||||
[赞赏列表](http://donate.alger.fun/)
|
||||
| 微信赞赏 | 支付宝赞赏 |
|
||||
| :--------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------: |
|
||||
| <img src="https://github.com/algerkong/algerkong/blob/main/wechat.jpg?raw=true" alt="WeChat QRcode" width=200> <br><small>喝点咖啡继续干</small> | <img src="https://github.com/algerkong/algerkong/blob/main/alipay.jpg?raw=true" alt="Wechat QRcode" width=200> <br><small>来包辣条吧~</small> |
|
||||
|
||||
|
||||
## 项目统计
|
||||
[](https://starchart.cc/algerkong/AlgerMusicPlayer)
|
||||

|
||||
|
||||
|
||||
|
||||
## 欢迎提Issues
|
||||
|
||||
## 免责声明
|
||||
本软件仅用于学习交流,禁止用于商业用途,否则后果自负。
|
||||
|
||||
101
android/.gitignore
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
|
||||
|
||||
# Built application files
|
||||
*.apk
|
||||
*.aar
|
||||
*.ap_
|
||||
*.aab
|
||||
|
||||
# Files for the ART/Dalvik VM
|
||||
*.dex
|
||||
|
||||
# Java class files
|
||||
*.class
|
||||
|
||||
# Generated files
|
||||
bin/
|
||||
gen/
|
||||
out/
|
||||
# Uncomment the following line in case you need and you don't have the release build type files in your app
|
||||
# release/
|
||||
|
||||
# Gradle files
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Proguard folder generated by Eclipse
|
||||
proguard/
|
||||
|
||||
# Log Files
|
||||
*.log
|
||||
|
||||
# Android Studio Navigation editor temp files
|
||||
.navigation/
|
||||
|
||||
# Android Studio captures folder
|
||||
captures/
|
||||
|
||||
# IntelliJ
|
||||
*.iml
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/gradle.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
.idea/dictionaries
|
||||
.idea/libraries
|
||||
# Android Studio 3 in .gitignore file.
|
||||
.idea/caches
|
||||
.idea/modules.xml
|
||||
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
|
||||
.idea/navEditor.xml
|
||||
|
||||
# Keystore files
|
||||
# Uncomment the following lines if you do not want to check your keystore files in.
|
||||
#*.jks
|
||||
#*.keystore
|
||||
|
||||
# External native build folder generated in Android Studio 2.2 and later
|
||||
.externalNativeBuild
|
||||
.cxx/
|
||||
|
||||
# Google Services (e.g. APIs or Firebase)
|
||||
# google-services.json
|
||||
|
||||
# Freeline
|
||||
freeline.py
|
||||
freeline/
|
||||
freeline_project_description.json
|
||||
|
||||
# fastlane
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# Version control
|
||||
vcs.xml
|
||||
|
||||
# lint
|
||||
lint/intermediates/
|
||||
lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
|
||||
# Android Profiling
|
||||
*.hprof
|
||||
|
||||
# Cordova plugins for Capacitor
|
||||
capacitor-cordova-android-plugins
|
||||
|
||||
# Copied web assets
|
||||
app/src/main/assets/public
|
||||
|
||||
# Generated Config files
|
||||
app/src/main/assets/capacitor.config.json
|
||||
app/src/main/assets/capacitor.plugins.json
|
||||
app/src/main/res/xml/config.xml
|
||||
90
auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: (typeof import('vue'))['EffectScope'];
|
||||
const computed: (typeof import('vue'))['computed'];
|
||||
const createApp: (typeof import('vue'))['createApp'];
|
||||
const customRef: (typeof import('vue'))['customRef'];
|
||||
const defineAsyncComponent: (typeof import('vue'))['defineAsyncComponent'];
|
||||
const defineComponent: (typeof import('vue'))['defineComponent'];
|
||||
const effectScope: (typeof import('vue'))['effectScope'];
|
||||
const getCurrentInstance: (typeof import('vue'))['getCurrentInstance'];
|
||||
const getCurrentScope: (typeof import('vue'))['getCurrentScope'];
|
||||
const h: (typeof import('vue'))['h'];
|
||||
const inject: (typeof import('vue'))['inject'];
|
||||
const isProxy: (typeof import('vue'))['isProxy'];
|
||||
const isReactive: (typeof import('vue'))['isReactive'];
|
||||
const isReadonly: (typeof import('vue'))['isReadonly'];
|
||||
const isRef: (typeof import('vue'))['isRef'];
|
||||
const markRaw: (typeof import('vue'))['markRaw'];
|
||||
const nextTick: (typeof import('vue'))['nextTick'];
|
||||
const onActivated: (typeof import('vue'))['onActivated'];
|
||||
const onBeforeMount: (typeof import('vue'))['onBeforeMount'];
|
||||
const onBeforeUnmount: (typeof import('vue'))['onBeforeUnmount'];
|
||||
const onBeforeUpdate: (typeof import('vue'))['onBeforeUpdate'];
|
||||
const onDeactivated: (typeof import('vue'))['onDeactivated'];
|
||||
const onErrorCaptured: (typeof import('vue'))['onErrorCaptured'];
|
||||
const onMounted: (typeof import('vue'))['onMounted'];
|
||||
const onRenderTracked: (typeof import('vue'))['onRenderTracked'];
|
||||
const onRenderTriggered: (typeof import('vue'))['onRenderTriggered'];
|
||||
const onScopeDispose: (typeof import('vue'))['onScopeDispose'];
|
||||
const onServerPrefetch: (typeof import('vue'))['onServerPrefetch'];
|
||||
const onUnmounted: (typeof import('vue'))['onUnmounted'];
|
||||
const onUpdated: (typeof import('vue'))['onUpdated'];
|
||||
const onWatcherCleanup: (typeof import('vue'))['onWatcherCleanup'];
|
||||
const provide: (typeof import('vue'))['provide'];
|
||||
const reactive: (typeof import('vue'))['reactive'];
|
||||
const readonly: (typeof import('vue'))['readonly'];
|
||||
const ref: (typeof import('vue'))['ref'];
|
||||
const resolveComponent: (typeof import('vue'))['resolveComponent'];
|
||||
const shallowReactive: (typeof import('vue'))['shallowReactive'];
|
||||
const shallowReadonly: (typeof import('vue'))['shallowReadonly'];
|
||||
const shallowRef: (typeof import('vue'))['shallowRef'];
|
||||
const toRaw: (typeof import('vue'))['toRaw'];
|
||||
const toRef: (typeof import('vue'))['toRef'];
|
||||
const toRefs: (typeof import('vue'))['toRefs'];
|
||||
const toValue: (typeof import('vue'))['toValue'];
|
||||
const triggerRef: (typeof import('vue'))['triggerRef'];
|
||||
const unref: (typeof import('vue'))['unref'];
|
||||
const useAttrs: (typeof import('vue'))['useAttrs'];
|
||||
const useCssModule: (typeof import('vue'))['useCssModule'];
|
||||
const useCssVars: (typeof import('vue'))['useCssVars'];
|
||||
const useDialog: (typeof import('naive-ui'))['useDialog'];
|
||||
const useId: (typeof import('vue'))['useId'];
|
||||
const useLoadingBar: (typeof import('naive-ui'))['useLoadingBar'];
|
||||
const useMessage: (typeof import('naive-ui'))['useMessage'];
|
||||
const useModel: (typeof import('vue'))['useModel'];
|
||||
const useNotification: (typeof import('naive-ui'))['useNotification'];
|
||||
const useSlots: (typeof import('vue'))['useSlots'];
|
||||
const useTemplateRef: (typeof import('vue'))['useTemplateRef'];
|
||||
const watch: (typeof import('vue'))['watch'];
|
||||
const watchEffect: (typeof import('vue'))['watchEffect'];
|
||||
const watchPostEffect: (typeof import('vue'))['watchPostEffect'];
|
||||
const watchSyncEffect: (typeof import('vue'))['watchSyncEffect'];
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type {
|
||||
Component,
|
||||
ComponentPublicInstance,
|
||||
ComputedRef,
|
||||
DirectiveBinding,
|
||||
ExtractDefaultPropTypes,
|
||||
ExtractPropTypes,
|
||||
ExtractPublicPropTypes,
|
||||
InjectionKey,
|
||||
PropType,
|
||||
Ref,
|
||||
MaybeRef,
|
||||
MaybeRefOrGetter,
|
||||
VNode,
|
||||
WritableComputedRef
|
||||
} from 'vue';
|
||||
import('vue');
|
||||
}
|
||||
20
build/entitlements.mac.plist
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
build/icon.icns
Normal file
BIN
build/icon.ico
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
build/icon.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
13
build/installer.nsh
Normal file
@@ -0,0 +1,13 @@
|
||||
# 设置 Windows 7 兼容性
|
||||
ManifestDPIAware true
|
||||
ManifestSupportedOS all
|
||||
|
||||
!macro customInit
|
||||
# 检查系统版本
|
||||
${If} ${AtLeastWin7}
|
||||
# Windows 7 或更高版本
|
||||
${Else}
|
||||
MessageBox MB_OK|MB_ICONSTOP "此应用程序需要 Windows 7 或更高版本。"
|
||||
Abort
|
||||
${EndIf}
|
||||
!macroend
|
||||
33
components.d.ts
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {};
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
NAvatar: typeof import('naive-ui')['NAvatar']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
NImage: typeof import('naive-ui')['NImage']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||
NLayout: typeof import('naive-ui')['NLayout']
|
||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NPopover: typeof import('naive-ui')['NPopover']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSlider: typeof import('naive-ui')['NSlider']
|
||||
NSpin: typeof import('naive-ui')['NSpin']
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
||||
3
dev-app-update.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
provider: generic
|
||||
url: https://example.com/auto-updates
|
||||
updaterCacheDirName: electron-lan-file-updater
|
||||
BIN
docs/image.png
Normal file
|
After Width: | Height: | Size: 897 KiB |
BIN
docs/image1.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
docs/image2.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
docs/image3.png
Normal file
|
After Width: | Height: | Size: 900 KiB |
BIN
docs/image4.png
Normal file
|
After Width: | Height: | Size: 406 KiB |
BIN
docs/image5.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/image6.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
45
electron-builder.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
appId: com.electron.app
|
||||
productName: electron-lan-file
|
||||
directories:
|
||||
buildResources: build
|
||||
files:
|
||||
- '!**/.vscode/*'
|
||||
- '!src/*'
|
||||
- '!electron.vite.config.{js,ts,mjs,cjs}'
|
||||
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
|
||||
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
|
||||
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
|
||||
asarUnpack:
|
||||
- resources/**
|
||||
win:
|
||||
executableName: electron-lan-file
|
||||
nsis:
|
||||
artifactName: ${name}-${version}-setup.${ext}
|
||||
shortcutName: ${productName}
|
||||
uninstallDisplayName: ${productName}
|
||||
createDesktopShortcut: always
|
||||
mac:
|
||||
entitlementsInherit: build/entitlements.mac.plist
|
||||
extendInfo:
|
||||
- NSCameraUsageDescription: Application requests access to the device's camera.
|
||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||
notarize: false
|
||||
dmg:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
linux:
|
||||
target:
|
||||
- AppImage
|
||||
- snap
|
||||
- deb
|
||||
maintainer: electronjs.org
|
||||
category: Utility
|
||||
appImage:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
npmRebuild: false
|
||||
publish:
|
||||
provider: generic
|
||||
url: https://example.com/auto-updates
|
||||
electronDownload:
|
||||
mirror: https://npmmirror.com/mirrors/electron/
|
||||
61
electron.vite.config.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
|
||||
import { resolve } from 'path';
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import viteCompression from 'vite-plugin-compression';
|
||||
import VueDevTools from 'vite-plugin-vue-devtools';
|
||||
|
||||
export default defineConfig({
|
||||
main: {
|
||||
plugins: [externalizeDepsPlugin()]
|
||||
},
|
||||
preload: {
|
||||
plugins: [externalizeDepsPlugin()]
|
||||
},
|
||||
renderer: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src/renderer'),
|
||||
'@renderer': resolve('src/renderer')
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
viteCompression(),
|
||||
VueDevTools(),
|
||||
AutoImport({
|
||||
imports: [
|
||||
'vue',
|
||||
{
|
||||
'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar']
|
||||
}
|
||||
]
|
||||
}),
|
||||
Components({
|
||||
resolvers: [NaiveUiResolver()]
|
||||
})
|
||||
],
|
||||
server: {
|
||||
proxy: {
|
||||
// with options
|
||||
[process.env.VITE_API_LOCAL as string]: {
|
||||
target: process.env.VITE_API,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^${process.env.VITE_API_LOCAL}`), '')
|
||||
},
|
||||
[process.env.VITE_API_MUSIC_PROXY as string]: {
|
||||
target: process.env.VITE_API_MUSIC,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^${process.env.VITE_API_MUSIC_PROXY}`), '')
|
||||
},
|
||||
[process.env.VITE_API_PROXY_MUSIC as string]: {
|
||||
target: process.env.VITE_API_PROXY,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^${process.env.VITE_API_PROXY_MUSIC}`), '')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
24
index.html
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>网抑云 | algerkong</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//at.alicdn.com/t/font_2685283_5bo4ekd5wh.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="./public/css/animate.css" />
|
||||
<style>
|
||||
:root {
|
||||
--animate-delay: 0.5s;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
201
package.json
@@ -1,33 +1,184 @@
|
||||
{
|
||||
"version": "0.0.0",
|
||||
"name": "AlgerMusicPlayer",
|
||||
"version": "4.7.0",
|
||||
"description": "Alger Music Player",
|
||||
"author": "Alger <algerkc@qq.com>",
|
||||
"main": "./out/main/index.js",
|
||||
"homepage": "https://github.com/algerkong/AlgerMusicPlayer",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview"
|
||||
"prepare": "husky",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix",
|
||||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
|
||||
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false",
|
||||
"typecheck": "npm run typecheck:node && npm run typecheck:web",
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev",
|
||||
"build": "npm run typecheck && electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:unpack": "npm run build && electron-builder --dir",
|
||||
"build:win": "npm run build && electron-builder --win",
|
||||
"build:mac": "npm run build && electron-builder --mac",
|
||||
"build:linux": "npm run build && electron-builder --linux"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/postcss7-compat": "^2.2.4",
|
||||
"@vue/runtime-core": "^3.3.4",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"axios": "^0.21.1",
|
||||
"lodash": "^4.17.21",
|
||||
"postcss": "^7.0.36",
|
||||
"sass": "^1.35.2",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.4",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4",
|
||||
"vuex": "^4.1.0"
|
||||
"@electron-toolkit/preload": "^3.0.1",
|
||||
"@electron-toolkit/utils": "^4.0.0",
|
||||
"@unblockneteasemusic/server": "^0.27.8-patch.1",
|
||||
"cors": "^2.8.5",
|
||||
"electron-store": "^8.1.0",
|
||||
"electron-updater": "^6.6.2",
|
||||
"express": "^4.18.2",
|
||||
"font-list": "^1.5.1",
|
||||
"husky": "^9.1.7",
|
||||
"netease-cloud-music-api-alger": "^4.26.1",
|
||||
"node-id3": "^0.2.9",
|
||||
"node-machine-id": "^1.1.12",
|
||||
"vue-i18n": "^11.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sicons/antd": "^0.10.0",
|
||||
"@vicons/antd": "^0.10.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"naive-ui": "^2.34.4",
|
||||
"typescript": "^4.3.2",
|
||||
"vfonts": "^0.1.0",
|
||||
"vite": "^4.4.7",
|
||||
"vite-plugin-vue-devtools": "1.0.0-beta.5",
|
||||
"vue-tsc": "^0.0.24"
|
||||
"@electron-toolkit/eslint-config": "^2.1.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^3.1.0",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@rushstack/eslint-patch": "^1.10.3",
|
||||
"@tailwindcss/postcss7-compat": "^2.2.4",
|
||||
"@types/howler": "^2.2.12",
|
||||
"@types/node": "^20.14.8",
|
||||
"@types/tinycolor2": "^1.4.6",
|
||||
"@typescript-eslint/eslint-plugin": "^8.30.1",
|
||||
"@typescript-eslint/parser": "^8.30.1",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vue/compiler-sfc": "^3.5.0",
|
||||
"@vue/eslint-config-prettier": "^10.2.0",
|
||||
"@vue/eslint-config-typescript": "^14.5.0",
|
||||
"@vue/runtime-core": "^3.5.0",
|
||||
"@vueuse/core": "^11.3.0",
|
||||
"@vueuse/electron": "^11.3.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.7",
|
||||
"cross-env": "^7.0.3",
|
||||
"electron": "^36.2.0",
|
||||
"electron-builder": "^25.1.8",
|
||||
"electron-vite": "^3.1.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
||||
"eslint-plugin-vue": "^10.0.0",
|
||||
"eslint-plugin-vue-scoped-css": "^2.9.0",
|
||||
"howler": "^2.2.4",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "^15.0.4",
|
||||
"naive-ui": "^2.41.0",
|
||||
"pinia": "^3.0.1",
|
||||
"pinyin-match": "^1.2.6",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.3.2",
|
||||
"remixicon": "^4.6.0",
|
||||
"sass": "^1.86.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"tunajs": "^1.0.15",
|
||||
"typescript": "^5.5.2",
|
||||
"unplugin-auto-import": "^19.1.1",
|
||||
"unplugin-vue-components": "^28.4.1",
|
||||
"vite": "^6.2.2",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-vue-devtools": "7.7.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-tsc": "^2.0.22"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.alger.music",
|
||||
"productName": "AlgerMusicPlayer",
|
||||
"publish": [
|
||||
{
|
||||
"provider": "github",
|
||||
"owner": "algerkong",
|
||||
"repo": "AlgerMusicPlayer"
|
||||
}
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "resources/html",
|
||||
"to": "html",
|
||||
"filter": [
|
||||
"**/*"
|
||||
]
|
||||
}
|
||||
],
|
||||
"mac": {
|
||||
"icon": "resources/icon.icns",
|
||||
"target": [
|
||||
{
|
||||
"target": "dmg",
|
||||
"arch": [
|
||||
"universal"
|
||||
]
|
||||
}
|
||||
],
|
||||
"artifactName": "${productName}-${version}-mac-${arch}.${ext}",
|
||||
"darkModeSupport": true,
|
||||
"hardenedRuntime": false,
|
||||
"gatekeeperAssess": false,
|
||||
"entitlements": "build/entitlements.mac.plist",
|
||||
"entitlementsInherit": "build/entitlements.mac.plist",
|
||||
"notarize": false,
|
||||
"identity": null,
|
||||
"type": "distribution",
|
||||
"binaries": [
|
||||
"Contents/MacOS/AlgerMusicPlayer"
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"icon": "resources/icon.ico",
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis",
|
||||
"arch": [
|
||||
"x64",
|
||||
"ia32",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"artifactName": "${productName}-${version}-win-${arch}.${ext}",
|
||||
"requestedExecutionLevel": "asInvoker"
|
||||
},
|
||||
"linux": {
|
||||
"icon": "resources/icon.png",
|
||||
"target": [
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "deb",
|
||||
"arch": [
|
||||
"x64",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"artifactName": "${productName}-${version}-linux-${arch}.${ext}",
|
||||
"category": "Audio",
|
||||
"maintainer": "Alger <algerkc@qq.com>"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"installerIcon": "resources/icon.ico",
|
||||
"uninstallerIcon": "resources/icon.ico",
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"shortcutName": "AlgerMusicPlayer",
|
||||
"include": "build/installer.nsh"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
||||
|
||||
7
public/css/animate.css
vendored
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
resources/favicon.ico
Normal file
|
After Width: | Height: | Size: 178 KiB |
486
resources/html/remote-control.html
Normal file
@@ -0,0 +1,486 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>AlgerMusicPlayer 远程控制</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #007AFF;
|
||||
--secondary-color: #5AC8FA;
|
||||
--success-color: #4CD964;
|
||||
--danger-color: #FF3B30;
|
||||
--warning-color: #FF9500;
|
||||
--light-gray: #F2F2F7;
|
||||
--medium-gray: #E5E5EA;
|
||||
--dark-gray: #8E8E93;
|
||||
--text-color: #000000;
|
||||
--text-secondary: #6C6C6C;
|
||||
--background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--primary-color: #0A84FF;
|
||||
--secondary-color: #64D2FF;
|
||||
--success-color: #30D158;
|
||||
--danger-color: #FF453A;
|
||||
--warning-color: #FF9F0A;
|
||||
--light-gray: #1C1C1E;
|
||||
--medium-gray: #2C2C2E;
|
||||
--dark-gray: #8E8E93;
|
||||
--text-color: #FFFFFF;
|
||||
--text-secondary: #AEAEB2;
|
||||
--background-color: #000000;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
background-color: var(--light-gray);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background-color: var(--background-color);
|
||||
padding: 12px 16px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid var(--medium-gray);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 540px;
|
||||
margin: 0 auto;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--background-color);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.song-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.song-cover {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
||||
background-color: var(--medium-gray);
|
||||
}
|
||||
|
||||
.song-details {
|
||||
margin-left: 16px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.song-details h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-color);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.song-details p {
|
||||
margin: 4px 0 0;
|
||||
font-size: 15px;
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.play-state {
|
||||
font-size: 14px;
|
||||
color: var(--primary-color);
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.play-state::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.playing .play-state::before {
|
||||
background-color: var(--success-color);
|
||||
}
|
||||
|
||||
.paused .play-state::before {
|
||||
background-color: var(--warning-color);
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.extra-controls {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--background-color);
|
||||
color: var(--primary-color);
|
||||
border: 1px solid var(--medium-gray);
|
||||
padding: 16px 0;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: scale(0.97);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--primary-color);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.btn:active::before {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.btn svg {
|
||||
margin-bottom: 8px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: var(--primary-color);
|
||||
}
|
||||
|
||||
.btn-play svg {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
text-align: center;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
background-color: var(--background-color);
|
||||
border-top: 1px solid var(--medium-gray);
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
display: inline-block;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.status-message.fade {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.refresh-button {
|
||||
color: var(--primary-color);
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 350px) {
|
||||
.controls, .extra-controls {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>AlgerMusicPlayer 远程控制</h1>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="card" id="songInfoCard">
|
||||
<div class="song-info">
|
||||
<img id="songCover" class="song-cover" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%238E8E93'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z'/%3E%3C/svg%3E" alt="封面">
|
||||
<div class="song-details">
|
||||
<h2 id="songTitle">未在播放</h2>
|
||||
<p id="songArtist">--</p>
|
||||
<div class="play-state" id="playState">未播放</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="controls">
|
||||
<button id="prevBtn" class="btn">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/>
|
||||
</svg>
|
||||
上一首
|
||||
</button>
|
||||
<button id="playBtn" class="btn btn-play">
|
||||
<svg id="playIcon" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
播放/暂停
|
||||
</button>
|
||||
<button id="nextBtn" class="btn">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/>
|
||||
</svg>
|
||||
下一首
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="extra-controls">
|
||||
<button id="volumeDownBtn" class="btn">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"/>
|
||||
</svg>
|
||||
音量-
|
||||
</button>
|
||||
<button id="volumeUpBtn" class="btn">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/>
|
||||
</svg>
|
||||
音量+
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="extra-controls">
|
||||
<button id="favoriteBtn" class="btn">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
||||
</svg>
|
||||
收藏
|
||||
</button>
|
||||
<button id="refreshBtn" class="btn">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>
|
||||
</svg>
|
||||
刷新
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-bar">
|
||||
<span id="status" class="status-message">准备就绪</span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 页面加载完成后执行
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 获取DOM元素
|
||||
const songInfoCard = document.getElementById('songInfoCard');
|
||||
const songTitle = document.getElementById('songTitle');
|
||||
const songArtist = document.getElementById('songArtist');
|
||||
const songCover = document.getElementById('songCover');
|
||||
const playState = document.getElementById('playState');
|
||||
const playBtn = document.getElementById('playBtn');
|
||||
const playIcon = document.getElementById('playIcon');
|
||||
const prevBtn = document.getElementById('prevBtn');
|
||||
const nextBtn = document.getElementById('nextBtn');
|
||||
const favoriteBtn = document.getElementById('favoriteBtn');
|
||||
const volumeUpBtn = document.getElementById('volumeUpBtn');
|
||||
const volumeDownBtn = document.getElementById('volumeDownBtn');
|
||||
const refreshBtn = document.getElementById('refreshBtn');
|
||||
const status = document.getElementById('status');
|
||||
|
||||
let isPlaying = false;
|
||||
|
||||
// 显示状态消息并淡出
|
||||
function showStatus(message, autoClear = true) {
|
||||
status.textContent = message;
|
||||
status.classList.remove('fade');
|
||||
|
||||
if (autoClear) {
|
||||
setTimeout(() => {
|
||||
status.classList.add('fade');
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新播放/暂停图标
|
||||
function updatePlayIcon() {
|
||||
if (isPlaying) {
|
||||
playIcon.innerHTML = '<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>';
|
||||
} else {
|
||||
playIcon.innerHTML = '<path d="M8 5v14l11-7z"/>';
|
||||
}
|
||||
}
|
||||
|
||||
// 更新状态的函数
|
||||
async function updateStatus() {
|
||||
try {
|
||||
showStatus('获取播放状态...', false);
|
||||
|
||||
const response = await fetch('/api/status');
|
||||
const data = await response.json();
|
||||
|
||||
// 更新播放状态
|
||||
isPlaying = data.isPlaying;
|
||||
updatePlayIcon();
|
||||
|
||||
// 更新UI
|
||||
if (data.currentSong) {
|
||||
songTitle.textContent = data.currentSong.name || '未知歌曲';
|
||||
|
||||
if (data.currentSong.ar && data.currentSong.ar.length) {
|
||||
songArtist.textContent = data.currentSong.ar.map(a => a.name).join(', ');
|
||||
} else if (data.currentSong.artists && data.currentSong.artists.length) {
|
||||
songArtist.textContent = data.currentSong.artists.map(a => a.name).join(', ');
|
||||
} else {
|
||||
songArtist.textContent = '未知艺术家';
|
||||
}
|
||||
|
||||
if (data.currentSong.picUrl) {
|
||||
songCover.src = data.currentSong.picUrl;
|
||||
}
|
||||
} else {
|
||||
songTitle.textContent = '未在播放';
|
||||
songArtist.textContent = '--';
|
||||
songCover.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%238E8E93'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 14.5c-2.49 0-4.5-2.01-4.5-4.5S9.51 7.5 12 7.5s4.5 2.01 4.5 4.5-2.01 4.5-4.5 4.5zm0-5.5c-.55 0-1 .45-1 1s.45 1 1 1 1-.45 1-1-.45-1-1-1z'/%3E%3C/svg%3E";
|
||||
}
|
||||
|
||||
// 更新播放状态
|
||||
playState.textContent = isPlaying ? '正在播放' : '已暂停';
|
||||
songInfoCard.className = isPlaying ? 'card playing' : 'card paused';
|
||||
|
||||
showStatus('已更新', true);
|
||||
} catch (error) {
|
||||
console.error('获取状态失败:', error);
|
||||
showStatus('获取状态失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 发送命令的函数
|
||||
async function sendCommand(endpoint) {
|
||||
try {
|
||||
showStatus('发送命令中...', false);
|
||||
const response = await fetch('/api/' + endpoint, { method: 'POST' });
|
||||
const data = await response.json();
|
||||
|
||||
showStatus(data.message || '命令已发送');
|
||||
|
||||
// 稍等后更新状态
|
||||
setTimeout(updateStatus, 500);
|
||||
} catch (error) {
|
||||
console.error('发送命令失败:', error);
|
||||
showStatus('发送命令失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定按钮事件
|
||||
playBtn.addEventListener('click', () => sendCommand('toggle-play'));
|
||||
prevBtn.addEventListener('click', () => sendCommand('prev'));
|
||||
nextBtn.addEventListener('click', () => sendCommand('next'));
|
||||
favoriteBtn.addEventListener('click', () => sendCommand('toggle-favorite'));
|
||||
volumeUpBtn.addEventListener('click', () => sendCommand('volume-up'));
|
||||
volumeDownBtn.addEventListener('click', () => sendCommand('volume-down'));
|
||||
refreshBtn.addEventListener('click', updateStatus);
|
||||
|
||||
// 初始加载状态
|
||||
updateStatus();
|
||||
|
||||
// 每1秒更新一次状态
|
||||
setInterval(updateStatus, 1000);
|
||||
|
||||
// 添加触摸反馈
|
||||
const buttons = document.querySelectorAll('.btn');
|
||||
buttons.forEach(btn => {
|
||||
btn.addEventListener('touchstart', function() {
|
||||
this.style.transform = 'scale(0.97)';
|
||||
this.style.opacity = '0.7';
|
||||
});
|
||||
|
||||
btn.addEventListener('touchend', function() {
|
||||
this.style.transform = 'scale(1)';
|
||||
this.style.opacity = '1';
|
||||
});
|
||||
});
|
||||
|
||||
// 检测深色模式变化
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
updateStatus();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
resources/icon.icns
Normal file
BIN
resources/icon.ico
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
resources/icon.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
resources/icon_16x16.png
Normal file
|
After Width: | Height: | Size: 626 B |
BIN
resources/icons/next.png
Normal file
|
After Width: | Height: | Size: 269 B |
BIN
resources/icons/pause.png
Normal file
|
After Width: | Height: | Size: 141 B |
BIN
resources/icons/play.png
Normal file
|
After Width: | Height: | Size: 251 B |
BIN
resources/icons/prev.png
Normal file
|
After Width: | Height: | Size: 289 B |
10
resources/manifest.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Alger Music PWA",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./icon.png",
|
||||
"type": "image/png",
|
||||
"sizes": "256x256"
|
||||
}
|
||||
]
|
||||
}
|
||||
22
src/App.vue
@@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<n-config-provider :theme="darkTheme">
|
||||
<router-view></router-view>
|
||||
</n-config-provider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { darkTheme } from 'naive-ui'
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped >
|
||||
div {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.app {
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,45 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
import { IHotSinger } from "@/type/singer";
|
||||
import { ISearchKeyword, IHotSearch } from "@/type/search";
|
||||
import { IPlayListSort } from "@/type/playlist";
|
||||
import { IRecommendMusic } from "@/type/music";
|
||||
import { IAlbumNew } from "@/type/album";
|
||||
|
||||
interface IHotSingerParams {
|
||||
offset: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
interface IRecommendMusicParams {
|
||||
limit: number;
|
||||
}
|
||||
|
||||
// 获取热门歌手
|
||||
export const getHotSinger = (params: IHotSingerParams) => {
|
||||
return request.get<IHotSinger>("/top/artists", { params });
|
||||
};
|
||||
|
||||
// 获取搜索推荐词
|
||||
export const getSearchKeyword = () => {
|
||||
return request.get<ISearchKeyword>("/search/default");
|
||||
};
|
||||
|
||||
// 获取热门搜索
|
||||
export const getHotSearch = () => {
|
||||
return request.get<IHotSearch>("/search/hot/detail");
|
||||
};
|
||||
|
||||
// 获取歌单分类
|
||||
export const getPlaylistCategory = () => {
|
||||
return request.get<IPlayListSort>("/playlist/catlist");
|
||||
};
|
||||
|
||||
// 获取推荐音乐
|
||||
export const getRecommendMusic = (params: IRecommendMusicParams) => {
|
||||
return request.get<IRecommendMusic>("/personalized/newsong", { params });
|
||||
};
|
||||
|
||||
// 获取最新专辑推荐
|
||||
export const getNewAlbum = () => {
|
||||
return request.get<IAlbumNew>("/album/newest");
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
import { IList } from "@/type/list";
|
||||
import type { IListDetail } from "@/type/listDetail";
|
||||
|
||||
interface IListByTagParams {
|
||||
tag: string;
|
||||
before: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
interface IListByCatParams {
|
||||
cat: string;
|
||||
offset: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
// 根据tag 获取歌单列表
|
||||
export function getListByTag(params: IListByTagParams) {
|
||||
return request.get<IList>("/top/playlist/highquality", { params: params });
|
||||
}
|
||||
|
||||
// 根据cat 获取歌单列表
|
||||
export function getListByCat(params: IListByCatParams) {
|
||||
return request.get("/top/playlist", {
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取推荐歌单
|
||||
export function getRecommendList(limit: number = 30) {
|
||||
return request.get("/personalized", { params: { limit } });
|
||||
}
|
||||
|
||||
// 获取歌单详情
|
||||
export function getListDetail(id: number | string) {
|
||||
return request.get<IListDetail>("/playlist/detail", { params: { id } });
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { IPlayMusicUrl } from "@/type/music"
|
||||
import { ILyric } from "@/type/lyric"
|
||||
import request from "@/utils/request"
|
||||
import requestMusic from "@/utils/request_music"
|
||||
// 根据音乐Id获取音乐播放URl
|
||||
export const getMusicUrl = (id: number) => {
|
||||
return request.get<IPlayMusicUrl>("/song/url", { params: { id: id } })
|
||||
}
|
||||
|
||||
// 根据音乐Id获取音乐歌词
|
||||
export const getMusicLrc = (id: number) => {
|
||||
return request.get<ILyric>("/lyric", { params: { id: id } })
|
||||
}
|
||||
|
||||
export const getParsingMusicUrl = (id: number) => {
|
||||
return requestMusic.get<any>("/music", { params: { id: id } })
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import request from "@/utils/request"
|
||||
import { ISearchDetail } from "@/type/search"
|
||||
|
||||
// 搜索内容
|
||||
export const getSearch = (keywords: any) => {
|
||||
return request.get<any>("/cloudsearch", {
|
||||
params: { keywords: keywords, type: 1 },
|
||||
})
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
// /user/detail
|
||||
export function getUserDetail(uid: number) {
|
||||
return request.get("/user/detail", { params: { uid } });
|
||||
}
|
||||
|
||||
// /user/playlist
|
||||
export function getUserPlaylist(uid: number) {
|
||||
return request.get("/user/playlist", { params: { uid } });
|
||||
}
|
||||
|
||||
// 播放历史
|
||||
// /user/record?uid=32953014&type=1
|
||||
export function getUserRecord(uid: number, type: number = 0) {
|
||||
return request.get("/user/record", { params: { uid, type } });
|
||||
}
|
||||
|
Before Width: | Height: | Size: 6.7 KiB |
@@ -1,79 +0,0 @@
|
||||
<template>
|
||||
<!-- 歌单分类列表 -->
|
||||
<div class="play-list-type">
|
||||
<div class="title" :class="setAnimationClass('animate__fadeInLeft')">歌单分类</div>
|
||||
<n-layout>
|
||||
<template v-for="(item, index) in playlistCategory?.sub" :key="item.name">
|
||||
<span
|
||||
class="play-list-type-item"
|
||||
:class="setAnimationClass('animate__bounceIn')"
|
||||
:style="setAnimationDelay(index <= 19 ? index : index - 19)"
|
||||
v-show="isShowAllPlaylistCategory || index <= 19"
|
||||
@click="handleClickPlaylistType(item.name)"
|
||||
>{{ item.name }}</span>
|
||||
</template>
|
||||
<div
|
||||
class="play-list-type-showall"
|
||||
:class="setAnimationClass('animate__bounceIn')"
|
||||
:style="
|
||||
setAnimationDelay(
|
||||
!isShowAllPlaylistCategory
|
||||
? 25
|
||||
: playlistCategory?.sub.length || 100 + 30
|
||||
)
|
||||
"
|
||||
@click="isShowAllPlaylistCategory = !isShowAllPlaylistCategory"
|
||||
>{{ !isShowAllPlaylistCategory ? "显示全部" : "隐藏一些" }}</div>
|
||||
</n-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { getPlaylistCategory } from "@/api/home";
|
||||
import type { IPlayListSort } from "@/type/playlist";
|
||||
import { setAnimationDelay, setAnimationClass } from "@/utils";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
// 歌单分类
|
||||
const playlistCategory = ref<IPlayListSort>();
|
||||
// 是否显示全部歌单分类
|
||||
const isShowAllPlaylistCategory = ref<boolean>(false);
|
||||
|
||||
// 加载歌单分类
|
||||
const loadPlaylistCategory = async () => {
|
||||
const { data } = await getPlaylistCategory();
|
||||
playlistCategory.value = data;
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
const handleClickPlaylistType = (type: any) => {
|
||||
router.push({
|
||||
path: "/list",
|
||||
query: {
|
||||
type: type,
|
||||
}
|
||||
});
|
||||
};
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
loadPlaylistCategory();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
@apply text-lg font-bold mb-4;
|
||||
}
|
||||
.play-list-type {
|
||||
width: 250px;
|
||||
@apply mx-6;
|
||||
&-item,
|
||||
&-showall {
|
||||
@apply py-2 px-3 mr-3 mb-3 inline-block border border-gray-700 rounded-xl cursor-pointer hover:bg-green-600 transition;
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
&-showall {
|
||||
@apply block text-center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<div class="recommend-album">
|
||||
<div class="title" :class="setAnimationClass('animate__fadeInLeft')">最新专辑</div>
|
||||
<div class="recommend-album-list">
|
||||
<template v-for="(item,index) in albumData?.albums" :key="item.id">
|
||||
<div
|
||||
v-if="index < 6"
|
||||
class="recommend-album-list-item"
|
||||
:class="setAnimationClass('animate__backInUp')"
|
||||
:style="setAnimationDelay(index, 100)"
|
||||
>
|
||||
<n-image
|
||||
class="recommend-album-list-item-img"
|
||||
:src="getImgUrl( item.blurPicUrl, '200y200')"
|
||||
lazy
|
||||
preview-disabled
|
||||
/>
|
||||
<div class="recommend-album-list-item-content">{{ item.name }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getNewAlbum } from "@/api/home"
|
||||
import { ref, onMounted } from "vue";
|
||||
import type { IAlbumNew } from "@/type/album"
|
||||
import { setAnimationClass, setAnimationDelay, getImgUrl } from "@/utils";
|
||||
|
||||
|
||||
const albumData = ref<IAlbumNew>()
|
||||
const loadAlbumList = async () => {
|
||||
const { data } = await getNewAlbum();
|
||||
albumData.value = data
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadAlbumList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.recommend-album {
|
||||
@apply flex-1 mx-5;
|
||||
.title {
|
||||
@apply text-lg font-bold mb-4;
|
||||
}
|
||||
|
||||
.recommend-album-list {
|
||||
@apply grid grid-cols-2 grid-rows-3 gap-2;
|
||||
&-item {
|
||||
@apply rounded-xl overflow-hidden relative;
|
||||
&-img {
|
||||
@apply rounded-xl transition w-full h-full;
|
||||
}
|
||||
&:hover img {
|
||||
filter: brightness(50%);
|
||||
}
|
||||
&-content {
|
||||
@apply w-full h-full opacity-0 transition absolute z-10 top-0 left-0 p-4 text-xl bg-opacity-60 bg-black;
|
||||
}
|
||||
&-content:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,85 +0,0 @@
|
||||
<template>
|
||||
<!-- 推荐歌手 -->
|
||||
<div class="recommend-singer">
|
||||
<div class="recommend-singer-list">
|
||||
<div
|
||||
class="recommend-singer-item relative"
|
||||
:class="setAnimationClass('animate__backInRight')"
|
||||
v-for="(item, index) in hotSingerData?.artists"
|
||||
:style="setAnimationDelay(index, 100)"
|
||||
:key="item.id"
|
||||
>
|
||||
<div
|
||||
:style="setBackgroundImg(getImgUrl(item.picUrl,'300y300'))"
|
||||
class="recommend-singer-item-bg"
|
||||
></div>
|
||||
<div
|
||||
class="recommend-singer-item-count p-2 text-base text-gray-200 z-10"
|
||||
>{{ item.musicSize }}首</div>
|
||||
<div class="recommend-singer-item-info z-10">
|
||||
<div class="recommend-singer-item-info-play" @click="toSearchSinger(item.name)">
|
||||
<i class="iconfont icon-playfill text-xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<div class="recommend-singer-item-info-name">{{ item.name }}</div>
|
||||
<div class="recommend-singer-item-info-name">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { setBackgroundImg, setAnimationDelay, setAnimationClass,getImgUrl } from "@/utils";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { getHotSinger } from "@/api/home";
|
||||
import type { IHotSinger } from "@/type/singer";
|
||||
import router from "@/router";
|
||||
|
||||
// 歌手信息
|
||||
const hotSingerData = ref<IHotSinger>();
|
||||
|
||||
//加载推荐歌手
|
||||
const loadSingerList = async () => {
|
||||
const { data } = await getHotSinger({ offset: 0, limit: 5 });
|
||||
hotSingerData.value = data;
|
||||
};
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
loadSingerList();
|
||||
});
|
||||
|
||||
|
||||
const toSearchSinger = (keyword: string) => {
|
||||
router.push({
|
||||
path: "/search",
|
||||
query: {
|
||||
keyword: keyword,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.recommend-singer {
|
||||
&-list {
|
||||
@apply flex;
|
||||
height: 350px;
|
||||
}
|
||||
&-item {
|
||||
@apply flex-1 h-full rounded-3xl p-5 mr-5 flex flex-col justify-between;
|
||||
&-bg {
|
||||
@apply bg-gray-900 bg-no-repeat bg-cover bg-center rounded-3xl absolute w-full h-full top-0 left-0 z-0;
|
||||
filter: brightness(80%);
|
||||
}
|
||||
&-info {
|
||||
@apply flex items-center p-2;
|
||||
&-play {
|
||||
@apply w-12 h-12 bg-green-500 rounded-full flex justify-center items-center hover:bg-green-600 cursor-pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,68 +0,0 @@
|
||||
<template>
|
||||
<div class="recommend-music">
|
||||
<div class="title" :class="setAnimationClass('animate__fadeInLeft')">
|
||||
本周最热音乐
|
||||
</div>
|
||||
<div
|
||||
class="recommend-music-list"
|
||||
:class="setAnimationClass('animate__bounceInUp')"
|
||||
v-show="recommendMusic?.result"
|
||||
>
|
||||
<!-- 推荐音乐列表 -->
|
||||
<template v-for="(item, index) in recommendMusic?.result" :key="item.id">
|
||||
<div
|
||||
:class="setAnimationClass('animate__bounceInUp')"
|
||||
:style="setAnimationDelay(index, 100)"
|
||||
>
|
||||
<song-item :item="item" @play="handlePlay" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { getRecommendMusic } from '@/api/home'
|
||||
import type { IRecommendMusic } from '@/type/music'
|
||||
import { setAnimationClass, setAnimationDelay } from '@/utils'
|
||||
import SongItem from './common/SongItem.vue'
|
||||
import { useStore } from 'vuex'
|
||||
|
||||
const store = useStore();
|
||||
// 推荐歌曲
|
||||
const recommendMusic = ref<IRecommendMusic>()
|
||||
|
||||
// 加载推荐歌曲
|
||||
const loadRecommendMusic = async () => {
|
||||
const { data } = await getRecommendMusic({ limit: 10 })
|
||||
recommendMusic.value = data
|
||||
}
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
loadRecommendMusic()
|
||||
})
|
||||
|
||||
const handlePlay = (item: any) => {
|
||||
const musicIndex = (recommendMusic.value?.result.findIndex((music: any) => music.id == item.id) || 0) + 1
|
||||
store.commit('setPlayList', recommendMusic.value?.result.slice(musicIndex))
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
@apply text-lg font-bold mb-4;
|
||||
}
|
||||
.recommend-music {
|
||||
@apply flex-auto;
|
||||
// width: 530px;
|
||||
.text-ellipsis {
|
||||
width: 100%;
|
||||
}
|
||||
&-list {
|
||||
@apply rounded-3xl p-2 w-full border border-gray-700;
|
||||
background-color: #0d0d0d;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,43 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { setAnimationClass, setAnimationDelay } from "@/utils";
|
||||
|
||||
const props = defineProps({
|
||||
showPop: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
})
|
||||
|
||||
const musicFullClass = computed(() => {
|
||||
if (props.showPop) {
|
||||
return setAnimationClass('animate__fadeInUp')
|
||||
} else {
|
||||
return setAnimationClass('animate__fadeOutDown')
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pop-page" v-show="props.showPop" :class="musicFullClass">
|
||||
<i class="iconfont icon-icon_error close" v-if="props.showClose" @click="close()"></i>
|
||||
<img src="http://code.myalger.top/2000*2000.jpg,f054f0,0f2255" />
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pop-page {
|
||||
height: 800px;
|
||||
@apply absolute top-4 left-0 w-full;
|
||||
background-color: #000000f0;
|
||||
.close {
|
||||
@apply absolute top-4 right-4 cursor-pointer text-white text-3xl;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<div class="recommend-music-list-item">
|
||||
<n-image
|
||||
:src="getImgUrl( item.picUrl, '40y40')"
|
||||
class="recommend-music-list-item-img"
|
||||
lazy
|
||||
preview-disabled
|
||||
/>
|
||||
<div class="recommend-music-list-item-content">
|
||||
<div class="recommend-music-list-item-content-title">
|
||||
<n-ellipsis class="text-ellipsis" line-clamp="1">{{
|
||||
item.name
|
||||
}}</n-ellipsis>
|
||||
</div>
|
||||
<div class="recommend-music-list-item-content-name">
|
||||
<n-ellipsis class="text-ellipsis" line-clamp="1">
|
||||
<span
|
||||
v-for="(artists, artistsindex) in item.song.artists"
|
||||
:key="artistsindex"
|
||||
>{{ artists.name
|
||||
}}{{
|
||||
artistsindex < item.song.artists.length - 1 ? ' / ' : ''
|
||||
}}</span
|
||||
>
|
||||
</n-ellipsis>
|
||||
</div>
|
||||
</div>
|
||||
<div class="recommend-music-list-item-operating">
|
||||
<div class="recommend-music-list-item-operating-like">
|
||||
<i class="iconfont icon-likefill"></i>
|
||||
</div>
|
||||
<div
|
||||
class="recommend-music-list-item-operating-play bg-black"
|
||||
:class="isPlaying ? 'bg-green-600' : ''"
|
||||
@click="playMusicEvent(item)"
|
||||
>
|
||||
<i v-if="isPlaying && play" class="iconfont icon-stop"></i>
|
||||
<i v-else class="iconfont icon-playfill"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useStore } from 'vuex'
|
||||
import type { SongResult } from '@/type/music'
|
||||
import { computed } from 'vue'
|
||||
import { getImgUrl } from '@/utils'
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const play = computed(() => store.state.play as boolean)
|
||||
|
||||
const playMusic = computed(() => store.state.playMusic)
|
||||
|
||||
// 判断是否为正在播放的音乐
|
||||
const isPlaying = computed(() => {
|
||||
return playMusic.value.id == props.item.id
|
||||
})
|
||||
|
||||
const emits = defineEmits(['play'])
|
||||
|
||||
// 播放音乐 设置音乐详情 打开音乐底栏
|
||||
const playMusicEvent = (item: any) => {
|
||||
store.commit('setPlay', item)
|
||||
store.commit('setIsPlay', true)
|
||||
store.state.playListIndex = 0
|
||||
emits('play', item)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.text-ellipsis {
|
||||
width: 100%;
|
||||
}
|
||||
.recommend-music-list-item {
|
||||
@apply rounded-3xl p-3 flex items-center hover:bg-gray-800 transition;
|
||||
&-img {
|
||||
@apply w-12 h-12 rounded-2xl mr-4;
|
||||
}
|
||||
&-content {
|
||||
@apply flex-1;
|
||||
&-title {
|
||||
@apply text-base text-white;
|
||||
}
|
||||
&-name {
|
||||
@apply text-xs;
|
||||
@apply text-gray-400;
|
||||
}
|
||||
}
|
||||
&-operating {
|
||||
@apply flex items-center pl-4 rounded-full border border-gray-700;
|
||||
background-color: #0d0d0d;
|
||||
.iconfont {
|
||||
@apply text-xl;
|
||||
}
|
||||
.icon-likefill {
|
||||
color: #868686;
|
||||
@apply text-xl hover:text-red-600 transition;
|
||||
}
|
||||
&-like {
|
||||
@apply mr-2 cursor-pointer;
|
||||
}
|
||||
&-play {
|
||||
@apply cursor-pointer border border-gray-500 rounded-full w-10 h-10 flex justify-center items-center hover:bg-green-600 transition;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,104 +0,0 @@
|
||||
import { getMusicLrc } from '@/api/music'
|
||||
import { ILyric } from '@/type/lyric'
|
||||
import { ref } from 'vue'
|
||||
|
||||
interface ILrcData {
|
||||
text: string
|
||||
trText: string
|
||||
}
|
||||
|
||||
const lrcData = ref<ILyric>()
|
||||
const newLrcIndex = ref<number>(0)
|
||||
const lrcArray = ref<Array<ILrcData>>([])
|
||||
const lrcTimeArray = ref<Array<Number>>([])
|
||||
|
||||
const parseTime = (timeString: string) => {
|
||||
const [minutes, seconds] = timeString.split(':')
|
||||
return parseInt(minutes) * 60 + parseFloat(seconds)
|
||||
}
|
||||
|
||||
const TIME_REGEX = /(\d{2}:\d{2}(\.\d*)?)/g
|
||||
const LRC_REGEX = /(\[(\d{2}):(\d{2})(\.(\d*))?\])/g
|
||||
|
||||
function parseLyricLine(lyricLine: string) {
|
||||
// [00:00.00] 作词 : 长友美知惠/
|
||||
const timeText = lyricLine.match(TIME_REGEX)?.[0] || ''
|
||||
const time = parseTime(timeText)
|
||||
const text = lyricLine.replace(LRC_REGEX, '').trim()
|
||||
return { time, text }
|
||||
}
|
||||
|
||||
interface ILyricText {
|
||||
text: string
|
||||
trText: string
|
||||
}
|
||||
|
||||
function parseLyrics(lyricsString: string) {
|
||||
const lines = lyricsString.split('\n')
|
||||
const lyrics: Array<ILyricText> = []
|
||||
const times: number[] = []
|
||||
lines.forEach((line) => {
|
||||
const { time, text } = parseLyricLine(line)
|
||||
times.push(time)
|
||||
lyrics.push({ text, trText: '' })
|
||||
})
|
||||
return { lyrics, times }
|
||||
}
|
||||
|
||||
const loadLrc = async (playMusicId: number): Promise<void> => {
|
||||
try {
|
||||
const { data } = await getMusicLrc(playMusicId)
|
||||
const { lyrics, times } = parseLyrics(data.lrc.lyric)
|
||||
lrcTimeArray.value = times
|
||||
lrcArray.value = lyrics
|
||||
} catch (err) {
|
||||
console.error('err', err)
|
||||
}
|
||||
}
|
||||
|
||||
// 歌词矫正时间Correction time
|
||||
const correctionTime = ref(0)
|
||||
|
||||
// 增加矫正时间
|
||||
const addCorrectionTime = (time: number) => {
|
||||
correctionTime.value += time
|
||||
}
|
||||
|
||||
// 减少矫正时间
|
||||
const reduceCorrectionTime = (time: number) => {
|
||||
correctionTime.value -= time
|
||||
}
|
||||
|
||||
const isCurrentLrc = (index: any, time: number) => {
|
||||
const currentTime = Number(lrcTimeArray.value[index])
|
||||
const nextTime = Number(lrcTimeArray.value[index + 1])
|
||||
const nowTime = time + correctionTime.value
|
||||
const isTrue = nowTime > currentTime && nowTime < nextTime
|
||||
if (isTrue) {
|
||||
newLrcIndex.value = index
|
||||
}
|
||||
return isTrue
|
||||
}
|
||||
|
||||
const nowTime = ref(0)
|
||||
const allTime = ref(0)
|
||||
|
||||
// 设置当前播放时间
|
||||
const setAudioTime = (index: any, audio: HTMLAudioElement) => {
|
||||
audio.currentTime = lrcTimeArray.value[index] as number
|
||||
audio.play()
|
||||
}
|
||||
|
||||
export {
|
||||
lrcData,
|
||||
lrcArray,
|
||||
lrcTimeArray,
|
||||
newLrcIndex,
|
||||
loadLrc,
|
||||
isCurrentLrc,
|
||||
addCorrectionTime,
|
||||
reduceCorrectionTime,
|
||||
setAudioTime,
|
||||
nowTime,
|
||||
allTime,
|
||||
}
|
||||
5
src/i18n/lang/en-US/artist.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
hotSongs: 'Hot Songs',
|
||||
albums: 'Albums',
|
||||
description: 'Artist Introduction'
|
||||
};
|
||||
56
src/i18n/lang/en-US/common.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
export default {
|
||||
play: 'Play',
|
||||
next: 'Next',
|
||||
previous: 'Previous',
|
||||
volume: 'Volume',
|
||||
settings: 'Settings',
|
||||
search: 'Search',
|
||||
loading: 'Loading...',
|
||||
loadingMore: 'Loading more...',
|
||||
alipay: 'Alipay',
|
||||
wechat: 'WeChat Pay',
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
show: 'Show',
|
||||
hide: 'Hide',
|
||||
confirm: 'Confirm',
|
||||
cancel: 'Cancel',
|
||||
configure: 'Configure',
|
||||
open: 'Open',
|
||||
modify: 'Modify',
|
||||
success: 'Operation Successful',
|
||||
error: 'Operation Failed',
|
||||
warning: 'Warning',
|
||||
info: 'Info',
|
||||
save: 'Save',
|
||||
delete: 'Delete',
|
||||
refresh: 'Refresh',
|
||||
retry: 'Retry',
|
||||
reset: 'Reset',
|
||||
copySuccess: 'Copied to clipboard',
|
||||
copyFailed: 'Copy failed',
|
||||
validation: {
|
||||
required: 'This field is required',
|
||||
invalidInput: 'Invalid input',
|
||||
selectRequired: 'Please select an option',
|
||||
numberRange: 'Please enter a number between {min} and {max}',
|
||||
ipAddress: 'Please enter a valid IP address',
|
||||
portNumber: 'Please enter a valid port number (1-65535)'
|
||||
},
|
||||
viewMore: 'View More',
|
||||
noMore: 'No more',
|
||||
expand: 'Expand',
|
||||
collapse: 'Collapse',
|
||||
songCount: '{count} songs',
|
||||
tray: {
|
||||
show: 'Show',
|
||||
quit: 'Quit',
|
||||
playPause: 'Play/Pause',
|
||||
prev: 'Previous',
|
||||
next: 'Next',
|
||||
pause: 'Pause',
|
||||
play: 'Play',
|
||||
favorite: 'Favorite'
|
||||
},
|
||||
language: 'Language'
|
||||
};
|
||||
120
src/i18n/lang/en-US/comp.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
export default {
|
||||
installApp: {
|
||||
description: 'Install the application for a better experience',
|
||||
noPrompt: 'Do not prompt again',
|
||||
install: 'Install now',
|
||||
cancel: 'Cancel',
|
||||
download: 'Download',
|
||||
downloadFailed: 'Download failed',
|
||||
downloadComplete: 'Download complete',
|
||||
downloadProblem: 'Download problem? Go to',
|
||||
downloadProblemLinkText: 'Download the latest version'
|
||||
},
|
||||
playlistDrawer: {
|
||||
title: 'Add to playlist',
|
||||
createPlaylist: 'Create new playlist',
|
||||
cancelCreate: 'Cancel create',
|
||||
create: 'Create',
|
||||
playlistName: 'Playlist name',
|
||||
privatePlaylist: 'Private playlist',
|
||||
publicPlaylist: 'Public playlist',
|
||||
createSuccess: 'Playlist created successfully',
|
||||
createFailed: 'Playlist creation failed',
|
||||
addSuccess: 'Song added successfully',
|
||||
addFailed: 'Song addition failed',
|
||||
private: 'Private',
|
||||
public: 'Public',
|
||||
count: 'songs',
|
||||
loginFirst: 'Please login first',
|
||||
getPlaylistFailed: 'Get playlist failed',
|
||||
inputPlaylistName: 'Please enter the playlist name'
|
||||
},
|
||||
update: {
|
||||
title: 'New version found',
|
||||
currentVersion: 'Current version',
|
||||
cancel: 'Do not update',
|
||||
prepareDownload: 'Preparing to download...',
|
||||
downloading: 'Downloading...',
|
||||
nowUpdate: 'Update now',
|
||||
downloadFailed: 'Download failed, please try again or download manually',
|
||||
startFailed: 'Start download failed, please try again or download manually',
|
||||
noDownloadUrl:
|
||||
'No suitable installation package found for the current system, please download manually',
|
||||
installConfirmTitle: 'Install Update',
|
||||
installConfirmContent: 'Do you want to close the application and install the update?',
|
||||
manualInstallTip:
|
||||
'If the installer does not open automatically after closing the application, please find the file in your download folder and open it manually.',
|
||||
yesInstall: 'Install Now',
|
||||
noThanks: 'Later',
|
||||
fileLocation: 'File Location',
|
||||
copy: 'Copy Path',
|
||||
copySuccess: 'Path copied to clipboard',
|
||||
copyFailed: 'Copy failed',
|
||||
backgroundDownload: 'Background Download'
|
||||
},
|
||||
coffee: {
|
||||
title: 'Buy me a coffee',
|
||||
alipay: 'Alipay',
|
||||
wechat: 'Wechat',
|
||||
alipayQR: 'Alipay QR code',
|
||||
wechatQR: 'Wechat QR code',
|
||||
coffeeDesc: 'A cup of coffee, a support',
|
||||
coffeeDescLinkText: 'View more',
|
||||
qqGroup: 'QQ group: algermusic',
|
||||
messages: {
|
||||
copySuccess: 'Copied to clipboard'
|
||||
},
|
||||
donateList: 'Buy me a coffee'
|
||||
},
|
||||
playlistType: {
|
||||
title: 'Playlist Category',
|
||||
showAll: 'Show all',
|
||||
hide: 'Hide some'
|
||||
},
|
||||
recommendAlbum: {
|
||||
title: 'Latest Album'
|
||||
},
|
||||
recommendSinger: {
|
||||
title: 'Daily Recommendation',
|
||||
songlist: 'Daily Recommendation List'
|
||||
},
|
||||
recommendSonglist: {
|
||||
title: 'Weekly Hot Music'
|
||||
},
|
||||
searchBar: {
|
||||
login: 'Login',
|
||||
toLogin: 'To Login',
|
||||
logout: 'Logout',
|
||||
set: 'Set',
|
||||
theme: 'Theme',
|
||||
restart: 'Restart',
|
||||
refresh: 'Refresh',
|
||||
currentVersion: 'Current Version',
|
||||
searchPlaceholder: 'Search for something...'
|
||||
},
|
||||
titleBar: {
|
||||
closeTitle: 'Choose how to close',
|
||||
minimizeToTray: 'Minimize to Tray',
|
||||
exitApp: 'Exit App',
|
||||
rememberChoice: 'Remember my choice',
|
||||
closeApp: 'Close App'
|
||||
},
|
||||
userPlayList: {
|
||||
title: "{name}'s Playlist"
|
||||
},
|
||||
musicList: {
|
||||
searchSongs: 'Search Songs',
|
||||
noSearchResults: 'No search results',
|
||||
switchToNormal: 'Switch to normal layout',
|
||||
switchToCompact: 'Switch to compact layout',
|
||||
playAll: 'Play All',
|
||||
collect: 'Collect',
|
||||
collectSuccess: 'Collect Success',
|
||||
cancelCollectSuccess: 'Cancel Collect Success',
|
||||
cancelCollect: 'Cancel Collect',
|
||||
addToPlaylist: 'Add to Playlist',
|
||||
addToPlaylistSuccess: 'Add to Playlist Success',
|
||||
operationFailed: 'Operation Failed',
|
||||
songsAlreadyInPlaylist: 'Songs already in playlist'
|
||||
}
|
||||
};
|
||||
9
src/i18n/lang/en-US/donation.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
description:
|
||||
'Your donation will be used to support development and maintenance work, including but not limited to server maintenance, domain name renewal, etc.',
|
||||
message: 'You can leave your email or github name when leaving a message.',
|
||||
refresh: 'Refresh List',
|
||||
toDonateList: 'Buy me a coffee',
|
||||
title: 'Donation List',
|
||||
noMessage: 'No Message'
|
||||
};
|
||||
49
src/i18n/lang/en-US/download.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
export default {
|
||||
title: 'Download Manager',
|
||||
localMusic: 'Local Music',
|
||||
count: '{count} songs in total',
|
||||
clearAll: 'Clear All',
|
||||
tabs: {
|
||||
downloading: 'Downloading',
|
||||
downloaded: 'Downloaded'
|
||||
},
|
||||
empty: {
|
||||
noTasks: 'No download tasks',
|
||||
noDownloaded: 'No downloaded songs'
|
||||
},
|
||||
progress: {
|
||||
total: 'Total Progress: {progress}%'
|
||||
},
|
||||
status: {
|
||||
downloading: 'Downloading',
|
||||
completed: 'Completed',
|
||||
failed: 'Failed',
|
||||
unknown: 'Unknown'
|
||||
},
|
||||
artist: {
|
||||
unknown: 'Unknown Artist'
|
||||
},
|
||||
delete: {
|
||||
title: 'Delete Confirmation',
|
||||
message: 'Are you sure you want to delete "{filename}"? This action cannot be undone.',
|
||||
confirm: 'Delete',
|
||||
cancel: 'Cancel',
|
||||
success: 'Successfully deleted',
|
||||
failed: 'Failed to delete',
|
||||
fileNotFound: 'File not found or moved, removed from records',
|
||||
recordRemoved: 'Failed to delete file, but removed from records'
|
||||
},
|
||||
clear: {
|
||||
title: 'Clear Download Records',
|
||||
message:
|
||||
'Are you sure you want to clear all download records? This will not delete the actual music files, but will clear all records.',
|
||||
confirm: 'Clear',
|
||||
cancel: 'Cancel',
|
||||
success: 'Download records cleared'
|
||||
},
|
||||
message: {
|
||||
downloadComplete: '{filename} download completed',
|
||||
downloadFailed: '{filename} download failed: {error}'
|
||||
},
|
||||
loading: 'Loading...'
|
||||
};
|
||||
17
src/i18n/lang/en-US/favorite.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export default {
|
||||
title: 'Favorites',
|
||||
count: 'Total {count}',
|
||||
batchDownload: 'Batch Download',
|
||||
selectAll: 'All',
|
||||
download: 'Download ({count})',
|
||||
cancel: 'Cancel',
|
||||
emptyTip: 'No favorite songs yet',
|
||||
viewMore: 'View More',
|
||||
noMore: 'No more',
|
||||
downloadSuccess: 'Download completed',
|
||||
downloadFailed: 'Download failed',
|
||||
downloading: 'Downloading, please wait...',
|
||||
selectSongsFirst: 'Please select songs to download first',
|
||||
descending: 'Descending',
|
||||
ascending: 'Ascending'
|
||||
};
|
||||
5
src/i18n/lang/en-US/history.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
title: 'Play History',
|
||||
playCount: '{count}',
|
||||
getHistoryFailed: 'Failed to get play history'
|
||||
};
|
||||
29
src/i18n/lang/en-US/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import artist from './artist';
|
||||
import common from './common';
|
||||
import comp from './comp';
|
||||
import donation from './donation';
|
||||
import download from './download';
|
||||
import favorite from './favorite';
|
||||
import history from './history';
|
||||
import login from './login';
|
||||
import player from './player';
|
||||
import search from './search';
|
||||
import settings from './settings';
|
||||
import songItem from './songItem';
|
||||
import user from './user';
|
||||
|
||||
export default {
|
||||
common,
|
||||
donation,
|
||||
favorite,
|
||||
history,
|
||||
login,
|
||||
player,
|
||||
search,
|
||||
settings,
|
||||
songItem,
|
||||
user,
|
||||
download,
|
||||
comp,
|
||||
artist
|
||||
};
|
||||
22
src/i18n/lang/en-US/login.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export default {
|
||||
title: {
|
||||
qr: 'QR Code Login',
|
||||
phone: 'Phone Login'
|
||||
},
|
||||
qrTip: 'Scan with NetEase Cloud Music APP',
|
||||
phoneTip: 'Login with NetEase Cloud account',
|
||||
placeholder: {
|
||||
phone: 'Phone Number',
|
||||
password: 'Password'
|
||||
},
|
||||
button: {
|
||||
login: 'Login',
|
||||
switchToQr: 'QR Code Login',
|
||||
switchToPhone: 'Phone Login'
|
||||
},
|
||||
message: {
|
||||
loginSuccess: 'Login successful',
|
||||
loadError: 'Error loading login information',
|
||||
qrCheckError: 'Error checking QR code status'
|
||||
}
|
||||
};
|
||||
119
src/i18n/lang/en-US/player.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
export default {
|
||||
nowPlaying: 'Now Playing',
|
||||
playlist: 'Playlist',
|
||||
lyrics: 'Lyrics',
|
||||
previous: 'Previous',
|
||||
play: 'Play',
|
||||
pause: 'Pause',
|
||||
next: 'Next',
|
||||
volumeUp: 'Volume Up',
|
||||
volumeDown: 'Volume Down',
|
||||
mute: 'Mute',
|
||||
unmute: 'Unmute',
|
||||
songNum: 'Song Number: {num}',
|
||||
playFailed: 'Play Failed, Play Next Song',
|
||||
playMode: {
|
||||
sequence: 'Sequence',
|
||||
loop: 'Loop',
|
||||
random: 'Random'
|
||||
},
|
||||
fullscreen: {
|
||||
enter: 'Enter Fullscreen',
|
||||
exit: 'Exit Fullscreen'
|
||||
},
|
||||
close: 'Close',
|
||||
modeHint: {
|
||||
single: 'Single',
|
||||
list: 'Next'
|
||||
},
|
||||
lrc: {
|
||||
noLrc: 'No lyrics, please enjoy'
|
||||
},
|
||||
reparse: {
|
||||
title: 'Select Music Source',
|
||||
desc: 'Click a source to directly reparse the current song. This source will be used next time this song plays.',
|
||||
success: 'Reparse successful',
|
||||
failed: 'Reparse failed',
|
||||
warning: 'Please select a music source',
|
||||
bilibiliNotSupported: 'Bilibili videos do not support reparsing',
|
||||
processing: 'Processing...'
|
||||
},
|
||||
playBar: {
|
||||
expand: 'Expand Lyrics',
|
||||
collapse: 'Collapse Lyrics',
|
||||
like: 'Like',
|
||||
lyric: 'Lyric',
|
||||
noSongPlaying: 'No song playing',
|
||||
eq: 'Equalizer',
|
||||
playList: 'Play List',
|
||||
reparse: 'Reparse',
|
||||
playMode: {
|
||||
sequence: 'Sequence',
|
||||
loop: 'Loop',
|
||||
random: 'Random'
|
||||
},
|
||||
play: 'Play',
|
||||
pause: 'Pause',
|
||||
prev: 'Previous',
|
||||
next: 'Next',
|
||||
volume: 'Volume',
|
||||
favorite: 'Favorite {name}',
|
||||
unFavorite: 'Unfavorite {name}',
|
||||
playbackSpeed: 'Playback Speed',
|
||||
advancedControls: 'Advanced Controls',
|
||||
},
|
||||
eq: {
|
||||
title: 'Equalizer',
|
||||
reset: 'Reset',
|
||||
on: 'On',
|
||||
off: 'Off',
|
||||
bass: 'Bass',
|
||||
midrange: 'Midrange',
|
||||
treble: 'Treble',
|
||||
presets: {
|
||||
flat: 'Flat',
|
||||
pop: 'Pop',
|
||||
rock: 'Rock',
|
||||
classical: 'Classical',
|
||||
jazz: 'Jazz',
|
||||
electronic: 'Electronic',
|
||||
hiphop: 'Hip-Hop',
|
||||
rb: 'R&B',
|
||||
metal: 'Metal',
|
||||
vocal: 'Vocal',
|
||||
dance: 'Dance',
|
||||
acoustic: 'Acoustic',
|
||||
custom: 'Custom'
|
||||
}
|
||||
},
|
||||
// Sleep timer related
|
||||
sleepTimer: {
|
||||
title: 'Sleep Timer',
|
||||
cancel: 'Cancel Timer',
|
||||
timeMode: 'By Time',
|
||||
songsMode: 'By Songs',
|
||||
playlistEnd: 'After Playlist',
|
||||
afterPlaylist: 'After Playlist Ends',
|
||||
activeUntilEnd: 'Active until end of playlist',
|
||||
minutes: 'min',
|
||||
hours: 'hr',
|
||||
songs: 'songs',
|
||||
set: 'Set',
|
||||
timerSetSuccess: 'Timer set for {minutes} minutes',
|
||||
songsSetSuccess: 'Timer set for {songs} songs',
|
||||
playlistEndSetSuccess: 'Timer set to end after playlist',
|
||||
timerCancelled: 'Sleep timer cancelled',
|
||||
timerEnded: 'Sleep timer ended',
|
||||
playbackStopped: 'Music playback stopped',
|
||||
minutesRemaining: '{minutes} min remaining',
|
||||
songsRemaining: '{count} songs remaining'
|
||||
},
|
||||
playList: {
|
||||
clearAll: 'Clear Playlist',
|
||||
alreadyEmpty: 'Playlist is already empty',
|
||||
cleared: 'Playlist cleared',
|
||||
empty: 'Playlist is empty',
|
||||
clearConfirmTitle: 'Clear Playlist',
|
||||
clearConfirmContent: 'This will clear all songs in the playlist and stop the current playback. Continue?'
|
||||
}
|
||||
};
|
||||
20
src/i18n/lang/en-US/search.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export default {
|
||||
title: {
|
||||
hotSearch: 'Hot Search',
|
||||
searchList: 'Search Results',
|
||||
searchHistory: 'Search History'
|
||||
},
|
||||
button: {
|
||||
clear: 'Clear',
|
||||
back: 'Back',
|
||||
playAll: 'Play All'
|
||||
},
|
||||
loading: {
|
||||
more: 'Loading...',
|
||||
failed: 'Search failed'
|
||||
},
|
||||
noMore: 'No more results',
|
||||
error: {
|
||||
searchFailed: 'Search failed'
|
||||
}
|
||||
};
|
||||
248
src/i18n/lang/en-US/settings.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
export default {
|
||||
theme: 'Theme',
|
||||
language: 'Language',
|
||||
regard: 'About',
|
||||
logout: 'Logout',
|
||||
sections: {
|
||||
basic: 'Basic Settings',
|
||||
playback: 'Playback Settings',
|
||||
application: 'Application Settings',
|
||||
network: 'Network Settings',
|
||||
system: 'System Management',
|
||||
donation: 'Donation',
|
||||
regard: 'About'
|
||||
},
|
||||
basic: {
|
||||
themeMode: 'Theme Mode',
|
||||
themeModeDesc: 'Switch between light/dark theme',
|
||||
language: 'Language Settings',
|
||||
languageDesc: 'Change display language',
|
||||
font: 'Font Settings',
|
||||
fontDesc: 'Select fonts, prioritize fonts in order',
|
||||
fontScope: {
|
||||
global: 'Global',
|
||||
lyric: 'Lyrics Only'
|
||||
},
|
||||
animation: 'Animation Speed',
|
||||
animationDesc: 'Enable/disable animations',
|
||||
animationSpeed: {
|
||||
slow: 'Very Slow',
|
||||
normal: 'Normal',
|
||||
fast: 'Very Fast'
|
||||
},
|
||||
fontPreview: {
|
||||
title: 'Font Preview',
|
||||
chinese: 'Chinese',
|
||||
english: 'English',
|
||||
japanese: 'Japanese',
|
||||
korean: 'Korean',
|
||||
chineseText: '静夜思 床前明月光 疑是地上霜',
|
||||
englishText: 'The quick brown fox jumps over the lazy dog',
|
||||
japaneseText: 'あいうえお かきくけこ さしすせそ',
|
||||
koreanText: '가나다라마 바사아자차 카타파하'
|
||||
}
|
||||
},
|
||||
playback: {
|
||||
quality: 'Audio Quality',
|
||||
qualityDesc: 'Select music playback quality (VIP)',
|
||||
qualityOptions: {
|
||||
standard: 'Standard',
|
||||
higher: 'Higher',
|
||||
exhigh: 'Extreme',
|
||||
lossless: 'Lossless',
|
||||
hires: 'Hi-Res',
|
||||
jyeffect: 'HD Surround',
|
||||
sky: 'Immersive',
|
||||
dolby: 'Dolby Atmos',
|
||||
jymaster: 'Master'
|
||||
},
|
||||
musicSources: 'Music Sources',
|
||||
musicSourcesDesc: 'Select music sources for song resolution',
|
||||
musicSourcesWarning: 'At least one music source must be selected',
|
||||
musicUnblockEnable: 'Enable Music Unblocking',
|
||||
musicUnblockEnableDesc: 'When enabled, attempts to resolve unplayable songs',
|
||||
configureMusicSources: 'Configure Sources',
|
||||
selectedMusicSources: 'Selected sources:',
|
||||
noMusicSources: 'No sources selected',
|
||||
gdmusicInfo: 'GD Music Station intelligently resolves music from multiple platforms automatically',
|
||||
autoPlay: 'Auto Play',
|
||||
autoPlayDesc: 'Auto resume playback when reopening the app',
|
||||
showStatusBar: "Show Status Bar",
|
||||
showStatusBarContent: "You can display the music control function in your mac status bar (effective after a restart)"
|
||||
},
|
||||
application: {
|
||||
closeAction: 'Close Action',
|
||||
closeActionDesc: 'Choose action when closing window',
|
||||
closeOptions: {
|
||||
ask: 'Ask Every Time',
|
||||
minimize: 'Minimize to Tray',
|
||||
close: 'Exit Directly'
|
||||
},
|
||||
shortcut: 'Shortcut Settings',
|
||||
shortcutDesc: 'Customize global shortcuts',
|
||||
download: 'Download Management',
|
||||
downloadDesc: 'Always show download list button',
|
||||
unlimitedDownload: 'Unlimited Download',
|
||||
unlimitedDownloadDesc: 'Enable unlimited download mode for music , default limit 300 songs',
|
||||
downloadPath: 'Download Directory',
|
||||
downloadPathDesc: 'Choose download location for music files',
|
||||
remoteControl: 'Remote Control',
|
||||
remoteControlDesc: 'Set remote control function'
|
||||
},
|
||||
network: {
|
||||
apiPort: 'Music API Port',
|
||||
apiPortDesc: 'Restart required after modification',
|
||||
proxy: 'Proxy Settings',
|
||||
proxyDesc: 'Enable proxy when unable to access music',
|
||||
proxyHost: 'Proxy Host',
|
||||
proxyHostPlaceholder: 'Enter proxy host',
|
||||
proxyPort: 'Proxy Port',
|
||||
proxyPortPlaceholder: 'Enter proxy port',
|
||||
realIP: 'RealIP Settings',
|
||||
realIPDesc: 'Use realIP parameter with mainland China IP to resolve access restrictions abroad',
|
||||
messages: {
|
||||
proxySuccess: 'Proxy settings saved, restart required to take effect',
|
||||
proxyError: 'Please check your input',
|
||||
realIPSuccess: 'RealIP settings saved',
|
||||
realIPError: 'Please enter a valid IP address'
|
||||
}
|
||||
},
|
||||
system: {
|
||||
cache: 'Cache Management',
|
||||
cacheDesc: 'Clear cache',
|
||||
cacheClearTitle: 'Select cache types to clear:',
|
||||
cacheTypes: {
|
||||
history: {
|
||||
label: 'Play History',
|
||||
description: 'Clear played song records'
|
||||
},
|
||||
favorite: {
|
||||
label: 'Favorites',
|
||||
description: 'Clear local favorite songs (cloud favorites not affected)'
|
||||
},
|
||||
user: {
|
||||
label: 'User Data',
|
||||
description: 'Clear login info and user-related data'
|
||||
},
|
||||
settings: {
|
||||
label: 'App Settings',
|
||||
description: 'Clear all custom app settings'
|
||||
},
|
||||
downloads: {
|
||||
label: 'Download History',
|
||||
description: 'Clear download history (downloaded files not affected)'
|
||||
},
|
||||
resources: {
|
||||
label: 'Music Resources',
|
||||
description: 'Clear cached music files, lyrics and other resources'
|
||||
},
|
||||
lyrics: {
|
||||
label: 'Lyrics Resources',
|
||||
description: 'Clear cached lyrics resources'
|
||||
}
|
||||
},
|
||||
restart: 'Restart',
|
||||
restartDesc: 'Restart application',
|
||||
messages: {
|
||||
clearSuccess: 'Cache cleared successfully, some settings will take effect after restart'
|
||||
}
|
||||
},
|
||||
about: {
|
||||
version: 'Version',
|
||||
checkUpdate: 'Check for Updates',
|
||||
checking: 'Checking...',
|
||||
latest: 'Already latest version',
|
||||
hasUpdate: 'New version available',
|
||||
gotoUpdate: 'Go to Update',
|
||||
gotoGithub: 'Go to Github',
|
||||
author: 'Author',
|
||||
authorDesc: 'algerkong Give a star🌟',
|
||||
messages: {
|
||||
checkError: 'Failed to check for updates, please try again later'
|
||||
}
|
||||
},
|
||||
validation: {
|
||||
selectProxyProtocol: 'Please select proxy protocol',
|
||||
proxyHost: 'Please enter proxy host',
|
||||
portNumber: 'Please enter a valid port number (1-65535)'
|
||||
},
|
||||
lyricSettings: {
|
||||
title: 'Lyric Settings',
|
||||
pureMode: 'Pure Mode',
|
||||
hideCover: 'Hide Cover',
|
||||
centerDisplay: 'Center Display',
|
||||
showTranslation: 'Show Translation',
|
||||
hidePlayBar: 'Hide Play Bar',
|
||||
fontSize: 'Font Size',
|
||||
letterSpacing: 'Letter Spacing',
|
||||
lineHeight: 'Line Height',
|
||||
backgroundTheme: 'Background Theme',
|
||||
fontSizeMarks: {
|
||||
small: 'Small',
|
||||
medium: 'Medium',
|
||||
large: 'Large'
|
||||
},
|
||||
letterSpacingMarks: {
|
||||
compact: 'Compact',
|
||||
default: 'Default',
|
||||
loose: 'Loose'
|
||||
},
|
||||
lineHeightMarks: {
|
||||
compact: 'Compact',
|
||||
default: 'Default',
|
||||
loose: 'Loose'
|
||||
},
|
||||
themeOptions: {
|
||||
default: 'Default',
|
||||
light: 'Light',
|
||||
dark: 'Dark'
|
||||
},
|
||||
hideMiniPlayBar: 'Hide Mini Play Bar',
|
||||
hideLyrics: 'Hide Lyrics',
|
||||
tabs: {
|
||||
interface: 'Interface',
|
||||
display: 'Display',
|
||||
typography: 'Typography'
|
||||
}
|
||||
},
|
||||
shortcutSettings: {
|
||||
title: 'Shortcut Settings',
|
||||
shortcut: 'Shortcut',
|
||||
shortcutDesc: 'Customize global shortcuts',
|
||||
shortcutConflict: 'Shortcut Conflict',
|
||||
inputPlaceholder: 'Click to input shortcut',
|
||||
resetShortcuts: 'Reset',
|
||||
disableAll: 'Disable All',
|
||||
enableAll: 'Enable All',
|
||||
togglePlay: 'Play/Pause',
|
||||
prevPlay: 'Previous',
|
||||
nextPlay: 'Next',
|
||||
volumeUp: 'Volume Up',
|
||||
volumeDown: 'Volume Down',
|
||||
toggleFavorite: 'Favorite/Unfavorite',
|
||||
toggleWindow: 'Show/Hide Window',
|
||||
scopeGlobal: 'Global',
|
||||
scopeApp: 'App Only',
|
||||
enabled: 'Enabled',
|
||||
disabled: 'Disabled',
|
||||
messages: {
|
||||
resetSuccess: 'Shortcuts reset successfully, please save',
|
||||
conflict: 'Shortcut conflict, please reset',
|
||||
saveSuccess: 'Shortcuts saved successfully',
|
||||
saveError: 'Failed to save shortcuts',
|
||||
cancelEdit: 'Edit cancelled',
|
||||
disableAll: 'All shortcuts disabled, please save to apply',
|
||||
enableAll: 'All shortcuts enabled, please save to apply'
|
||||
}
|
||||
},
|
||||
remoteControl: {
|
||||
title: 'Remote Control',
|
||||
enable: 'Enable Remote Control',
|
||||
port: 'Port',
|
||||
allowedIps: 'Allowed IPs',
|
||||
addIp: 'Add IP',
|
||||
emptyListHint: 'Empty list means allow all IPs',
|
||||
saveSuccess: 'Remote control settings saved',
|
||||
accessInfo: 'Remote control access address:',
|
||||
}
|
||||
};
|
||||
28
src/i18n/lang/en-US/songItem.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export default {
|
||||
menu: {
|
||||
play: 'Play',
|
||||
playNext: 'Play Next',
|
||||
download: 'Download',
|
||||
addToPlaylist: 'Add to Playlist',
|
||||
favorite: 'Like',
|
||||
unfavorite: 'Unlike',
|
||||
removeFromPlaylist: 'Remove from Playlist',
|
||||
dislike: 'Dislike',
|
||||
undislike: 'Undislike',
|
||||
},
|
||||
message: {
|
||||
downloading: 'Downloading, please wait...',
|
||||
downloadFailed: 'Download failed',
|
||||
downloadQueued: 'Added to download queue',
|
||||
addedToNextPlay: 'Added to play next',
|
||||
getUrlFailed: 'Failed to get music download URL, please check if logged in'
|
||||
},
|
||||
dialog: {
|
||||
dislike:{
|
||||
title: 'Dislike',
|
||||
content: 'Are you sure you want to dislike this song?',
|
||||
positiveText: 'Dislike',
|
||||
negativeText: 'Cancel'
|
||||
}
|
||||
}
|
||||
};
|
||||
48
src/i18n/lang/en-US/user.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
export default {
|
||||
profile: {
|
||||
followers: 'Followers',
|
||||
following: 'Following',
|
||||
level: 'Level'
|
||||
},
|
||||
playlist: {
|
||||
created: 'Created Playlists',
|
||||
mine: 'Mine',
|
||||
trackCount: '{count} tracks',
|
||||
playCount: 'Played {count} times'
|
||||
},
|
||||
ranking: {
|
||||
title: 'Listening History',
|
||||
playCount: '{count} times'
|
||||
},
|
||||
follow: {
|
||||
title: 'Follow List',
|
||||
viewPlaylist: 'View Playlist',
|
||||
noFollowings: 'No Followings',
|
||||
loadMore: 'Load More',
|
||||
noSignature: 'This guy is lazy, nothing left',
|
||||
userFollowsTitle: '\'s Followings',
|
||||
myFollowsTitle: 'My Followings'
|
||||
},
|
||||
follower: {
|
||||
title: 'Follower List',
|
||||
noFollowers: 'No Followers',
|
||||
loadMore: 'Load More',
|
||||
userFollowersTitle: '\'s Followers',
|
||||
myFollowersTitle: 'My Followers'
|
||||
},
|
||||
detail: {
|
||||
playlists: 'Playlists',
|
||||
records: 'Listening History',
|
||||
noPlaylists: 'No Playlists',
|
||||
noRecords: 'No Listening History',
|
||||
artist: 'Artist',
|
||||
noSignature: 'This guy is lazy, nothing left',
|
||||
invalidUserId: 'Invalid User ID',
|
||||
noRecordPermission: '{name} doesn\'t let you see your listening history'
|
||||
},
|
||||
message: {
|
||||
loadFailed: 'Failed to load user page',
|
||||
deleteSuccess: 'Successfully deleted',
|
||||
deleteFailed: 'Failed to delete'
|
||||
}
|
||||
};
|
||||
5
src/i18n/lang/zh-CN/artist.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
hotSongs: '热门歌曲',
|
||||
albums: '专辑',
|
||||
description: '艺人介绍'
|
||||
};
|
||||
55
src/i18n/lang/zh-CN/common.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
export default {
|
||||
play: '播放',
|
||||
next: '下一首',
|
||||
previous: '上一首',
|
||||
volume: '音量',
|
||||
settings: '设置',
|
||||
search: '搜索',
|
||||
loading: '加载中...',
|
||||
loadingMore: '加载更多...',
|
||||
alipay: '支付宝',
|
||||
wechat: '微信支付',
|
||||
on: '开启',
|
||||
off: '关闭',
|
||||
show: '显示',
|
||||
hide: '隐藏',
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
configure: '配置',
|
||||
open: '打开',
|
||||
modify: '修改',
|
||||
success: '操作成功',
|
||||
error: '操作失败',
|
||||
warning: '警告',
|
||||
info: '提示',
|
||||
save: '保存',
|
||||
delete: '删除',
|
||||
refresh: '刷新',
|
||||
retry: '重试',
|
||||
reset: '重置',
|
||||
copySuccess: '已复制到剪贴板',
|
||||
copyFailed: '复制失败',
|
||||
validation: {
|
||||
required: '此项是必填的',
|
||||
invalidInput: '输入无效',
|
||||
selectRequired: '请选择一个选项',
|
||||
numberRange: '请输入 {min} 到 {max} 之间的数字'
|
||||
},
|
||||
viewMore: '查看更多',
|
||||
noMore: '没有更多了',
|
||||
selectAll: '全选',
|
||||
expand: '展开',
|
||||
collapse: '收起',
|
||||
songCount: '{count}首',
|
||||
language: '语言',
|
||||
tray: {
|
||||
show: '显示',
|
||||
quit: '退出',
|
||||
playPause: '播放/暂停',
|
||||
prev: '上一首',
|
||||
next: '下一首',
|
||||
pause: '暂停',
|
||||
play: '播放',
|
||||
favorite: '收藏'
|
||||
}
|
||||
};
|
||||
118
src/i18n/lang/zh-CN/comp.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
export default {
|
||||
installApp: {
|
||||
description: '安装应用程序,获得更好的体验',
|
||||
noPrompt: '不再提示',
|
||||
install: '立即安装',
|
||||
cancel: '暂不安装',
|
||||
download: '下载',
|
||||
downloadFailed: '下载失败',
|
||||
downloadComplete: '下载完成',
|
||||
downloadProblem: '下载遇到问题?去',
|
||||
downloadProblemLinkText: '下载最新版本'
|
||||
},
|
||||
playlistDrawer: {
|
||||
title: '添加到歌单',
|
||||
createPlaylist: '创建新歌单',
|
||||
cancelCreate: '取消创建',
|
||||
create: '创建',
|
||||
playlistName: '歌单名称',
|
||||
privatePlaylist: '私密歌单',
|
||||
publicPlaylist: '公开歌单',
|
||||
createSuccess: '歌单创建成功',
|
||||
createFailed: '歌单创建失败',
|
||||
addSuccess: '歌曲添加成功',
|
||||
addFailed: '歌曲添加失败',
|
||||
private: '私密',
|
||||
public: '公开',
|
||||
count: '首歌曲',
|
||||
loginFirst: '请先登录',
|
||||
getPlaylistFailed: '获取歌单失败',
|
||||
inputPlaylistName: '请输入歌单名称'
|
||||
},
|
||||
update: {
|
||||
title: '发现新版本',
|
||||
currentVersion: '当前版本',
|
||||
cancel: '暂不更新',
|
||||
prepareDownload: '准备下载...',
|
||||
downloading: '下载中...',
|
||||
nowUpdate: '立即更新',
|
||||
downloadFailed: '下载失败,请重试或手动下载',
|
||||
startFailed: '启动下载失败,请重试或手动下载',
|
||||
noDownloadUrl: '未找到适合当前系统的安装包,请手动下载',
|
||||
installConfirmTitle: '安装更新',
|
||||
installConfirmContent: '是否关闭应用并安装更新?',
|
||||
manualInstallTip: '如果关闭应用后没有正常弹出安装程序,请至下载文件夹查找文件并手动打开。',
|
||||
yesInstall: '立即安装',
|
||||
noThanks: '稍后安装',
|
||||
fileLocation: '文件位置',
|
||||
copy: '复制路径',
|
||||
copySuccess: '路径已复制到剪贴板',
|
||||
copyFailed: '复制失败',
|
||||
backgroundDownload: '后台下载'
|
||||
},
|
||||
coffee: {
|
||||
title: '请我喝咖啡',
|
||||
alipay: '支付宝',
|
||||
wechat: '微信支付',
|
||||
alipayQR: '支付宝收款码',
|
||||
wechatQR: '微信收款码',
|
||||
coffeeDesc: '一杯咖啡,一份支持',
|
||||
coffeeDescLinkText: '查看更多',
|
||||
qqGroup: 'QQ频道:algermusic',
|
||||
messages: {
|
||||
copySuccess: '已复制到剪贴板'
|
||||
},
|
||||
donateList: '请我喝咖啡'
|
||||
},
|
||||
playlistType: {
|
||||
title: '歌单分类',
|
||||
showAll: '显示全部',
|
||||
hide: '隐藏一些'
|
||||
},
|
||||
recommendAlbum: {
|
||||
title: '最新专辑'
|
||||
},
|
||||
recommendSinger: {
|
||||
title: '每日推荐',
|
||||
songlist: '每日推荐列表'
|
||||
},
|
||||
recommendSonglist: {
|
||||
title: '本周最热音乐'
|
||||
},
|
||||
searchBar: {
|
||||
login: '登录',
|
||||
toLogin: '去登录',
|
||||
logout: '退出登录',
|
||||
set: '设置',
|
||||
theme: '主题',
|
||||
restart: '重启',
|
||||
refresh: '刷新',
|
||||
currentVersion: '当前版本',
|
||||
searchPlaceholder: '搜索点什么吧...'
|
||||
},
|
||||
titleBar: {
|
||||
closeTitle: '请选择关闭方式',
|
||||
minimizeToTray: '最小化到托盘',
|
||||
exitApp: '退出应用',
|
||||
rememberChoice: '记住我的选择',
|
||||
closeApp: '关闭应用'
|
||||
},
|
||||
userPlayList: {
|
||||
title: '{name}的常听'
|
||||
},
|
||||
musicList: {
|
||||
searchSongs: '搜索歌曲',
|
||||
noSearchResults: '没有找到相关歌曲',
|
||||
switchToNormal: '切换到默认布局',
|
||||
switchToCompact: '切换到紧凑布局',
|
||||
playAll: '播放全部',
|
||||
collect: '收藏',
|
||||
collectSuccess: '收藏成功',
|
||||
cancelCollectSuccess: '取消收藏成功',
|
||||
operationFailed: '操作失败',
|
||||
cancelCollect: '取消收藏',
|
||||
addToPlaylist: '添加到播放列表',
|
||||
addToPlaylistSuccess: '添加到播放列表成功',
|
||||
songsAlreadyInPlaylist: '歌曲已存在于播放列表中'
|
||||
}
|
||||
};
|
||||
8
src/i18n/lang/zh-CN/donation.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
description: '您的捐赠将用于支持开发和维护工作,包括但不限于服务器维护、域名续费等。',
|
||||
message: '留言时可留下您的邮箱或 github名称。',
|
||||
refresh: '刷新列表',
|
||||
toDonateList: '请我喝咖啡',
|
||||
noMessage: '暂无留言',
|
||||
title: '捐赠列表'
|
||||
};
|
||||
48
src/i18n/lang/zh-CN/download.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
export default {
|
||||
title: '下载管理',
|
||||
localMusic: '本地音乐',
|
||||
count: '共 {count} 首歌曲',
|
||||
clearAll: '清空记录',
|
||||
tabs: {
|
||||
downloading: '下载中',
|
||||
downloaded: '已下载'
|
||||
},
|
||||
empty: {
|
||||
noTasks: '暂无下载任务',
|
||||
noDownloaded: '暂无已下载歌曲'
|
||||
},
|
||||
progress: {
|
||||
total: '总进度: {progress}%'
|
||||
},
|
||||
status: {
|
||||
downloading: '下载中',
|
||||
completed: '已完成',
|
||||
failed: '失败',
|
||||
unknown: '未知'
|
||||
},
|
||||
artist: {
|
||||
unknown: '未知歌手'
|
||||
},
|
||||
delete: {
|
||||
title: '删除确认',
|
||||
message: '确定要删除歌曲 "{filename}" 吗?此操作不可恢复。',
|
||||
confirm: '确定删除',
|
||||
cancel: '取消',
|
||||
success: '删除成功',
|
||||
failed: '删除失败',
|
||||
fileNotFound: '文件不存在或已被移动,已从记录中移除',
|
||||
recordRemoved: '文件删除失败,但已从记录中移除'
|
||||
},
|
||||
clear: {
|
||||
title: '清空下载记录',
|
||||
message: '确定要清空所有下载记录吗?此操作不会删除已下载的音乐文件,但将清空所有记录。',
|
||||
confirm: '确定清空',
|
||||
cancel: '取消',
|
||||
success: '下载记录已清空'
|
||||
},
|
||||
message: {
|
||||
downloadComplete: '{filename} 下载完成',
|
||||
downloadFailed: '{filename} 下载失败: {error}'
|
||||
},
|
||||
loading: '加载中...'
|
||||
};
|
||||
13
src/i18n/lang/zh-CN/favorite.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export default {
|
||||
title: '我的收藏',
|
||||
count: '共 {count} 首',
|
||||
batchDownload: '批量下载',
|
||||
download: '下载 ({count})',
|
||||
emptyTip: '还没有收藏歌曲',
|
||||
downloadSuccess: '下载完成',
|
||||
downloadFailed: '下载失败',
|
||||
downloading: '正在下载中,请稍候...',
|
||||
selectSongsFirst: '请先选择要下载的歌曲',
|
||||
descending: '降',
|
||||
ascending: '升'
|
||||
};
|
||||
5
src/i18n/lang/zh-CN/history.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
title: '播放历史',
|
||||
playCount: '{count}',
|
||||
getHistoryFailed: '获取历史记录失败'
|
||||
};
|
||||
29
src/i18n/lang/zh-CN/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import artist from './artist';
|
||||
import common from './common';
|
||||
import comp from './comp';
|
||||
import donation from './donation';
|
||||
import download from './download';
|
||||
import favorite from './favorite';
|
||||
import history from './history';
|
||||
import login from './login';
|
||||
import player from './player';
|
||||
import search from './search';
|
||||
import settings from './settings';
|
||||
import songItem from './songItem';
|
||||
import user from './user';
|
||||
|
||||
export default {
|
||||
common,
|
||||
donation,
|
||||
favorite,
|
||||
history,
|
||||
login,
|
||||
player,
|
||||
search,
|
||||
settings,
|
||||
songItem,
|
||||
user,
|
||||
download,
|
||||
comp,
|
||||
artist
|
||||
};
|
||||
22
src/i18n/lang/zh-CN/login.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export default {
|
||||
title: {
|
||||
qr: '扫码登录',
|
||||
phone: '手机号登录'
|
||||
},
|
||||
qrTip: '使用网易云APP扫码登录',
|
||||
phoneTip: '使用网易云账号登录',
|
||||
placeholder: {
|
||||
phone: '手机号',
|
||||
password: '密码'
|
||||
},
|
||||
button: {
|
||||
login: '登录',
|
||||
switchToQr: '扫码登录',
|
||||
switchToPhone: '手机号登录'
|
||||
},
|
||||
message: {
|
||||
loginSuccess: '登录成功',
|
||||
loadError: '加载登录信息时出错',
|
||||
qrCheckError: '检查二维码状态时出错'
|
||||
}
|
||||
};
|
||||
120
src/i18n/lang/zh-CN/player.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
export default {
|
||||
nowPlaying: '正在播放',
|
||||
playlist: '播放列表',
|
||||
lyrics: '歌词',
|
||||
previous: '上一个',
|
||||
play: '播放',
|
||||
pause: '暂停',
|
||||
next: '下一个',
|
||||
volumeUp: '音量增加',
|
||||
volumeDown: '音量减少',
|
||||
mute: '静音',
|
||||
unmute: '取消静音',
|
||||
songNum: '歌曲总数:{num}',
|
||||
playFailed: '当前歌曲播放失败,播放下一首',
|
||||
playMode: {
|
||||
sequence: '顺序播放',
|
||||
loop: '循环播放',
|
||||
random: '随机播放'
|
||||
},
|
||||
fullscreen: {
|
||||
enter: '全屏',
|
||||
exit: '退出全屏'
|
||||
},
|
||||
close: '关闭',
|
||||
modeHint: {
|
||||
single: '单曲循环',
|
||||
list: '自动播放下一个'
|
||||
},
|
||||
lrc: {
|
||||
noLrc: '暂无歌词, 请欣赏'
|
||||
},
|
||||
reparse: {
|
||||
title: '选择解析音源',
|
||||
desc: '点击音源直接进行解析,下次播放此歌曲时将使用所选音源',
|
||||
success: '重新解析成功',
|
||||
failed: '重新解析失败',
|
||||
warning: '请选择一个音源',
|
||||
bilibiliNotSupported: 'B站视频不支持重新解析',
|
||||
processing: '解析中...'
|
||||
},
|
||||
playBar: {
|
||||
expand: '展开歌词',
|
||||
collapse: '收起歌词',
|
||||
like: '喜欢',
|
||||
lyric: '歌词',
|
||||
noSongPlaying: '没有正在播放的歌曲',
|
||||
eq: '均衡器',
|
||||
playList: '播放列表',
|
||||
reparse: '重新解析',
|
||||
playMode: {
|
||||
sequence: '顺序播放',
|
||||
loop: '循环播放',
|
||||
random: '随机播放'
|
||||
},
|
||||
play: '开始播放',
|
||||
pause: '暂停播放',
|
||||
prev: '上一首',
|
||||
next: '下一首',
|
||||
volume: '音量',
|
||||
favorite: '已收藏{name}',
|
||||
unFavorite: '已取消收藏{name}',
|
||||
miniPlayBar: '迷你播放栏',
|
||||
playbackSpeed: '播放速度',
|
||||
advancedControls: '更多设置s',
|
||||
},
|
||||
eq: {
|
||||
title: '均衡器',
|
||||
reset: '重置',
|
||||
on: '开启',
|
||||
off: '关闭',
|
||||
bass: '低音',
|
||||
midrange: '中音',
|
||||
treble: '高音',
|
||||
presets: {
|
||||
flat: '平坦',
|
||||
pop: '流行',
|
||||
rock: '摇滚',
|
||||
classical: '古典',
|
||||
jazz: '爵士',
|
||||
electronic: '电子',
|
||||
hiphop: '嘻哈',
|
||||
rb: 'R&B',
|
||||
metal: '金属',
|
||||
vocal: '人声',
|
||||
dance: '舞曲',
|
||||
acoustic: '原声',
|
||||
custom: '自定义'
|
||||
}
|
||||
},
|
||||
// 定时关闭功能相关
|
||||
sleepTimer: {
|
||||
title: '定时关闭',
|
||||
cancel: '取消定时',
|
||||
timeMode: '按时间关闭',
|
||||
songsMode: '按歌曲数关闭',
|
||||
playlistEnd: '播放完列表后关闭',
|
||||
afterPlaylist: '播放完列表后关闭',
|
||||
activeUntilEnd: '播放至列表结束',
|
||||
minutes: '分钟',
|
||||
hours: '小时',
|
||||
songs: '首歌',
|
||||
set: '设置',
|
||||
timerSetSuccess: '已设置{minutes}分钟后关闭',
|
||||
songsSetSuccess: '已设置播放{songs}首歌后关闭',
|
||||
playlistEndSetSuccess: '已设置播放完列表后关闭',
|
||||
timerCancelled: '已取消定时关闭',
|
||||
timerEnded: '定时关闭已触发',
|
||||
playbackStopped: '音乐播放已停止',
|
||||
minutesRemaining: '剩余{minutes}分钟',
|
||||
songsRemaining: '剩余{count}首歌'
|
||||
},
|
||||
playList: {
|
||||
clearAll: '清空播放列表',
|
||||
alreadyEmpty: '播放列表已经为空',
|
||||
cleared: '已清空播放列表',
|
||||
empty: '播放列表为空',
|
||||
clearConfirmTitle: '清空播放列表',
|
||||
clearConfirmContent: '这将清空所有播放列表中的歌曲并停止当前播放。是否继续?'
|
||||
}
|
||||
};
|
||||
20
src/i18n/lang/zh-CN/search.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export default {
|
||||
title: {
|
||||
hotSearch: '热搜列表',
|
||||
searchList: '搜索列表',
|
||||
searchHistory: '搜索历史'
|
||||
},
|
||||
button: {
|
||||
clear: '清空',
|
||||
back: '返回',
|
||||
playAll: '播放列表'
|
||||
},
|
||||
loading: {
|
||||
more: '加载中...',
|
||||
failed: '搜索失败'
|
||||
},
|
||||
noMore: '没有更多了',
|
||||
error: {
|
||||
searchFailed: '搜索失败'
|
||||
}
|
||||
};
|
||||
248
src/i18n/lang/zh-CN/settings.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
export default {
|
||||
theme: '主题',
|
||||
language: '语言',
|
||||
regard: '关于',
|
||||
logout: '退出登录',
|
||||
sections: {
|
||||
basic: '基础设置',
|
||||
playback: '播放设置',
|
||||
application: '应用设置',
|
||||
network: '网络设置',
|
||||
system: '系统管理',
|
||||
donation: '捐赠支持',
|
||||
regard: '关于'
|
||||
},
|
||||
basic: {
|
||||
themeMode: '主题模式',
|
||||
themeModeDesc: '切换日间/夜间主题',
|
||||
language: '语言设置',
|
||||
languageDesc: '切换显示语言',
|
||||
font: '字体设置',
|
||||
fontDesc: '选择字体,优先使用排在前面的字体',
|
||||
fontScope: {
|
||||
global: '全局',
|
||||
lyric: '仅歌词'
|
||||
},
|
||||
animation: '动画速度',
|
||||
animationDesc: '是否开启动画',
|
||||
animationSpeed: {
|
||||
slow: '极慢',
|
||||
normal: '正常',
|
||||
fast: '极快'
|
||||
},
|
||||
fontPreview: {
|
||||
title: '字体预览',
|
||||
chinese: '中文',
|
||||
english: 'English',
|
||||
japanese: '日本語',
|
||||
korean: '한국어',
|
||||
chineseText: '静夜思 床前明月光 疑是地上霜',
|
||||
englishText: 'The quick brown fox jumps over the lazy dog',
|
||||
japaneseText: 'あいうえお かきくけこ さしすせそ',
|
||||
koreanText: '가나다라마 바사아자차 카타파하'
|
||||
}
|
||||
},
|
||||
playback: {
|
||||
quality: '音质设置',
|
||||
qualityDesc: '选择音乐播放音质(网易云VIP)',
|
||||
qualityOptions: {
|
||||
standard: '标准',
|
||||
higher: '较高',
|
||||
exhigh: '极高',
|
||||
lossless: '无损',
|
||||
hires: 'Hi-Res',
|
||||
jyeffect: '高清环绕声',
|
||||
sky: '沉浸环绕声',
|
||||
dolby: '杜比全景声',
|
||||
jymaster: '超清母带'
|
||||
},
|
||||
musicSources: '音源设置',
|
||||
musicSourcesDesc: '选择音乐解析使用的音源平台',
|
||||
musicSourcesWarning: '至少需要选择一个音源平台',
|
||||
musicUnblockEnable: '启用音乐解析',
|
||||
musicUnblockEnableDesc: '开启后将尝试解析无法播放的音乐',
|
||||
configureMusicSources: '配置音源',
|
||||
selectedMusicSources: '已选音源:',
|
||||
noMusicSources: '未选择音源',
|
||||
gdmusicInfo: 'GD音乐台可自动解析多个平台音源,自动选择最佳结果',
|
||||
autoPlay: '自动播放',
|
||||
autoPlayDesc: '重新打开应用时是否自动继续播放',
|
||||
showStatusBar: '是否显示状态栏控制功能',
|
||||
showStatusBarContent: '可以在您的mac状态栏显示音乐控制功能(重启后生效)',
|
||||
},
|
||||
application: {
|
||||
closeAction: '关闭行为',
|
||||
closeActionDesc: '选择关闭窗口时的行为',
|
||||
closeOptions: {
|
||||
ask: '每次询问',
|
||||
minimize: '最小化到托盘',
|
||||
close: '直接退出'
|
||||
},
|
||||
shortcut: '快捷键设置',
|
||||
shortcutDesc: '自定义全局快捷键',
|
||||
download: '下载管理',
|
||||
downloadDesc: '是否始终显示下载列表按钮',
|
||||
unlimitedDownload: '无限制下载',
|
||||
unlimitedDownloadDesc: '开启后将无限制下载音乐(可能出现下载失败的情况), 默认限制 300 首',
|
||||
downloadPath: '下载目录',
|
||||
downloadPathDesc: '选择音乐文件的下载位置',
|
||||
remoteControl: '远程控制',
|
||||
remoteControlDesc: '设置远程控制功能'
|
||||
},
|
||||
network: {
|
||||
apiPort: '音乐API端口',
|
||||
apiPortDesc: '修改后需要重启应用',
|
||||
proxy: '代理设置',
|
||||
proxyDesc: '无法访问音乐时可以开启代理',
|
||||
proxyHost: '代理地址',
|
||||
proxyHostPlaceholder: '请输入代理地址',
|
||||
proxyPort: '代理端口',
|
||||
proxyPortPlaceholder: '请输入代理端口',
|
||||
realIP: 'realIP设置',
|
||||
realIPDesc: '由于限制,此项目在国外使用会受到限制可使用realIP参数,传进国内IP解决',
|
||||
messages: {
|
||||
proxySuccess: '代理设置已保存,重启应用后生效',
|
||||
proxyError: '请检查输入是否正确',
|
||||
realIPSuccess: '真实IP设置已保存',
|
||||
realIPError: '请输入有效的IP地址'
|
||||
}
|
||||
},
|
||||
system: {
|
||||
cache: '缓存管理',
|
||||
cacheDesc: '清除缓存',
|
||||
cacheClearTitle: '请选择要清除的缓存类型:',
|
||||
cacheTypes: {
|
||||
history: {
|
||||
label: '播放历史',
|
||||
description: '清除播放过的歌曲记录'
|
||||
},
|
||||
favorite: {
|
||||
label: '收藏记录',
|
||||
description: '清除本地收藏的歌曲记录(不会影响云端收藏)'
|
||||
},
|
||||
user: {
|
||||
label: '用户数据',
|
||||
description: '清除登录信息和用户相关数据'
|
||||
},
|
||||
settings: {
|
||||
label: '应用设置',
|
||||
description: '清除应用的所有自定义设置'
|
||||
},
|
||||
downloads: {
|
||||
label: '下载记录',
|
||||
description: '清除下载历史记录(不会删除已下载的文件)'
|
||||
},
|
||||
resources: {
|
||||
label: '音乐资源',
|
||||
description: '清除已加载的音乐文件、歌词等资源缓存'
|
||||
},
|
||||
lyrics: {
|
||||
label: '歌词资源',
|
||||
description: '清除已加载的歌词资源缓存'
|
||||
}
|
||||
},
|
||||
restart: '重启',
|
||||
restartDesc: '重启应用',
|
||||
messages: {
|
||||
clearSuccess: '清除成功,部分设置在重启后生效'
|
||||
}
|
||||
},
|
||||
about: {
|
||||
version: '版本',
|
||||
checkUpdate: '检查更新',
|
||||
checking: '检查中...',
|
||||
latest: '当前已是最新版本',
|
||||
hasUpdate: '发现新版本',
|
||||
gotoUpdate: '前往更新',
|
||||
gotoGithub: '前往 Github',
|
||||
author: '作者',
|
||||
authorDesc: 'algerkong 点个star🌟呗',
|
||||
messages: {
|
||||
checkError: '检查更新失败,请稍后重试'
|
||||
}
|
||||
},
|
||||
validation: {
|
||||
selectProxyProtocol: '请选择代理协议',
|
||||
proxyHost: '请输入代理地址',
|
||||
portNumber: '请输入有效的端口号(1-65535)'
|
||||
},
|
||||
lyricSettings: {
|
||||
title: '页面设置',
|
||||
pureMode: '纯净模式',
|
||||
hideCover: '隐藏封面',
|
||||
centerDisplay: '居中显示',
|
||||
showTranslation: '显示翻译',
|
||||
hidePlayBar: '隐藏播放栏',
|
||||
fontSize: '字体大小',
|
||||
fontSizeMarks: {
|
||||
small: '小',
|
||||
medium: '中',
|
||||
large: '大'
|
||||
},
|
||||
letterSpacing: '文字间距',
|
||||
letterSpacingMarks: {
|
||||
compact: '紧凑',
|
||||
default: '默认',
|
||||
loose: '宽松'
|
||||
},
|
||||
lineHeight: '行高',
|
||||
lineHeightMarks: {
|
||||
compact: '紧凑',
|
||||
default: '默认',
|
||||
loose: '宽松'
|
||||
},
|
||||
backgroundTheme: '背景主题',
|
||||
themeOptions: {
|
||||
default: '默认',
|
||||
light: '亮色',
|
||||
dark: '暗色'
|
||||
},
|
||||
hideMiniPlayBar: '隐藏迷你播放栏',
|
||||
hideLyrics: '隐藏歌词',
|
||||
tabs: {
|
||||
interface: '界面',
|
||||
typography: '文字',
|
||||
display: '显示'
|
||||
}
|
||||
},
|
||||
shortcutSettings: {
|
||||
title: '快捷键设置',
|
||||
shortcut: '快捷键',
|
||||
shortcutDesc: '自定义快捷键',
|
||||
shortcutConflict: '快捷键冲突',
|
||||
inputPlaceholder: '点击输入快捷键',
|
||||
resetShortcuts: '恢复默认',
|
||||
disableAll: '全部禁用',
|
||||
enableAll: '全部启用',
|
||||
togglePlay: '播放/暂停',
|
||||
prevPlay: '上一首',
|
||||
nextPlay: '下一首',
|
||||
volumeUp: '音量增加',
|
||||
volumeDown: '音量减少',
|
||||
toggleFavorite: '收藏/取消收藏',
|
||||
toggleWindow: '显示/隐藏窗口',
|
||||
scopeGlobal: '全局',
|
||||
scopeApp: '应用内',
|
||||
enabled: '启用',
|
||||
disabled: '禁用',
|
||||
messages: {
|
||||
resetSuccess: '已恢复默认快捷键,请记得保存',
|
||||
conflict: '存在冲突的快捷键,请重新设置',
|
||||
saveSuccess: '快捷键设置已保存',
|
||||
saveError: '保存快捷键失败,请重试',
|
||||
cancelEdit: '已取消修改',
|
||||
disableAll: '已禁用所有快捷键,请记得保存',
|
||||
enableAll: '已启用所有快捷键,请记得保存'
|
||||
}
|
||||
},
|
||||
remoteControl: {
|
||||
title: '远程控制',
|
||||
enable: '启用远程控制',
|
||||
port: '服务端口',
|
||||
allowedIps: '允许的IP地址',
|
||||
addIp: '添加IP',
|
||||
emptyListHint: '空列表表示允许所有IP访问',
|
||||
saveSuccess: '远程控制设置已保存',
|
||||
accessInfo: '远程控制访问地址:',
|
||||
}
|
||||
};
|
||||
28
src/i18n/lang/zh-CN/songItem.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export default {
|
||||
menu: {
|
||||
play: '播放',
|
||||
playNext: '下一首播放',
|
||||
download: '下载歌曲',
|
||||
addToPlaylist: '添加到歌单',
|
||||
favorite: '喜欢',
|
||||
unfavorite: '取消喜欢',
|
||||
removeFromPlaylist: '从歌单中删除',
|
||||
dislike: '不喜欢',
|
||||
undislike: '取消不喜欢',
|
||||
},
|
||||
message: {
|
||||
downloading: '正在下载中,请稍候...',
|
||||
downloadFailed: '下载失败',
|
||||
downloadQueued: '已加入下载队列',
|
||||
addedToNextPlay: '已添加到下一首播放',
|
||||
getUrlFailed: '获取音乐下载地址失败,请检查是否登录'
|
||||
},
|
||||
dialog: {
|
||||
dislike: {
|
||||
title: '提示!',
|
||||
content: '确认不喜欢这首歌吗?再次进入将从每日推荐中排除。',
|
||||
positiveText: '不喜欢',
|
||||
negativeText: '取消'
|
||||
}
|
||||
}
|
||||
};
|
||||
48
src/i18n/lang/zh-CN/user.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
export default {
|
||||
profile: {
|
||||
followers: '粉丝',
|
||||
following: '关注',
|
||||
level: '等级'
|
||||
},
|
||||
playlist: {
|
||||
created: '创建的歌单',
|
||||
mine: '我创建的',
|
||||
trackCount: '{count}首',
|
||||
playCount: '播放{count}次'
|
||||
},
|
||||
ranking: {
|
||||
title: '听歌排行',
|
||||
playCount: '{count}次'
|
||||
},
|
||||
follow: {
|
||||
title: '关注列表',
|
||||
viewPlaylist: '查看歌单',
|
||||
noFollowings: '暂无关注',
|
||||
loadMore: '加载更多',
|
||||
noSignature: '这个家伙很懒,什么都没留下',
|
||||
userFollowsTitle: '的关注',
|
||||
myFollowsTitle: '我的关注'
|
||||
},
|
||||
follower: {
|
||||
title: '粉丝列表',
|
||||
noFollowers: '暂无粉丝',
|
||||
loadMore: '加载更多',
|
||||
userFollowersTitle: '的粉丝',
|
||||
myFollowersTitle: '我的粉丝'
|
||||
},
|
||||
detail: {
|
||||
playlists: '歌单',
|
||||
records: '听歌排行',
|
||||
noPlaylists: '暂无歌单',
|
||||
noRecords: '暂无听歌记录',
|
||||
artist: '歌手',
|
||||
noSignature: '这个人很懒,什么都没留下',
|
||||
invalidUserId: '用户ID无效',
|
||||
noRecordPermission: '{name}不让你看听歌排行'
|
||||
},
|
||||
message: {
|
||||
loadFailed: '加载用户页面失败',
|
||||
deleteSuccess: '删除成功',
|
||||
deleteFailed: '删除失败'
|
||||
}
|
||||
};
|
||||
38
src/i18n/main.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import enUS from './lang/en-US';
|
||||
import zhCN from './lang/zh-CN';
|
||||
|
||||
const messages = {
|
||||
'zh-CN': zhCN,
|
||||
'en-US': enUS
|
||||
} as const;
|
||||
|
||||
type Language = keyof typeof messages;
|
||||
|
||||
// 为主进程提供一个简单的 i18n 实现
|
||||
const mainI18n = {
|
||||
global: {
|
||||
currentLocale: 'zh-CN' as Language,
|
||||
get locale() {
|
||||
return this.currentLocale;
|
||||
},
|
||||
set locale(value: Language) {
|
||||
this.currentLocale = value;
|
||||
},
|
||||
t(key: string) {
|
||||
const keys = key.split('.');
|
||||
let current: any = messages[this.currentLocale];
|
||||
for (const k of keys) {
|
||||
if (current[k] === undefined) {
|
||||
// 如果找不到翻译,返回键名
|
||||
return key;
|
||||
}
|
||||
current = current[k];
|
||||
}
|
||||
return current;
|
||||
},
|
||||
messages
|
||||
}
|
||||
};
|
||||
|
||||
export type { Language };
|
||||
export default mainI18n;
|
||||