Compare commits

...

289 Commits

Author SHA1 Message Date
alger
76e55d4e6b 🐞 fix: 修复歌曲播放地址缓存导致播放失败问题 添加过期时间 2025-04-16 00:03:56 +08:00
alger
b7de5fc173 🌈 style: v4.3.0 2025-04-13 00:04:48 +08:00
alger
7bc8405df0 feat: 优化歌单加载逻辑 2025-04-12 23:51:37 +08:00
alger
a7f2045c7b feat: 添加统计服务 2025-04-12 23:16:12 +08:00
alger
b9b52f4d9f feat:确保仅在 Electron 中调用 API 发送歌曲数据 2025-04-12 13:05:08 +08:00
alger
09f8837fe4 feat: 增加无限滚动页面大小至100,以优化历史和收藏视图的加载体验 2025-04-12 12:37:08 +08:00
alger
d7fea7f888 feat: 优化歌单歌曲播放处理 2025-04-12 12:32:19 +08:00
alger
bb7d1e332f feat: 优化音乐封面显示逻辑,确保在缺失封面时使用默认图片,并更新推荐专辑组件以显示封面 2025-04-11 20:07:51 +08:00
alger
2dc907a20f 🌈 style: 修改依赖 2025-04-11 19:37:45 +08:00
Alger
ad3e52f6e1 Merge pull request #126 from algerkong/feat/music-list-search
 feat: 添加搜索功能至歌曲列表(可搜索名称 歌手 专辑),支持拼音匹配,优化播放列表加载逻辑,更好适配超大歌单
2025-04-11 19:36:10 +08:00
alger
b593ca3011 feat: 添加搜索功能至歌曲列表(可搜索名称 歌手 专辑),支持拼音匹配,优化播放列表加载逻辑,更好适配超大歌单 2025-04-11 19:35:21 +08:00
alger
988418e8d1 feat: 优化歌曲下载逻辑 2025-04-10 22:15:58 +08:00
Alger
1922311238 Merge pull request #123 from algerkong/feat/down-new
 feat: 歌曲下载内置封面歌词歌曲信息等,添加无限制下载功能,优化下载管理,支持清空下载记录
2025-04-10 00:27:48 +08:00
alger
3b1488f147 feat: 歌曲下载内置封面歌词歌曲信息等,添加无限制下载功能,优化下载管理,支持清空下载记录 2025-04-10 00:26:58 +08:00
alger
5f4b53c167 feat: 添加直接播放歌单功能,优化播放列表加载逻辑,支持异步加载完整歌单 2025-04-09 22:27:52 +08:00
alger
e17941dfb0 feat: 调整迷你播放栏列表项高度,从52px更改为69px,以优化滚动体验 2025-04-06 21:11:17 +08:00
Alger
9aff694116 Merge pull request #119 from algerkong/feat/shortcut
 feat: 添加快捷键管理功能,支持全局和应用内快捷键的启用/禁用,优化快捷键配置界面
2025-04-05 20:36:13 +08:00
algerkong
c2983ba079 feat: 添加快捷键管理功能,支持全局和应用内快捷键的启用/禁用,优化快捷键配置界面
feat: #117
2025-04-05 20:33:34 +08:00
algerkong
541ff2b76c feat: 优化桌面歌词 歌词行动态样式计算,提升歌词显示效果
feat: #115
2025-04-04 22:46:23 +08:00
algerkong
55b50d764b feat: 优化歌词界面配置 2025-04-04 22:19:35 +08:00
algerkong
30ff7b2930 feat: 优化历史歌曲获取功能, 分离网易云音乐和B站视频处理逻辑 2025-04-04 21:16:27 +08:00
algerkong
a24f901d1d feat: 优化B站视频代理URL获取逻辑 2025-04-04 21:16:09 +08:00
alger
7f7d41f883 feat: 更新迷你播放栏在页面显示逻辑 2025-04-02 00:07:37 +08:00
alger
7b27cf5bc6 fix: 修复类型问题 2025-04-01 23:34:17 +08:00
alger
ad8f7af3a9 feat: 更新至 v4.2.0
## v4.2.0

###  新功能
- 添加迷你播放器模式 ([0f55795](https://github.com/algerkong/AlgerMusicPlayer/commit/0f55795))
- 更新网易云音乐API版本,添加B站视频搜索功能和播放器组件 ([280fec1](https://github.com/algerkong/AlgerMusicPlayer/commit/280fec1))
- mac端添加状态栏 显示当前播放歌曲和操作按钮 ([374a7a8](https://github.com/algerkong/AlgerMusicPlayer/commit/374a7a8))
- 添加音频URL过期事件监听,自动重新获取B站和网易云音乐音频URL并恢复播放 ([ee6e9d4](https://github.com/algerkong/AlgerMusicPlayer/commit/ee6e9d4))
- 优化搜索功能,改进搜索历史管理和路由处理逻辑 ([477f8bb](https://github.com/algerkong/AlgerMusicPlayer/commit/477f8bb))
- 在播放列表中添加歌曲删除功能,优化播放列表管理逻辑 ([a5f694e](https://github.com/algerkong/AlgerMusicPlayer/commit/a5f694e)) (#94)
- 优化歌词窗口字体控制按钮样式 ([c5e50c9](https://github.com/algerkong/AlgerMusicPlayer/commit/c5e50c9))
- 优化首页banner加载逻辑 ([01ccad4](https://github.com/algerkong/AlgerMusicPlayer/commit/01ccad4))
- 优化歌手详情页面 由抽屉改为页面 ([dfb8f55](https://github.com/algerkong/AlgerMusicPlayer/commit/dfb8f55))
- 增加用户关注列表和关注用户详情页 可查看听歌排行和用户歌单 ([2924ad6](https://github.com/algerkong/AlgerMusicPlayer/commit/2924ad6))
- 优化进度条 鼠标悬停直接显示进度信息 ([9ce872e](https://github.com/algerkong/AlgerMusicPlayer/commit/9ce872e))
- 优化应用更新下载功能 可后台下载 弹出下载完成提示 不再自动关闭应用 ([23b2340](https://github.com/algerkong/AlgerMusicPlayer/commit/23b2340))

### 🐛 Bug 修复
- 修复进度条多次拖动和多次暂停播放引发的歌曲重复播放bug ([cfe197c](https://github.com/algerkong/AlgerMusicPlayer/commit/cfe197c)) (#104)
- 修复关闭按钮最小化 还在任务栏显示的bug ([e0d1305](https://github.com/algerkong/AlgerMusicPlayer/commit/e0d1305))
- 修复播放列表中歌曲删除时类型不匹配的问题 ([8d6d052](https://github.com/algerkong/AlgerMusicPlayer/commit/8d6d052))
2025-04-01 23:25:52 +08:00
alger
2599766e3e feat: cursor rule 2025-04-01 23:25:19 +08:00
alger
0f55795ca9 feat: 添加迷你模式功能,支持迷你窗口的显示与隐藏,更新设置项以控制迷你播放栏和歌词显示,优化路由管理以适应迷你模式 2025-04-01 23:22:26 +08:00
alger
8d6d0527db 🐛fix: 修复播放列表中歌曲删除时类型不匹配的问题,确保正确移除歌曲 2025-03-31 23:07:31 +08:00
alger
374a7a837d feat: mac添加音乐控制图标 , 托盘菜单项,更新播放状态和当前歌曲信息的逻辑
feat #105
2025-03-31 23:05:19 +08:00
alger
e0d13057c3 🐛fix: 修改标题栏行为,将最小化功能更改为托盘显示,优化窗口管理逻辑
fix #98
2025-03-31 23:04:35 +08:00
alger
23b2340169 feat: 优化更新提示对话框,支持文件路径复制和后台下载功能,优化安装流程
feat: #100
2025-03-31 23:01:03 +08:00
Alger
7e826311fe Merge pull request #104 from algerkong/fix/play-error
 feat: 优化音频播放进度更新逻辑,添加拖动滑块时的状态管理和节流处理
2025-03-31 22:58:36 +08:00
alger
cfe197c805 feat: 优化音频播放进度更新逻辑,添加拖动滑块时的状态管理和节流处理 2025-03-31 22:57:00 +08:00
Alger
230132904e Merge pull request #102 from algerkong/feat/bilibili-play
添加 bilibili资源搜索播放
2025-03-31 22:53:35 +08:00
alger
fb44ae45cc Merge branch 'dev_electron' into feat/bilibili-play 2025-03-31 22:48:59 +08:00
alger
9ce872eebe feat: 优化播放条滑块提示样式,添加滑块悬停提示功能 2025-03-30 12:56:42 +08:00
alger
ee6e9d43fd feat: 添加音频URL过期事件监听,自动重新获取B站和网易云音乐音频URL并恢复播放 2025-03-30 12:40:39 +08:00
alger
1a440fad09 feat: 添加B站音频URL获取功能,优化播放器逻辑,删除不再使用的BilibiliPlayer和MusicBar组件 2025-03-30 01:20:28 +08:00
alger
477f8bb99b feat: 优化搜索功能,改进搜索历史管理和路由处理逻辑 2025-03-30 00:18:44 +08:00
Alger
56c3ca1cce Merge pull request #94 from algerkong/feat/del-playlist
 feat: 在播放列表中添加歌曲删除功能,优化播放列表管理逻辑
2025-03-29 23:28:47 +08:00
alger
a5f694ea72 feat: 在播放列表中添加歌曲删除功能,优化播放列表管理逻辑
feat: #92
2025-03-29 23:26:26 +08:00
alger
280fec1990 feat: 更新网易云音乐 API 版本,添加 B站视频搜索功能和播放器组件 2025-03-29 23:19:51 +08:00
alger
c5e50c9fd5 feat: 优化歌词窗口字体控制按钮样式 2025-03-29 20:53:47 +08:00
alger
01ccad4df7 feat: 优化首页banner加载逻辑 2025-03-29 20:53:10 +08:00
alger
dfb8f55fba feat: 添加新的歌手详情页面 2025-03-29 20:52:50 +08:00
alger
2924ad6c18 feat: 增加用户关注列表 和 用户详情页 2025-03-24 22:54:04 +08:00
alger
9f5bac29a0 🚀new: v4.1.0 更新
### 🐛 Bug 修复
- 修复歌词窗口处理逻辑,解决 Windows 系统下桌面歌词窗口拖动问题
- 解决歌词初始化重复播放问题

###  新功能
- 优化移动端和网页端效果和体验
- 增加系统控制的音频服务的上一曲和下一曲功能
- 优化用户数据加载逻辑和错误处理
- 增强语言切换功能
- 首页添加用户歌单推荐
- 优化音频监听器初始化和设置保存逻辑

### 🔄 重构
- 将 Vuex 替换为 Pinia 状态管理
- 更新依赖版本
2025-03-23 12:33:13 +08:00
Alger
2fe1f0c04c Merge pull request #83 from algerkong/fix/duplicate-playback
 feat: 增强歌词窗口处理逻辑,修复可能引起的歌词初始化重复播放问题
2025-03-23 00:49:08 +08:00
alger
2a12f57cb2 feat: 增强歌词窗口处理逻辑,修复可能引起的歌词初始化重复播放问题 2025-03-23 00:47:01 +08:00
alger
4c10533a3d 🛠️ lint: 修复格式问题 2025-03-23 00:33:49 +08:00
alger
cda440b01a 🛠️ feat: 移除不必要的字体设置 2025-03-23 00:28:42 +08:00
alger
7b9e23743b lint: 修复格式问题 2025-03-22 15:01:38 +08:00
alger
e43270f35d lint: 修复格式问题 2025-03-22 14:54:24 +08:00
alger
9431faf932 feat: 优化移动端和网页端效果和体验
feat: #82
2025-03-22 13:45:23 +08:00
Alger
be03b5f8fc Merge pull request #82 from algerkong/feat/next-play
 feat: 增加音频服务的上一曲和下一曲功能
2025-03-22 10:40:19 +08:00
alger
8a414d0c25 feat: 增加音频服务的上一曲和下一曲功能 2025-03-22 10:37:57 +08:00
alger
f9fd9afcdd feat: 优化用户数据加载逻辑和错误处理 2025-03-22 10:31:05 +08:00
alger
b114cf4a33 feat: 增强语言切换功能和用户播放列表显示 2025-03-22 10:30:57 +08:00
alger
fa39d4ca55 feat: 优化音频监听器初始化和设置保存逻辑
- 在 App.vue 中引入 initAudioListeners 函数,确保在播放音乐时初始化音频监听器。
- 在 MusicHook.ts 中重构音频监听器的初始化逻辑,增加音频加载的超时处理。
- 在设置页面中实现防抖保存功能,避免频繁更新设置,提高性能和用户体验。

这些更改旨在提升音频播放的稳定性和设置管理的效率。
2025-03-21 00:19:15 +08:00
alger
650e4ff786 🔧 feat: 更新依赖版本 修复类型错误 优化首页推荐样式 2025-03-20 01:07:39 +08:00
alger
e355341596 🦄 refactor: 重构代码将 Vuex替换为 Pinia
集成 Pinia 状态管理
2025-03-19 22:48:28 +08:00
algerkong
4fa5ed0ca6 feat: 更新依赖和配置,增强开发体验
- 在 electron.vite.config.ts 中启用 Vue DevTools 插件
- 更新 package.json 中多个依赖版本,确保兼容性和性能
- 调整 tsconfig.node.json 的配置,优化模块解析
- 删除不再使用的组件 PlaylistType.vue、RecommendAlbum.vue、RecommendSinger.vue 和 RecommendSonglist.vue
- 在请求处理逻辑中改进错误日志输出,使用 console.error 替代 console.log
- 在首页视图中替换推荐歌手组件为顶部横幅组件

这些更改旨在提升开发效率和用户体验,确保项目的稳定性和可维护性。
2025-03-19 21:25:32 +08:00
alger
df9a1370c3 🐞 fix: 添加文件名清理功能以处理非法字符
- 新增 sanitizeFilename 函数,清理文件名中的非法字符
- 在下载音乐功能中应用清理后的文件名,确保文件名有效性

fixed: #78
2025-03-14 21:19:23 +08:00
alger
6a8813531f 🐞 fix: 修复歌词窗口拖动变大问题和多屏幕支持,优化字体样式
fixed: #77
2025-03-11 23:28:04 +08:00
alger
e5e45148c3 feat: 优化标题栏交互和下载按钮
- 为非 Electron 环境添加下载桌面版按钮
- 调整标题栏按钮显示逻辑,支持 Web 和桌面端不同交互
- 新增打开下载页面的方法,增强用户引导体验
2025-03-09 19:34:05 +08:00
alger
4a66796747 🚀 chore: 优化 GitHub Actions Web 部署工作 2025-03-09 12:13:19 +08:00
alger
7f8ab8be7c 🔒 chore: 添加 GitHub 部署密钥到 .gitignore 2025-03-08 23:52:32 +08:00
alger
ce276df55c feat: 优化赞赏支持 2025-03-08 23:22:56 +08:00
alger
ccc59ea893 🔧 fix: 优化音频服务和EQ设置的跨平台兼容性 2025-03-08 21:27:05 +08:00
alger
0b409f38d6 🚀 release: v4.0.0 2025-03-08 20:58:53 +08:00
alger
f9878ed88a feat: 优化歌词窗口交互和同步机制
- 增强歌词窗口数据同步逻辑,支持实时更新和状态管理
- 添加歌词窗口关闭事件监听和状态处理
- 优化无歌词时的默认提示和窗口行为
- 实现歌词窗口定时同步机制,提升用户体验
- 修复歌词窗口打开和关闭时的状态控制
- 国际化支持无歌曲播放时的提示文案
2025-03-08 19:00:50 +08:00
alger
e43e85480d feat: 增强音频播放状态管理和进度恢复功能
- 实现全局进度动画管理,优化歌词进度更新机制
- 新增音频播放进度本地存储和恢复功能
- 优化音频服务初始化和播放状态控制
- 改进音频上下文和 Howler 初始化逻辑
- 增加播放状态和进度的本地持久化支持
2025-03-08 18:31:46 +08:00
Alger
b97170d1b2 Update README.md 2025-03-08 17:07:32 +08:00
Alger
b9aa1d574a Merge pull request #75 from algerkong/feat/music-eq
 feat: 添加EQ音效调节功能 实时调节以及多个预设提供
2025-03-07 22:50:07 +08:00
alger
dd7b06d7e5 feat: 添加EQ音效调节功能 实时调节以及多个预设提供 2025-03-07 01:14:35 +08:00
alger
ddafcfba10 🔧 chore: 移除网站访问统计脚本和无用的统计显示元素 2025-03-05 23:03:05 +08:00
Alger
da5b8c408a Merge pull request #72 from algerkong/fix/random-music
fix: 修复随机播放模式 手动下一首不是随机的问题
2025-03-04 19:32:29 +08:00
alger
fb35d42fc4 fix: 修复随机播放模式 手动下一首不是随机的问题 2025-03-04 19:29:46 +08:00
Alger
dfd5d4c8b7 Merge pull request #71 from algerkong/fix/music-list-play
Fix/music list play
2025-03-02 22:49:00 +08:00
alger
e5309cedee feat: 音乐列表加载优化
- 重构音乐列表加载逻辑,提升数据加载性能和用户体验
- 新增歌曲总数显示,优化滚动加载和状态管理
- 改进歌曲数据格式化和异步加载处理
2025-03-02 08:27:07 +08:00
alger
d335f57a1a feat: 优化音乐列表加载策略,提升异步加载稳定性和错误处理 2025-03-01 10:57:06 +08:00
alger
c703d9c197 feat: 优化音乐列表加载和播放逻辑,增强性能和用户体验 2025-02-28 19:52:00 +08:00
alger
87a0ceb5b0 feat: 优化WEB下载应用程序代理 2025-02-28 19:50:53 +08:00
alger
891d70f3ed feat: 优化语言设置和国际化处理 2025-02-19 01:31:19 +08:00
alger
37b5908ddc 🌈 style: v3.9.3 2025-02-19 01:02:12 +08:00
alger
ead017e4b1 feat: 国际化 (i18n) 功能实现 2025-02-19 01:01:43 +08:00
alger
da2a32e420 Merge branch 'dev_electron' into dev_i18n 2025-02-13 10:39:25 +08:00
Alger
5fc6edba20 Update README.md 2025-02-12 11:56:35 +08:00
alger
1ef47b8f1d feat: 增加动态代理节点获取和缓存机制 2025-02-12 11:44:18 +08:00
alger
cdaab19afa feat: 修改捐赠列表 API 2025-02-12 10:12:20 +08:00
alger
6a0b03cfe1 feat: 优化更新检查逻辑,增加多个代理源支持 2025-02-12 10:11:50 +08:00
alger
d449930a02 🌈 style: v3.9.2 2025-01-26 00:31:18 +08:00
Alger
820509dbea Merge pull request #50 from algerkong/feat/download
 feat: 弱化下载功能 默认隐藏下载列表按钮 在设置中配置打开
2025-01-26 00:22:58 +08:00
alger
1493ab7317 feat: 弱化下载功能 默认隐藏下载列表按钮 在设置中配置打开 2025-01-26 00:20:08 +08:00
alger
c6ca63ee11 🐞 fix: 修复一些下载和登录问题
fix: #49
2025-01-25 23:53:20 +08:00
alger
4fa1295b84 🐞 fix: 修复登录状态问题 修复播放退出登录的问题 2025-01-25 21:49:22 +08:00
alger
174428b386 feat: 国际化功能基础实现 2025-01-23 11:02:55 +08:00
alger
599b0251af 🌈 style: v3.9.0 2025-01-22 23:43:17 +08:00
alger
25c2180247 feat: 添加右键添加到歌单 可以创建歌单 可以在我的歌单中右键取消收藏 2025-01-22 23:37:50 +08:00
alger
a6ff0e7f5c feat: 歌曲右键 添加下一首播放功能 2025-01-22 22:22:32 +08:00
alger
2e06711600 feat: 添加自动播放 和自动保存正在播放列表功能 2025-01-22 22:16:52 +08:00
Alger
80770d6c75 Update README.md 2025-01-20 09:53:24 +08:00
Alger
1e068df2ad Update README.md 2025-01-20 09:46:26 +08:00
alger
4172ff9fc6 🐞 fix: 修复我的搜藏 查看更多跳转空白页的问题 2025-01-19 18:46:36 +08:00
alger
83a7df9fe8 Merge branch 'feat/new-update' into dev_electron 2025-01-19 15:06:16 +08:00
alger
ba95dc11fe feat: 优化歌词下一首的滚动 2025-01-19 13:35:10 +08:00
alger
93829acdab feat: 升级依赖包 升级 electron 版本 2025-01-19 13:34:31 +08:00
alger
1f0f35dd51 🌈 style: v3.8.0 2025-01-18 03:26:14 +08:00
alger
be94d6ff8e feat: 添加歌词界面样式配置功能 2025-01-18 03:25:21 +08:00
alger
1bdb8fcb4a feat: 添加字体配置功能 可配置歌词页面 或全局字体 2025-01-17 22:45:59 +08:00
alger
914e043502 feat: 去除歌曲缓存 优化播放下一首 2025-01-17 22:35:42 +08:00
alger
dfa175b8b2 feat: 应用单例模式 2025-01-17 22:35:33 +08:00
alger
a94e0efba5 feat: 优化播放 2025-01-17 22:34:49 +08:00
alger
fb0831f2eb feat: 应用更新在内部更新 自动打开安装包 2025-01-17 00:02:57 +08:00
alger
573023600a 🌈 style: v3.7.2 2025-01-16 23:27:51 +08:00
Alger
041aad31b4 Merge pull request #47 from algerkong/fix/player-freeze-recovery-46
 feat: 优化播放体验 优化代码 优化歌词缓存
2025-01-16 23:20:57 +08:00
alger
f652932d71 feat: 优化播放体验 优化代码 优化歌词缓存 2025-01-16 23:19:16 +08:00
alger
7efeb9492b 🐞 fix: 修复类型错误 2025-01-16 00:58:57 +08:00
alger
055536eb5c 🌈 style: v3.7.1 2025-01-16 00:46:33 +08:00
Alger
14852fc8d3 Merge pull request #44 from algerkong/fix/minimize-on-space-after-restore-42
🐞 fix: 最小化点击后恢复窗口按空格会继续最小化
2025-01-16 00:43:05 +08:00
Alger
9866e772df Merge pull request #45 from algerkong/fix/repeat-sound-on-lyric-seek-43
🐞 fix: 修复单曲循环后点击歌词快进会有重复的声音播放
2025-01-16 00:42:50 +08:00
alger
87ca0836b1 🐞 fix: 修复单曲循环后点击歌词快进会有重复的声音播放 2025-01-16 00:40:44 +08:00
alger
fa07c5a40c 🐞 fix: 最小化点击后恢复窗口按空格会继续最小化
fixed #42
2025-01-16 00:38:47 +08:00
alger
5dbfea240b 🌈 style: v3.7.0 2025-01-15 00:52:09 +08:00
alger
c1344393c3 🐞 fix: 修复清除缓存问题 2025-01-15 00:51:32 +08:00
alger
426888f77c feat: 优化设置页面样式以及布局 2025-01-15 00:31:40 +08:00
alger
45cbc15c0f feat: 添加快捷键 以及快捷键管理功能
ref #39
2025-01-15 00:30:00 +08:00
alger
072025a543 🐞 fix: 修复抽屉zindex 修复一些样式问题
closes  #37 #38
2025-01-15 00:26:42 +08:00
alger
c6427aa3e1 feat: v3.6.0 2025-01-13 23:08:47 +08:00
alger
632cdb1239 feat: 优化页面样式 2025-01-13 22:55:46 +08:00
alger
8ffe472605 feat: 添加歌手详情抽屉 2025-01-13 22:13:46 +08:00
alger
8e86d378d0 feat: 优化音乐解析,添加搜索记录 添加搜索滚动加载更多 添加关闭动画功能 2025-01-13 22:13:21 +08:00
alger
744fd53fb1 feat: 添加歌词缓存功能 2025-01-12 20:59:36 +08:00
alger
3c64473dbb feat: 优化音乐播放 控制 系统控制功能 (#36,#16)
fixed #36,#16
2025-01-12 19:14:25 +08:00
alger
e70fed37da feat: 添加下载列表显示功能 可播放已经下载的歌曲 添加清除缓存功能 修复下载文件类型问题 2025-01-12 16:04:03 +08:00
alger
b749854c5e feat: 优化留言显示 2025-01-12 12:38:51 +08:00
alger
d9210cc50a feat: 修改 捐赠支持 添加留言显示 可隐藏列表 2025-01-12 01:25:39 +08:00
alger
f186d34885 📃 docs: 更新README 2025-01-11 19:12:26 +08:00
alger
ba992b7c33 📃 docs: v3.4.0 2025-01-11 19:03:06 +08:00
alger
24d7c839c7 🌈 style: 添加 "animate.css" 2025-01-11 18:51:40 +08:00
alger
a4f3df80c9 📃 docs: v3.4.0 2025-01-11 18:45:42 +08:00
alger
866fec6ee3 feat: 优化收藏逻辑 本地和线上同步 添加批量下载 2025-01-11 18:38:34 +08:00
alger
8f7d6fbb8d feat: 设置页 添加捐赠支持列表 2025-01-11 18:22:14 +08:00
alger
62e26cae7d 🌈 style: 优化代码格式化 2025-01-10 22:49:55 +08:00
alger
ddb814da10 feat: v3.3.0 2025-01-06 22:33:13 +08:00
alger
e266ea8ef8 🐞 fix: 修复类型校验问题 2025-01-06 22:24:37 +08:00
alger
a894954641 🐞 fix: 修复类型校验问题 2025-01-06 22:15:25 +08:00
alger
f640ab9969 feat: v3.3.0 2025-01-06 22:10:20 +08:00
alger
9eb17fd978 feat: 优化登录失效 2025-01-06 22:03:50 +08:00
alger
020aca7384 feat: 添加音质选择 优化灰色歌曲解析 2025-01-06 20:54:42 +08:00
alger
fcc47dc0ff feat: 添加退出登录 2025-01-05 15:58:48 +08:00
alger
17ce268da6 feat: 修复未登录 收藏问题 2025-01-05 15:01:55 +08:00
alger
43c64b1b43 feat: 收藏功能改为接口对接 2025-01-04 16:58:08 +08:00
alger
11ced6b418 feat: 优化更新检查 下载 功能 2025-01-04 16:13:37 +08:00
alger
3d3992154a 🐞 fix: 修复歌词滚动问题 2025-01-04 00:26:30 +08:00
alger
81e7b67c7f 📃 docs: 3.2.0 2025-01-03 23:59:07 +08:00
alger
d7e94a342b feat: 添加代理功能和 realIP配置功能 2025-01-03 23:53:07 +08:00
alger
46f8067577 feat: 关闭应用的提示修改 可存储配置最小化 还是 关闭 2025-01-03 22:24:13 +08:00
alger
1dc7d0ceca 🐞 fix: 修复歌词页面与底栏冲突问题(#26) 修复搜索歌曲列表页面显示错误问题 (#33)
closed #26   #33
2025-01-03 22:03:26 +08:00
alger
ba64631a17 🐞 fix: 修复搜索类型切换 没有重新加载搜索的问题(#25)
closed #25
2025-01-03 21:28:48 +08:00
alger
cdb9524f04 feat: 解决检查更新请求失败问题 2025-01-02 00:45:01 +08:00
alger
5213aa13c5 🌈 style: 修复格式问题 2025-01-02 00:27:31 +08:00
alger
d870d0198f 🌈 style: 修复格式问题 2025-01-02 00:25:54 +08:00
alger
976a9afd2f 📃 docs: v3.1.0 2025-01-02 00:18:41 +08:00
alger
018218a5bf feat: 优化主入口代码 添加歌曲下载功能 2025-01-02 00:14:05 +08:00
alger
38a9d6ed31 feat: 完善网页版 安装应用功能 2025-01-01 22:42:25 +08:00
alger
8dab799939 feat: 修改更新检查功能 2025-01-01 15:05:49 +08:00
alger
1ddbe6f24e feat: 修改 github action 2025-01-01 14:48:31 +08:00
alger
4d5bcba6c7 fix: update macOS build config 2025-01-01 14:42:19 +08:00
alger
f833306b60 feat: 修改 github action 2025-01-01 14:31:27 +08:00
alger
4d92ed9963 🐞 fix: 修复mac 安装包损坏问题 2025-01-01 14:19:44 +08:00
alger
a22285156a feat: 修改 github action 添加更新日志 2025-01-01 14:01:55 +08:00
alger
d1029f16d6 feat: 修改 github action 2025-01-01 13:42:08 +08:00
alger
4908555635 feat: 修改 github action 2025-01-01 13:34:36 +08:00
alger
750cf7a484 🐞 fix: 去除无用导入 2025-01-01 13:26:06 +08:00
alger
a334743f6f feat: 添加 github action 自动 打包 发布 2025-01-01 13:14:56 +08:00
alger
14747cac10 feat: 优化打包和版本更新功能 2025-01-01 13:12:46 +08:00
alger
cc239aeaba 📃 docs: 修改文档 2025-01-01 02:44:39 +08:00
alger
eeda296589 📃 docs: 修改文档 2025-01-01 02:43:00 +08:00
alger
edb7ea201c 🌈 style: 去除无用提交 2025-01-01 02:30:37 +08:00
alger
17d20fa299 🦄 refactor: 重构整个项目 优化打包 修改后台服务为本地运行 添加更新版本检测功能 2025-01-01 02:25:18 +08:00
alger
f8d421c9b1 🐞 fix: 修复web移动端 页面空白问题 (#24)
closed #24
2024-12-30 11:20:23 +08:00
alger
dfdf02a17f feat: 优化主题效果 添加展开 menu功能 优化图片清晰度 添加随机播放功能(#20) 2024-12-29 00:43:39 +08:00
alger
abdb2bcd50 feat: 新增主题色切换功能 默认为日间主题可切换夜间 (#19、#21)
fixes #19  #21
2024-12-28 16:43:52 +08:00
alger
f728191a8f feat: 顶栏修改 2024-12-27 18:27:01 +08:00
alger
dfa8b51a53 📃 docs: qq 2024-12-25 19:59:58 +08:00
alger
b2c13121fd feat: 添加mv分类 2024-12-25 19:55:24 +08:00
alger
d28adb61a4 feat: 优化 list 加载 2024-12-17 23:23:20 +08:00
alger
9a7d5a3834 feat: 记忆歌词窗口位置 主窗口可关闭歌词窗口 2024-12-16 22:25:38 +08:00
alger
2037798fbe feat: 修复桌面歌词滚动问题 2024-12-16 22:15:25 +08:00
alger
85bd0ad015 feat: 优化桌面歌词添加歌曲控制 上一首下一首 播放暂停 2024-12-16 22:12:28 +08:00
alger
e1557a51a3 feat: 优化下载应用功能 去除web 窗口样式 2024-12-16 20:40:57 +08:00
alger
1ecc6f136f feat: 添加网页端可拖动边缘调整窗口大小功能 2024-12-15 21:17:35 +08:00
alger
53b3061b03 feat: 优化歌单列表页面 添加分类 2024-12-15 18:19:58 +08:00
alger
3d2f6a2330 feat: 将收藏与历史合并 2024-12-15 15:12:45 +08:00
alger
3b1470f28f feat: 添加设置菜单 优化移动端菜单显示 2024-12-15 14:35:18 +08:00
alger
100268448a feat: 优化图片加载 2024-12-15 14:13:13 +08:00
alger
51f67bb2c2 feat: 优化应用下载 2024-12-15 13:00:20 +08:00
alger
7be126cf5f feat: 优化播放器样式 添加单曲循环 优化桌面歌词效果 2024-12-15 01:40:13 +08:00
alger
f2f5d3ac15 feat: 优化web端页面效果 展示为 pc应用样式 2024-12-14 13:49:32 +08:00
alger
34c45e0105 📃 docs: 修该注释 2024-12-14 13:15:59 +08:00
alger
f9333f5f78 🐞 fix: 修复搜索时 使用空格导致的空格快捷键冲突问题(#18)
fixes #18
2024-12-14 13:00:06 +08:00
alger
7365daf700 🐞 fix: 修复播放暂停控制问题 后续优化为参数监听 2024-12-12 22:36:07 +08:00
alger
cebf313075 feat: 优化播放 修改为howler 修复搜索导致播放无限卡顿问题(#15)
- 优化了整个项目的播放
- 去除audio
- 优化歌词页 歌词同步时间

fixes #15
2024-12-12 22:18:52 +08:00
alger
bb99049991 feat: 优化页面效果 2024-12-09 22:58:57 +08:00
alger
df74dafbc5 feat: 优化歌单列表页面 2024-12-09 22:39:33 +08:00
alger
721d2a9704 feat: 添加搜藏功能 与页面 2024-12-09 21:55:08 +08:00
alger
1e60fa9a95 feat: 添加展开收起歌词的提示 2024-12-09 20:51:40 +08:00
alger
f24e8232f8 feat: 修复布局问题 2024-12-09 20:39:32 +08:00
alger
a1b1d861ac feat: 修改下载地址 2024-12-09 18:43:05 +08:00
alger
f24263b416 🐞 fix: 修复滚动问题 2024-12-08 21:57:34 +08:00
alger
17795e5da2 feat: 添加动画速度调整功能 优化页面自适应效果 2024-12-08 21:50:58 +08:00
alger
f1030d3a78 feat: seo 优化 2024-12-08 21:35:15 +08:00
alger
b979ce250f feat: 添加 Coffee 2024-12-07 23:20:31 +08:00
alger
d0d8966875 feat: 优化移动端 歌词与歌单页面显示 2024-12-07 22:54:45 +08:00
alger
d39ba65263 📃 docs: 修改文档 2024-12-07 22:38:56 +08:00
alger
62d400827e 📃 docs: 修改文档 2024-12-07 22:33:36 +08:00
alger
75b99c46b5 📃 docs: 修改文档 2024-12-07 22:32:06 +08:00
alger
e7ae79144c feat: 修改登录背景 2024-12-07 21:50:18 +08:00
alger
04d6cbe7f3 🐞 fix: 修复二维码登录 重复触发请求问题 修改为手机号优先 2024-12-07 21:37:10 +08:00
alger
bea1e5751f feat: 优化cpu占用过高问题 2024-12-07 14:30:20 +08:00
alger
f2ebb04fab 📃 docs: 修改文档 2024-12-07 12:02:54 +08:00
alger
42048764d5 📃 docs: 修改文档 2024-12-07 11:38:56 +08:00
alger
e326253fd8 feat: 优化打包命令 2024-12-06 23:52:08 +08:00
alger
edf5c77ea0 feat: 优化桌面歌词功能 添加歌词进度 优化歌词页面样式 2024-12-06 23:50:44 +08:00
alger
8870390770 feat: 修复下载提示弹出问题 2024-12-06 20:57:33 +08:00
alger
c9514e6e19 feat: 在页面显示 github地址 2024-12-05 21:57:14 +08:00
alger
08fa160de4 🐞 fix: 修复搜索下 mv和歌曲同时播放问题 2024-12-05 21:35:20 +08:00
alger
5d4c4922fd feat: 修复搜索播放 bug 优化搜索 mv播放器 2024-12-05 21:29:13 +08:00
alger
c5e7c87658 feat: 优化列表渲染 2024-12-04 20:38:26 +08:00
alger
f6923b4c47 🐞 fix: 修复调整窗口大小 歌单列表重新加载问题 2024-12-01 16:41:23 +08:00
alger
4cf7598a7d 📃 docs: 更新 docs 2024-12-01 16:28:26 +08:00
alger
81b09bef0d feat: 修复无法播放的问题 2024-12-01 15:55:09 +08:00
alger
b21df3de25 feat: 修改下载地址 2024-11-29 08:49:32 +08:00
alger
c49d814182 feat: 优化滚动条 位置 2024-11-28 23:45:44 +08:00
alger
1cb3c72ab7 feat: 优化歌单列表数量 2024-11-28 23:39:56 +08:00
alger
f03372de6a feat: 优化歌单列表 添加加载更多 优化自动布局 优化歌单 mv 歌单类型的动画效果 2024-11-28 23:33:38 +08:00
alger
d925f40303 feat: 优化歌词进度 添加下载 优化播放 优化历史记录 2024-11-28 08:12:37 +08:00
alger
dc12d895d8 feat: 2.1.0 2024-11-23 22:43:01 +08:00
alger
0bb14902f2 feat: 优化播放样式 优化歌曲背景色 优化 mv播放样式 添加循环播放 等控制功能 2024-11-23 22:42:23 +08:00
alger
3027a5f6ff feat: 完善mac打包规则 修复 icon显示问题 2024-11-20 22:44:17 +08:00
alger
f320f4760b feat: 添加网页标题修改 2024-11-01 17:39:18 +08:00
alger
e939933d6f feat: 添加减轻动画效果选项 添加indexdb方法 2024-10-22 21:09:51 +08:00
alger
06bffe7618 feat: 优化歌词页面样式 添加歌词进度显示 优化歌曲及列表加载方式 大幅提升歌曲歌词播放速度 2024-10-18 18:37:53 +08:00
alger
7abc087d70 feat: 添加播放列表自动滚动到播放的那个 2024-09-18 17:05:36 +08:00
alger
eb2ea1981d feat: 优化歌词背景色 加载问题 2024-09-18 15:11:20 +08:00
alger
6dc14ec51b feat: 优化歌词背景 修改为背景色 以解决卡顿问题 2024-09-14 18:22:56 +08:00
alger
36f8257a3e 🐞 fix: 上一首下一首逻辑错乱问题 2024-09-13 17:23:03 +08:00
alger
c55544df46 feat: 修复排行播放列表问题 优化暂停播放逻辑 2024-09-13 17:07:45 +08:00
alger
008f2183de 🐞 fix: 修复历史播放 不触发播放列表问题 2024-09-13 14:14:32 +08:00
alger
dd3a3c3bbb 🐞 fix: 类型问题修复 2024-09-13 14:11:02 +08:00
alger
941eb2e66e 🐞 fix: 修复作者不显示问题 2024-09-13 09:43:05 +08:00
alger
a98fcb43d6 🐞 fix: 修复播放列表无法显示问题 2024-09-13 09:08:57 +08:00
alger
791121ae06 feat: 优化搜索 2024-09-12 17:28:51 +08:00
alger
0c156e2708 feat: V1.7.0 2024-09-12 16:48:13 +08:00
alger
017b47fded 🐞 fix: 修复各种报错问题 2024-09-12 16:44:42 +08:00
alger
e27ed22c16 feat: 完善搜索歌单列表加载问题 2024-09-12 15:26:07 +08:00
alger
904d8744ef feat: 优化播放栏背景问题 2024-09-12 15:00:00 +08:00
alger
800e0b7360 feat: 完善歌单列表组件 实现滚动加载更多 2024-09-11 16:29:43 +08:00
alger
b6a5461a1d 🎈 perf: 优化加载 升级vue3.5 electron32等多个包 添加v-loading指令 2024-09-04 15:20:43 +08:00
alger
a4eda61a86 🌈 style: 更新版本 1.5.1 2024-06-25 15:22:30 +08:00
alger
a79d0712a4 🌈 style: 修改mv搜索项样式 2024-06-05 17:03:27 +08:00
alger
8f782cdc9d 🌈 style: 修改mv搜索项样式 2024-06-05 15:53:12 +08:00
alger
2f851f3172 🎈 perf: 优化歌曲列表以及图片加载 2024-06-05 15:35:31 +08:00
alger
9fcf455c08 🌈 style: 更新版本 1.5.0 2024-05-31 08:56:16 +08:00
alger
9b14906a46 🌈 style: add LICENSE. 2024-05-27 18:18:02 +08:00
alger
14ce428951 🌈 style: 修改README 2024-05-27 11:52:10 +08:00
alger
8c93124311 🌈 style: 修改README 2024-05-27 11:51:15 +08:00
alger
c09707867b 🦄 refactor: 适配 web移动端 改造 2024-05-23 17:12:35 +08:00
alger
a2af0f3904 feat: 优化搜索功能 2024-05-22 19:20:57 +08:00
alger
73982f0e84 feat: 添加manifest.json 2024-05-22 15:38:43 +08:00
alger
449a6fd335 feat: 登录问题修复 2024-05-22 15:14:26 +08:00
alger
32b39c7927 feat: 添加每日推荐 样式, 请求等大量优化 2024-05-22 12:07:48 +08:00
alger
c6f1e0b233 🌈 style: 修改issue模板 2024-05-21 11:36:06 +08:00
alger
7c1a3ae4bc 🌈 style: 添加issue模板 2024-05-21 11:33:57 +08:00
alger
6bd6622484 🌈 style: 添加gzip压缩配置 2024-05-21 11:24:33 +08:00
alger
433aff385d 🌈 style: 更换ico文件 2024-05-21 11:06:20 +08:00
alger
c37ad07f93 🌈 style: 优化类型 2024-05-21 11:01:23 +08:00
alger
e4c1f855fb 🐞 fix: 修复关闭报错 2024-05-21 10:27:46 +08:00
alger
6978656061 🌈 style: 更新logo.png 2024-05-21 10:24:32 +08:00
alger
973d60c98f 🌈 style: 完善gitgnore 2024-05-21 10:21:51 +08:00
alger
5a43ba2576 🌈 style: 删除无用配置 2024-05-21 10:18:40 +08:00
alger
e52a02cf3c 🌈 style: 去除无用代码 2024-05-21 10:16:30 +08:00
alger
da8216e2ca 🦄 refactor: 重构打包方式 2024-05-21 08:52:34 +08:00
alger
bd0e2ec35c feat: 历史纪录去除按钮 2024-05-20 19:55:52 +08:00
alger
7c8598ffa5 feat: 优化歌词体验 2024-05-20 19:54:00 +08:00
alger
50e594b91d 🐞 fix: 修复歌词界面无法打开问题 2024-05-16 19:57:20 +08:00
alger
a9e5bb33e4 feat: 添加eslint 和 桌面歌词(未完成) 2024-05-16 18:54:30 +08:00
alger
5e8676a039 📃 docs: 完善文档 2024-05-14 18:11:54 +08:00
284 changed files with 29308 additions and 4663 deletions

View File

@@ -0,0 +1,92 @@
---
description: 这个规则是项目描述
globs:
alwaysApply: false
---
您是 TypeScript、Node.j、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 VitalsLCP、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
View 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 实现响应式设计

View File

@@ -1,4 +1,12 @@
VITE_API = /api
VITE_API_MT = /mt
VITE_API_MUSIC = /music
VITE_API_PROXY = http://110.42.251.190:9856
# 你的接口地址 (必填)
VITE_API_LOCAL = ***
# 音乐破解接口地址
VITE_API_MUSIC = ***
# 代理地址
VITE_API_PROXY = ***
# 本地运行代理地址
VITE_API_PROXY = /api
VITE_API_MUSIC_PROXY = /music
VITE_API_PROXY_MUSIC = /music_proxy

View File

@@ -1,4 +0,0 @@
VITE_API = http://110.42.251.190:9898
VITE_API_MT = http://mt.myalger.top
VITE_API_MUSIC = http://110.42.251.190:4100
VITE_API_PROXY = http://110.42.251.190:9856

4
.eslintignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules
dist
out
.gitignore

138
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,138 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution');
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'eslint-config-airbnb-base',
'@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
View File

@@ -0,0 +1 @@
* @algerkong

View 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
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name:
url:
about:

View 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
View 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
View 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
View File

@@ -0,0 +1,8 @@
## IssueShoot
- 预估时长: {{ .duration }}
- 期望完成时间: {{ .deadline }}
- 开发难度: {{ .level }}
- 参与人数: 1
- 需求对接人: ivringpeng
- 验收标准: 实现期望改造效果,提 PR 并通过验收无误
- 备注: 最终激励以实际提交 `pull request` 并合并为准

87
.github/workflows/build.yml vendored Normal file
View 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
View File

@@ -0,0 +1,51 @@
name: Deploy Web
on:
push:
branches:
- dev_electron # 或者您的主分支名称
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)"

18
.gitignore vendored
View File

@@ -1,11 +1,27 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
dist_electron
.idea
# lock
yarn.lock
pnpm-lock.yaml
package-lock.json
dist.zip
dist.zip
.vscode
bun.lockb
bun.lock
.env.*.local
out
.cursorrules
.github/deploy_keys

6
.prettierignore Normal file
View File

@@ -0,0 +1,6 @@
out
dist
pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json

5
.prettierrc.yaml Normal file
View File

@@ -0,0 +1,5 @@
singleQuote: true
semi: true
printWidth: 100
trailingComma: none
endOfLine: auto

View File

@@ -1,3 +0,0 @@
{
"compile-hero.disable-compile-files-on-did-save-code": true
}

17
CHANGELOG.md Normal file
View File

@@ -0,0 +1,17 @@
# 更新日志
## v4.3.0
### ✨ 新功能
- 歌曲下载内置封面歌词歌曲信息,添加无限制下载功能,优化下载页面添加下载记录清除功能 ([3b1488f](https://github.com/algerkong/AlgerMusicPlayer/commit/3b1488f)) (#123) ([988418e](https://github.com/algerkong/AlgerMusicPlayer/commit/988418e))
- 添加搜索功能至歌曲列表,支持名称、歌手、专辑搜索,支持拼音匹配 ([b593ca3](https://github.com/algerkong/AlgerMusicPlayer/commit/b593ca3)) (#126)
- 添加快捷键管理功能,支持全局和应用内快捷键的启用/禁用 ([c2983ba](https://github.com/algerkong/AlgerMusicPlayer/commit/c2983ba)) (#119)
- 优化歌单加载、播放逻辑,提升大型歌单加载性能 ([7bc8405](https://github.com/algerkong/AlgerMusicPlayer/commit/7bc8405))、([d7fea7f](https://github.com/algerkong/AlgerMusicPlayer/commit/d7fea7f))
- 添加直接播放首页歌单功能 ([5f4b53c](https://github.com/algerkong/AlgerMusicPlayer/commit/5f4b53c))
- 添加统计服务([a7f2045](https://github.com/algerkong/AlgerMusicPlayer/commit/a7f2045))
- 优化历史和收藏视图的加载体验 ([09f8837](https://github.com/algerkong/AlgerMusicPlayer/commit/09f8837))
- 优化歌词界面配置,提供更好的用户体验 ([55b50d7](https://github.com/algerkong/AlgerMusicPlayer/commit/55b50d7))
### 🐛 Bug 修复
- 优化音乐封面显示逻辑,确保在缺失封面时使用默认图片 ([bb7d1e3](https://github.com/algerkong/AlgerMusicPlayer/commit/bb7d1e3))
- 优化桌面歌词行动态样式计算,提升歌词显示效果 ([541ff2b](https://github.com/algerkong/AlgerMusicPlayer/commit/541ff2b))

View File

@@ -1,5 +1,64 @@
# Vue 3 + Typescript + Vite
# Alger Music Player
主要功能如下
- 🎵 音乐推荐
- 🔐 网易云账号登录与同步
- 📝 功能
- 播放历史记录
- 歌曲收藏管理
- 自定义快捷键配置
- 🎨 界面与交互
- 沉浸式歌词显示(点击左下角封面进入)
- 独立桌面歌词窗口
- 明暗主题切换
- 🎼 音乐功能
- 支持歌单、MV、专辑等完整音乐服务
- 灰色音乐资源解析(基于 @unblockneteasemusic/server
- 高品质音乐试听需网易云VIP
- 音乐文件下载(支持右键下载和批量下载)
- 🚀 技术特性
- 本地化服务无需依赖在线API (基于 netease-cloud-music-api)
- 自动更新检测
- 全平台适配Desktop & Web & Mobile Web
vue3 + TypeScript + NaiveUI + animateCss + Vuex + VueRouter + Axios等实现音乐桌面web端
实现各项功能
网站地址http://mc.myalger.top/
## 项目简介
一个基于 electron typescript vue3 的桌面音乐播放器 适配 web端 桌面端 web移动端
## 预览地址
[http://mc.alger.fun/](http://mc.alger.fun/)
QQ群:789288579
## 软件截图
![首页白](./docs/image.png)
![首页黑](./docs/image3.png)
![歌词](./docs/image1.png)
![桌面歌词](./docs/image2.png)
![设置页面](./docs/image4.png)
## 技术栈
### 主要框架
- Vue 3 - 渐进式 JavaScript 框架
- TypeScript - JavaScript 的超集,添加了类型系统
- Electron - 跨平台桌面应用开发框架
- Vite - 下一代前端构建工具
- Naive UI - 基于 Vue 3 的组件库
## 赞赏☕️
[赞赏列表](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> |
## 项目统计
[![Stargazers over time](https://starchart.cc/algerkong/AlgerMusicPlayer.svg?variant=adaptive)](https://starchart.cc/algerkong/AlgerMusicPlayer)
![Alt](https://repobeats.axiom.co/api/embed/c4d01b3632e241c90cdec9508dfde86a7f54c9f5.svg "Repobeats analytics image")
## 欢迎提Issues
## 免责声明
本软件仅用于学习交流,禁止用于商业用途,否则后果自负。

139
app.js
View File

@@ -1,139 +0,0 @@
const { app, BrowserWindow, ipcMain, Tray, Menu, globalShortcut, nativeImage } = require('electron')
const path = require('path')
const Store = require('electron-store');
const setJson = require('./electron/set.json')
let mainWin = null
function createWindow() {
mainWin = new BrowserWindow({
width: 1200,
height: 780,
frame: false,
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, '/electron/preload.js'),
},
})
const win = mainWin
win.setMinimumSize(1200, 780)
if (process.env.NODE_ENV === 'development') {
win.webContents.openDevTools({ mode: 'detach' })
win.loadURL('http://localhost:4678/')
} else {
win.loadURL(`file://${__dirname}/dist/index.html`)
}
const image = nativeImage.createFromPath(path.join(__dirname, 'public/icon.png'))
const tray = new Tray(image)
// 创建一个上下文菜单
const contextMenu = Menu.buildFromTemplate([
{
label: '显示',
click: () => {
win.show()
},
},
{
label: '退出',
click: () => {
win.destroy()
},
},
])
// 设置系统托盘图标的上下文菜单
tray.setContextMenu(contextMenu)
// 当系统托盘图标被点击时,切换窗口的显示/隐藏
tray.on('click', () => {
if (win.isVisible()) {
win.hide()
} else {
win.show()
}
})
const set = store.get('set')
// store.set('set', setJson)
if (!set) {
store.set('set', setJson)
}
}
// 限制只能启动一个应用
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
}
app.whenReady().then(createWindow)
app.on('ready',()=>{
globalShortcut.register('CommandOrControl+Alt+Shift+M', () => {
if (mainWin.isVisible()) {
mainWin.hide()
} else {
mainWin.show()
}
})
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('will-quit', () => {
globalShortcut.unregisterAll()
})
ipcMain.on('minimize-window', (event) => {
const win = BrowserWindow.fromWebContents(event.sender)
win.minimize()
})
ipcMain.on('maximize-window', (event) => {
const win = BrowserWindow.fromWebContents(event.sender)
if (win.isMaximized()) {
win.unmaximize()
} else {
win.maximize()
}
})
ipcMain.on('close-window', (event) => {
const win = BrowserWindow.fromWebContents(event.sender)
win.destroy()
})
ipcMain.on('drag-start', (event, data) => {
const win = BrowserWindow.fromWebContents(event.sender)
win.webContents.beginFrameSubscription((frameBuffer) => {
event.reply('frame-buffer', frameBuffer)
})
})
ipcMain.on('mini-tray', (event) => {
const win = BrowserWindow.fromWebContents(event.sender)
win.hide()
})
// 重启
ipcMain.on('restart', () => {
app.relaunch()
app.exit(0)
})
const store = new Store();
// 定义ipcRenderer监听事件
ipcMain.on('setStore', (_, key, value) => {
store.set(key, value)
})
ipcMain.on('getStore', (_, key) => {
let value = store.get(key)
_.returnValue = value || ""
})

136
auto-imports.d.ts vendored
View File

@@ -3,68 +3,88 @@
// @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 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 useLoadingBar: typeof import('naive-ui')['useLoadingBar']
const useMessage: typeof import('naive-ui')['useMessage']
const useNotification: typeof import('naive-ui')['useNotification']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
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, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue')
export type {
Component,
ComponentPublicInstance,
ComputedRef,
DirectiveBinding,
ExtractDefaultPropTypes,
ExtractPropTypes,
ExtractPublicPropTypes,
InjectionKey,
PropType,
Ref,
MaybeRef,
MaybeRefOrGetter,
VNode,
WritableComputedRef
} from 'vue';
import('vue');
}

View 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

Binary file not shown.

BIN
build/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
build/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

13
build/installer.nsh Normal file
View 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

20
components.d.ts vendored
View File

@@ -1,39 +1,33 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
export {};
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
MPop: typeof import('./src/components/common/MPop.vue')['default']
MusicList: typeof import('./src/components/MusicList.vue')['default']
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']
NTooltip: typeof import('naive-ui')['NTooltip']
PlayBottom: typeof import('./src/components/common/PlayBottom.vue')['default']
PlayListsItem: typeof import('./src/components/common/PlayListsItem.vue')['default']
PlaylistType: typeof import('./src/components/PlaylistType.vue')['default']
RecommendAlbum: typeof import('./src/components/RecommendAlbum.vue')['default']
RecommendSinger: typeof import('./src/components/RecommendSinger.vue')['default']
RecommendSonglist: typeof import('./src/components/RecommendSonglist.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SearchItem: typeof import('./src/components/common/SearchItem.vue')['default']
SongItem: typeof import('./src/components/common/SongItem.vue')['default']
}
}

3
dev-app-update.yml Normal file
View File

@@ -0,0 +1,3 @@
provider: generic
url: https://example.com/auto-updates
updaterCacheDirName: electron-lan-file-updater

BIN
docs/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
docs/image1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
docs/image2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
docs/image3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
docs/image4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

45
electron-builder.yml Normal file
View 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/

View File

@@ -1,18 +0,0 @@
{
"appId": "com.alger.music",
"productName": "AlgerMusic",
"directories": {
"output": "dist_electron"
},
"files": ["dist/**/*", "package.json", "app.js", "electron/**/*"],
"win": {
"icon": "public/icon.png",
"target": "nsis",
"extraFiles": [
{
"from": "installer/installer.nsh",
"to": "$INSTDIR"
}
]
}
}

61
electron.vite.config.ts Normal file
View 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}`), '')
}
}
}
}
});

View File

@@ -1,25 +0,0 @@
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
minimize: () => ipcRenderer.send('minimize-window'),
maximize: () => ipcRenderer.send('maximize-window'),
close: () => ipcRenderer.send('close-window'),
dragStart: (data) => ipcRenderer.send('drag-start', data),
miniTray: () => ipcRenderer.send('mini-tray'),
restart: () => ipcRenderer.send('restart'),
})
const electronHandler = {
ipcRenderer: {
setStoreValue: (key, value) => {
ipcRenderer.send("setStore", key, value)
},
getStoreValue(key) {
const resp = ipcRenderer.sendSync("getStore", key)
return resp
},
}
}
contextBridge.exposeInMainWorld('electron', electronHandler)

View File

@@ -1,5 +0,0 @@
{
"version": "1.3.0",
"isProxy": false,
"author": "alger"
}

View File

@@ -1,25 +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="./public/icon/iconfont.css"
/>
<link rel="stylesheet" href="./public/css/animate.css" />
<link rel="stylesheet" href="./public/css/base.css" />
<style>
:root {
--animate-delay: 0.5s;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,47 +1,169 @@
{
"name": "alger-music",
"version": "1.3.0",
"description": "这是一个用于音乐播放的应用程序。",
"name": "AlgerMusicPlayer",
"version": "4.3.0",
"description": "Alger Music Player",
"author": "Alger <algerkc@qq.com>",
"main": "app.js",
"main": "./out/main/index.js",
"homepage": "https://github.com/algerkong/AlgerMusicPlayer",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"es": "vite && electron .",
"start": "set NODE_ENV=development&&electron .",
"e:b": "electron-builder --config ./electron.config.json",
"eb": "vite build && e:b"
"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",
"@vueuse/core": "^10.7.1",
"autoprefixer": "^9.8.6",
"axios": "^0.21.1",
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"@unblockneteasemusic/server": "^0.27.8-patch.1",
"electron-store": "^8.1.0",
"lodash": "^4.17.21",
"naive-ui": "^2.34.4",
"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-updater": "^6.1.7",
"font-list": "^1.5.1",
"netease-cloud-music-api-alger": "^4.26.1",
"node-id3": "^0.2.9",
"node-machine-id": "^1.1.12",
"vue-i18n": "9"
},
"devDependencies": {
"@sicons/antd": "^0.10.0",
"@vicons/antd": "^0.10.0",
"@vitejs/plugin-vue": "^4.2.3",
"@vue/compiler-sfc": "^3.3.4",
"electron": "^28.0.0",
"electron-builder": "^24.9.1",
"typescript": "^4.3.2",
"unplugin-auto-import": "^0.17.2",
"unplugin-vue-components": "^0.26.0",
"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": "^1.0.2",
"@electron-toolkit/eslint-config-ts": "^2.0.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": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"@vitejs/plugin-vue": "^5.0.5",
"@vue/compiler-sfc": "^3.5.0",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
"@vue/runtime-core": "^3.5.0",
"@vueuse/core": "^11.0.3",
"@vueuse/electron": "^11.0.3",
"animate.css": "^4.1.1",
"autoprefixer": "^10.4.20",
"axios": "^1.7.7",
"cross-env": "^7.0.3",
"electron": "^35.0.2",
"electron-builder": "^25.1.8",
"electron-vite": "^3.0.0",
"eslint": "^8.57.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-simple-import-sort": "^12.0.0",
"eslint-plugin-vue": "^9.26.0",
"eslint-plugin-vue-scoped-css": "^2.7.2",
"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"
}
],
"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/favicon.ico",
"target": [
{
"target": "nsis",
"arch": [
"x64",
"ia32"
]
}
],
"artifactName": "${productName}-${version}-win-${arch}.${ext}",
"requestedExecutionLevel": "asInvoker"
},
"linux": {
"icon": "resources/icon.png",
"target": [
{
"target": "AppImage",
"arch": [
"x64"
]
},
{
"target": "deb",
"arch": [
"x64"
]
}
],
"artifactName": "${productName}-${version}-linux-${arch}.${ext}",
"category": "Audio",
"maintainer": "Alger <algerkc@qq.com>"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"installerIcon": "resources/favicon.ico",
"uninstallerIcon": "resources/favicon.ico",
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "AlgerMusicPlayer",
"include": "build/installer.nsh"
}
}
}

View File

@@ -1,6 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
autoprefixer: {}
}
};

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +0,0 @@
body{
background-color: #000;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

BIN
resources/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

BIN
resources/icon.icns Normal file

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
resources/icon_16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

BIN
resources/icons/next.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

BIN
resources/icons/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

BIN
resources/icons/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

BIN
resources/icons/prev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

10
resources/manifest.json Normal file
View File

@@ -0,0 +1,10 @@
{
"name": "Alger Music PWA",
"icons": [
{
"src": "./icon.png",
"type": "image/png",
"sizes": "256x256"
}
]
}

View File

@@ -1,37 +0,0 @@
<template>
<div class="app">
<audio id="MusicAudio" ref="audioRef" :src="playMusicUrl" :autoplay="play"></audio>
<n-config-provider :theme="darkTheme">
<n-dialog-provider>
<router-view></router-view>
</n-dialog-provider>
</n-config-provider>
</div>
</template>
<script lang="ts" setup>
import { darkTheme } from 'naive-ui'
import store from '@/store'
const audio = ref<HTMLAudioElement | null>(null)
const playMusicUrl = computed(() => store.state.playMusicUrl as string)
// 是否播放
const play = computed(() => store.state.play as boolean)
const windowData = window as any
onMounted(()=>{
if(windowData.electron){
const setData = windowData.electron.ipcRenderer.getStoreValue('set');
store.commit('setSetData', setData)
}
})
</script>
<style lang="scss" scoped >
div {
box-sizing: border-box;
}
.app {
user-select: none;
}
</style>

View File

@@ -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");
};

View File

@@ -1,22 +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 } })
}
// 获取歌曲详情
export const getMusicDetail = (ids: Array<number>) => {
return request.get("/song/detail", { params: { ids: ids.join(",")}})
}
// 根据音乐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 } })
}

View File

@@ -1,30 +0,0 @@
import { IData } from '@/type'
import { IMvItem, IMvUrlData } from '@/type/mv'
import request from '@/utils/request'
// 获取 mv 排行
export const getTopMv = (limit: number) => {
return request.get<IData<Array<IMvItem>>>('/top/mv', {
params: {
limit,
},
})
}
// 获取 mv 数据
export const getMvDetail = (mvid: string) => {
return request.get('/mv/detail', {
params: {
mvid,
},
})
}
// 获取 mv 地址
export const getMvUrl = (id: Number) => {
return request.get<IData<IMvUrlData>>('/mv/url', {
params: {
id,
},
})
}

View File

@@ -1,13 +0,0 @@
import request from "@/utils/request"
import { ISearchDetail } from "@/type/search"
interface IParams {
keywords: string
type: number
}
// 搜索内容
export const getSearch = (params: IParams) => {
return request.get<any>('/cloudsearch', {
params,
})
}

View File

@@ -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 } });
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,75 +0,0 @@
<template>
<n-drawer :show="show" height="70vh" placement="bottom" :drawer-style="{ backgroundColor: 'transparent' }">
<div class="music-page">
<i class="iconfont icon-icon_error music-close" @click="close"></i>
<div class="music-title">{{ name }}</div>
<!-- 歌单歌曲列表 -->
<div class="music-list">
<n-scrollbar >
<div v-for="(item, index) in songList" :key="item.id" :class="setAnimationClass('animate__bounceInUp')"
:style="setAnimationDelay(index, 100)">
<SongItem :item="formatDetail(item)" @play="handlePlay" />
</div>
<PlayBottom/>
</n-scrollbar>
</div>
</div>
</n-drawer>
</template>
<script setup lang="ts">
import { useStore } from 'vuex'
import { setAnimationClass, setAnimationDelay } from "@/utils";
import SongItem from "@/components/common/SongItem.vue";
import PlayBottom from './common/PlayBottom.vue';
const store = useStore()
const props = defineProps<{
show: boolean;
name: string;
songList: any[]
}>()
const emit = defineEmits(['update:show'])
const formatDetail = computed(() => (detail: any) => {
let song = {
artists: detail.ar,
name: detail.al.name,
id: detail.al.id,
}
detail.song = song
detail.picUrl = detail.al.picUrl
return detail
})
const handlePlay = (item: any) => {
const tracks = props.songList || []
store.commit('setPlayList', tracks)
}
const close = () => {
emit('update:show', false)
}
</script>
<style scoped lang="scss">
.music {
&-page {
@apply px-8 w-full h-full bg-black bg-opacity-75 rounded-t-2xl;
backdrop-filter: blur(20px);
}
&-title {
@apply text-lg font-bold text-white p-4;
}
&-close {
@apply absolute top-4 right-8 cursor-pointer text-white text-3xl;
}
&-list {
height: calc(100% - 60px);
}
}
</style>

View File

@@ -1,79 +0,0 @@
<template>
<!-- 歌单分类列表 -->
<div class="play-list-type">
<div class="title" :class="setAnimationClass('animate__fadeInLeft')">歌单分类</div>
<div>
<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>
</div>
</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>

View File

@@ -1,86 +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)"
@click="handleClick(item)"
>
<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>
<MusicList v-model:show="showMusic" :name="albumName" :song-list="songList" />
</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";
import { getAlbum } from "@/api/list";
const albumData = ref<IAlbumNew>()
const loadAlbumList = async () => {
const { data } = await getNewAlbum();
albumData.value = data
}
const showMusic = ref(false)
const songList = ref([])
const albumName = ref('')
const handleClick = async (item:any) => {
albumName.value = item.name
showMusic.value = true
const res = await getAlbum(item.id)
songList.value = res.data.songs.map((song:any)=>{
song.al.picUrl = song.al.picUrl || item.picUrl
return song
})
}
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>

View File

@@ -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: 280px;
}
&-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>

View File

@@ -1,66 +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 { 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) => {
store.commit('setPlayList', recommendMusic.value?.result)
}
</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>

View File

@@ -1,42 +0,0 @@
<script lang="ts" setup>
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"></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>

View File

@@ -1,15 +0,0 @@
<template>
<div class="bottom" v-if="isPlay"></div>
</template>
<script setup lang="ts">
import { useStore } from 'vuex';
const store = useStore()
const isPlay = computed(() => store.state.isPlay as boolean)
</script>
<style lang="scss" scoped>
.bottom{
@apply h-28;
}
</style>

View File

@@ -1,70 +0,0 @@
<template>
<div class="search-item" @click="handleClick">
<div class="search-item-img">
<n-image
:src="getImgUrl(item.picUrl, 'album')"
lazy
preview-disabled
/>
</div>
<div class="search-item-info">
<div class="search-item-name">{{ item.name }}</div>
<div class="search-item-artist">{{ item.desc}}</div>
</div>
<MusicList v-model:show="showMusic" :name="item.name" :song-list="songList" />
</div>
</template>
<script setup lang="ts">
import { getImgUrl } from '@/utils'
import type {Album} from '@/type/album'
import { getAlbum } from '@/api/list';
const props = defineProps<{
item: {
picUrl: string
name: string
desc: string
type: string
[key: string]: any
}
}>()
const songList = ref([])
const showMusic = ref(false)
const handleClick = async () => {
showMusic.value = true
if(props.item.type === '专辑'){
const res = await getAlbum(props.item.id)
songList.value = res.data.songs.map((song:any)=>{
song.al.picUrl = song.al.picUrl || props.item.picUrl
return song
})
}
}
</script>
<style scoped lang="scss">
.search-item{
@apply rounded-3xl p-3 flex items-center hover:bg-gray-800 transition;
margin: 0 10px;
.search-item-img{
@apply w-12 h-12 mr-4 rounded-2xl overflow-hidden;
}
.search-item-info{
&-name{
@apply text-white text-sm text-center;
}
&-artist{
@apply text-gray-400 text-xs text-center;
}
}
}
</style>

View File

@@ -1,152 +0,0 @@
<template>
<div class="song-item" :class="{'song-mini': mini}">
<n-image
v-if="item.picUrl "
:src="getImgUrl( item.picUrl, '40y40')"
class="song-item-img"
lazy
preview-disabled
/>
<div class="song-item-content">
<div class="song-item-content-title">
<n-ellipsis class="text-ellipsis" line-clamp="1">{{
item.name
}}</n-ellipsis>
</div>
<div class="song-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="song-item-operating">
<div class="song-item-operating-like">
<i class="iconfont icon-likefill"></i>
</div>
<div
class="song-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 { getImgUrl } from '@/utils'
const props = withDefaults(defineProps<{
item: SongResult
mini?: boolean
}>(), {
mini: false
})
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)
emits('play', item)
}
</script>
<style lang="scss" scoped>
// 配置文字不可选中
.text-ellipsis {
width: 100%;
}
.song-item {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
@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 ml-4;
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;
}
}
}
.song-mini{
@apply p-2 rounded-2xl;
.song-item{
@apply p-0;
&-img{
@apply w-10 h-10 mr-2;
}
&-content{
@apply flex-1;
&-title{
@apply text-sm;
}
&-name{
@apply text-xs;
}
}
&-operating{
@apply pl-2;
.iconfont{
@apply text-base;
}
&-like{
@apply mr-1;
}
&-play{
@apply w-8 h-8;
}
}
}
}
</style>

View File

@@ -1,65 +0,0 @@
export const USER_SET_OPTIONS = [
// {
// label: '打卡',
// key: 'card',
// },
// {
// label: '听歌升级',
// key: 'card_music',
// },
// {
// label: '歌曲次数',
// key: 'listen',
// },
{
label: '退出登录',
key: 'logout',
},
{
label: '设置',
key: 'set',
},
]
export const SEARCH_TYPES = [
{
label: '单曲',
key: 1,
},
{
label: '专辑',
key: 10,
},
{
label: '歌手',
key: 100,
},
{
label: '歌单',
key: 1000,
},
{
label: '用户',
key: 1002,
},
{
label: 'MV',
key: 1004,
},
{
label: '歌词',
key: 1006,
},
{
label: '电台',
key: 1009,
},
{
label: '视频',
key: 1014,
},
{
label: '综合',
key: 1018,
},
]

10
src/electron.d.ts vendored
View File

@@ -1,10 +0,0 @@
declare global {
interface Window {
electronAPI: {
minimize: () => void
maximize: () => void
close: () => void
dragStart: () => void
}
}
}

View File

@@ -1,39 +0,0 @@
// musicHistoryHooks
import { RemovableRef, useLocalStorage } from '@vueuse/core'
import type { SongResult } from '@/type/music'
export const useMusicHistory = () => {
const musicHistory = useLocalStorage<SongResult[]>('musicHistory', [])
const addMusic = (music: SongResult) => {
const index = musicHistory.value.findIndex((item) => item.id === music.id)
if (index !== -1) {
musicHistory.value[index].count =
(musicHistory.value[index].count || 0) + 1
musicHistory.value.unshift(musicHistory.value.splice(index, 1)[0])
} else {
musicHistory.value.unshift({ ...music, count: 1 })
}
}
const delMusic = (music: any) => {
const index = musicHistory.value.findIndex((item) => item.id === music.id)
if (index !== -1) {
musicHistory.value.splice(index, 1)
}
}
const musicList = ref(musicHistory.value)
watch(
() => musicHistory.value,
() => {
musicList.value = musicHistory.value
}
)
return {
musicHistory,
musicList,
addMusic,
delMusic,
}
}

View File

@@ -1,104 +0,0 @@
import { getMusicLrc } from '@/api/music'
import { ILyric } from '@/type/lyric'
import { getIsMc } from '@/utils'
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.4)
// 增加矫正时间
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,
}

View File

@@ -0,0 +1,5 @@
export default {
hotSongs: 'Hot Songs',
albums: 'Albums',
description: 'Artist Introduction'
};

View File

@@ -0,0 +1,52 @@
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',
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'
},
language: 'Language'
};

109
src/i18n/lang/en-US/comp.ts Normal file
View File

@@ -0,0 +1,109 @@
export default {
installApp: {
description: 'Install the application on the desktop 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: 789288579',
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'
}
};

View File

@@ -0,0 +1,7 @@
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'
};

View File

@@ -0,0 +1,48 @@
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}'
}
};

View File

@@ -0,0 +1,15 @@
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'
};

View File

@@ -0,0 +1,5 @@
export default {
title: 'Play History',
playCount: '{count}',
getHistoryFailed: 'Failed to get play history'
};

View 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
};

View 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'
}
};

View File

@@ -0,0 +1,77 @@
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'
},
playBar: {
expand: 'Expand Lyrics',
collapse: 'Collapse Lyrics',
like: 'Like',
lyric: 'Lyric',
noSongPlaying: 'No song playing',
eq: 'Equalizer',
playList: 'Play List',
playMode: {
sequence: 'Sequence',
loop: 'Loop',
random: 'Random'
},
play: 'Play',
pause: 'Pause',
prev: 'Previous',
next: 'Next',
volume: 'Volume',
favorite: 'Favorite {name}',
unFavorite: 'Unfavorite {name}'
},
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'
}
}
};

View File

@@ -0,0 +1,19 @@
export default {
title: {
hotSearch: 'Hot Search',
searchList: 'Search Results',
searchHistory: 'Search History'
},
button: {
clear: 'Clear',
back: 'Back'
},
loading: {
more: 'Loading...',
failed: 'Search failed'
},
noMore: 'No more results',
error: {
searchFailed: 'Search failed'
}
};

View File

@@ -0,0 +1,225 @@
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'
},
autoPlay: 'Auto Play',
autoPlayDesc: 'Auto resume playback when reopening the app'
},
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'
},
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'
}
}
};

View File

@@ -0,0 +1,18 @@
export default {
menu: {
play: 'Play',
playNext: 'Play Next',
download: 'Download',
addToPlaylist: 'Add to Playlist',
favorite: 'Like',
unfavorite: 'Unlike',
removeFromPlaylist: 'Remove from Playlist'
},
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'
}
};

View File

@@ -0,0 +1,42 @@
export default {
profile: {
followers: 'Followers',
following: 'Following',
level: 'Level'
},
playlist: {
created: 'Created Playlists',
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'
},
follower: {
title: 'Follower List',
noFollowers: 'No Followers',
loadMore: 'Load More'
},
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'
},
message: {
loadFailed: 'Failed to load user page',
deleteSuccess: 'Successfully deleted',
deleteFailed: 'Failed to delete'
}
};

View File

@@ -0,0 +1,5 @@
export default {
hotSongs: '热门歌曲',
albums: '专辑',
description: '艺人介绍'
};

View File

@@ -0,0 +1,51 @@
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: '重试',
validation: {
required: '此项是必填的',
invalidInput: '输入无效',
selectRequired: '请选择一个选项',
numberRange: '请输入 {min} 到 {max} 之间的数字'
},
viewMore: '查看更多',
noMore: '没有更多了',
selectAll: '全选',
expand: '展开',
collapse: '收起',
songCount: '{count}首',
language: '语言',
tray: {
show: '显示',
quit: '退出',
playPause: '播放/暂停',
prev: '上一首',
next: '下一首',
pause: '暂停',
play: '播放'
}
};

107
src/i18n/lang/zh-CN/comp.ts Normal file
View File

@@ -0,0 +1,107 @@
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群789288579',
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: '没有找到相关歌曲'
}
};

View File

@@ -0,0 +1,6 @@
export default {
description: '您的捐赠将用于支持开发和维护工作,包括但不限于服务器维护、域名续费等。',
message: '留言时可留下您的邮箱或 github名称。',
refresh: '刷新列表',
toDonateList: '请我喝咖啡'
};

View File

@@ -0,0 +1,47 @@
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}'
}
};

View File

@@ -0,0 +1,11 @@
export default {
title: '我的收藏',
count: '共 {count} 首',
batchDownload: '批量下载',
download: '下载 ({count})',
emptyTip: '还没有收藏歌曲',
downloadSuccess: '下载完成',
downloadFailed: '下载失败',
downloading: '正在下载中,请稍候...',
selectSongsFirst: '请先选择要下载的歌曲'
};

View File

@@ -0,0 +1,5 @@
export default {
title: '播放历史',
playCount: '{count}',
getHistoryFailed: '获取历史记录失败'
};

View 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
};

View File

@@ -0,0 +1,22 @@
export default {
title: {
qr: '扫码登录',
phone: '手机号登录'
},
qrTip: '使用网易云APP扫码登录',
phoneTip: '使用网易云账号登录',
placeholder: {
phone: '手机号',
password: '密码'
},
button: {
login: '登录',
switchToQr: '扫码登录',
switchToPhone: '手机号登录'
},
message: {
loginSuccess: '登录成功',
loadError: '加载登录信息时出错',
qrCheckError: '检查二维码状态时出错'
}
};

Some files were not shown because too many files have changed in this diff Show More