1
0
mirror of https://github.com/hanxi/xiaomusic.git synced 2025-12-06 14:52:50 +08:00

Compare commits

..

747 Commits

Author SHA1 Message Date
涵曦
0db3e4cada bump: version 0.3.54 → 0.3.55 2024-12-04 10:22:27 +08:00
涵曦
fa21d02e1d build: update static version 2024-12-04 10:22:26 +08:00
涵曦
be48fe9f54 fix: 修复播放接口报错问题 2024-12-04 10:17:06 +08:00
徒言
042a91336e chore: 引导页新增小程序码 (#290) 2024-12-04 09:54:38 +08:00
涵曦
790a4cd808 bump: version 0.3.53 → 0.3.54 2024-12-04 01:41:29 +08:00
涵曦
70d029f4d1 build: update static version 2024-12-04 01:41:28 +08:00
52fisher
0472ad3188 fix: 安卓低版本webview对audio的src为空值的报错 (#289) 2024-12-04 01:30:07 +08:00
涵曦
733c44d12f feat: 新增最近新增歌单 close #273 2024-12-03 21:44:48 +08:00
涵曦
8c92afd09b fix: 修复M01语音播放问题,X08C X08E X8F 型号默认采用型号兼容模式 see #30 2024-12-03 21:25:51 +08:00
涵曦
742929b9d5 Update README.md 2024-12-03 21:18:26 +08:00
涵曦
4766e08ff4 Update README.md 2024-12-03 21:17:11 +08:00
涵曦
99c4296c7a bump: version 0.3.52 → 0.3.53 2024-12-03 12:15:09 +08:00
涵曦
0ae24f4810 build: update static version 2024-12-03 12:15:08 +08:00
涵曦
77cca0d18c fix: 解决播放接口修改后播放失败的问题 2024-12-03 12:14:57 +08:00
涵曦
ddc24595df bump: version 0.3.51 → 0.3.52 2024-12-03 12:02:39 +08:00
涵曦
ce1bfb164c build: update static version 2024-12-03 12:02:39 +08:00
涵曦
78dbba6c1f fix: 修复播放接口参数错误的问题 2024-12-03 12:02:14 +08:00
涵曦
7eb0ab1e46 bump: version 0.3.50 → 0.3.51 2024-12-03 09:06:51 +08:00
涵曦
0a2e3cc579 build: update static version 2024-12-03 09:06:50 +08:00
涵曦
75ec336285 fix: 修复空配置启动失败问题 close #284 2024-12-03 09:02:15 +08:00
涵曦
a19d53f000 bump: version 0.3.49 → 0.3.50 2024-12-03 07:11:40 +08:00
涵曦
cdb6190e7a build: update static version 2024-12-03 07:11:40 +08:00
涵曦
1aa9b58561 style: 更新 gitignore 2024-12-02 23:17:54 +08:00
涵曦
6a990f48d0 fix: 更新 yt-dlp ,解决 B 站下载问题 close #279 2024-12-02 23:17:12 +08:00
涵曦
82e92a0380 Update README.md 2024-12-02 21:43:56 +08:00
涵曦
ecce5c8848 feat: 修改日志文件的默认值 2024-12-02 21:38:31 +08:00
涵曦
87fb34e5c9 feat: 新增修改tag缓存信息的接口 close #266 2024-12-02 12:27:09 +08:00
涵曦
df3c4b7fa9 feat: 新增专用的播放歌曲和播放歌单接口,解决默认口令提示词被修改了导致后台失效的问题 2024-12-02 11:40:26 +08:00
涵曦
cb2af0ee9f feat: 统计设备型号 2024-12-02 11:08:19 +08:00
涵曦
8f9bba0ca3 refactor: 调整设置页面 2024-12-01 22:47:56 +08:00
涵曦
b86e8d3196 Update README.md 2024-12-01 22:41:47 +08:00
52fisher
65067346f3 feat: 页面与设置中的HOST不一致时弹窗提醒 (#281) 2024-12-01 22:23:50 +08:00
52fisher
865b412fb7 fix: 网页播放audio获取到错误url无法播放时提醒用户 (#280) 2024-12-01 12:33:05 +08:00
涵曦
441fffd59e Update README.md 2024-11-29 10:02:51 +08:00
52fisher
2ee7b956cf feat: 未发现小爱设备时给予提示 (#278)
fix: input标签自闭合
2024-11-29 07:23:57 +08:00
涵曦
126bfa43a2 feat: 优化设置页面提示 2024-11-28 23:29:06 +08:00
涵曦
3a4d702070 bump: version 0.3.48 → 0.3.49 2024-11-28 20:30:36 +08:00
涵曦
2c7a09b9d4 build: update static version 2024-11-28 20:30:36 +08:00
涵曦
74837baaef feat: 临时文件目录支持配置 #99 2024-11-28 18:55:18 +08:00
涵曦
def63b2407 feat: 新增单曲播放和顺序播放功能 close #277 2024-11-28 18:07:40 +08:00
Formatter [BOT]
93d2047c7a Auto-format code 🧹🌟🤖 2024-11-27 10:31:40 +00:00
dludream
9a1b9d1949 fix: 修复中文数字转换函数对'十、十一'等数字的处理 (#275) 2024-11-27 18:31:12 +08:00
涵曦
88fa4318dc Update setting.html 2024-11-27 14:29:59 +08:00
涵曦
0bc4bc8b13 feat: 设置播放类型支持配置语音提示词,定时任务支持设置播放类型 2024-11-26 23:18:25 +08:00
涵曦
ca2ddcd89c bump: version 0.3.47 → 0.3.48 2024-11-20 00:31:57 +08:00
涵曦
78acc35a62 build: update static version 2024-11-20 00:31:56 +08:00
涵曦
323a832a9e feat: 支持替换默认口令,而不是追加 close #259 2024-11-20 00:24:11 +08:00
涵曦
0c99f4d537 feat: 新增自定义个歌单接口 #242 2024-11-20 00:09:54 +08:00
涵曦
b2737f745d fix: 锁定 PWA 应用旋转方向 2024-11-14 22:55:41 +08:00
涵曦
464a1e1bd1 bump: version 0.3.46 → 0.3.47 2024-11-14 08:15:40 +08:00
涵曦
7bd613f74c build: update static version 2024-11-14 08:15:39 +08:00
涵曦
3fb0a3bae9 fix: 修复 PWA 应用有密码时报错的问题 2024-11-14 00:51:53 +08:00
涵曦
b4f0e7b349 feat: 支持 PWA 应用安装 2024-11-14 00:26:19 +08:00
Formatter [BOT]
7c1ce81b5e Auto-format code 🧹🌟🤖 2024-11-13 15:28:49 +00:00
涵曦
052027fb50 feat: 新增模糊匹配测试用例 2024-11-13 23:28:10 +08:00
涵曦
b4d8434507 fix: 修复播放顺序没有按数字排序的问题 close #249 2024-11-12 09:14:20 +08:00
涵曦
ec6018eabf bump: version 0.3.45 → 0.3.46 2024-11-08 23:22:21 +08:00
涵曦
bdeb2b5647 build: update static version 2024-11-08 23:22:21 +08:00
涵曦
657e046379 fix: 添加依赖库 requests 2024-11-08 23:11:20 +08:00
涵曦
bd6dd43737 refactor: 依赖库已经支持分段获取静态文件,重构代码 2024-11-08 21:19:38 +08:00
涵曦
5766919e69 feat: 升级依赖库 2024-11-08 15:54:17 +08:00
涵曦
0fe5f74d12 bump: version 0.3.44 → 0.3.45 2024-11-08 00:43:14 +08:00
涵曦
a2d6d1c357 build: update static version 2024-11-08 00:43:13 +08:00
涵曦
5c61ef4b26 fix: 修复定时任务报错问题 2024-11-07 22:43:27 +08:00
涵曦
e57a02a192 feat: 定时任务支持设置音量 2024-11-07 22:35:17 +08:00
涵曦
5587411e37 feat: 播放歌单口令支持配置 2024-11-07 22:30:05 +08:00
涵曦
95f756fb28 bump: version 0.3.43 → 0.3.44 2024-11-01 10:14:31 +08:00
涵曦
25ed90efa3 build: update static version 2024-11-01 10:14:29 +08:00
涵曦
147bbcdd4c build: 编译优化 2024-11-01 09:53:21 +08:00
涵曦
8dd9cfe0ac feat: 日志时间里加上日期 2024-11-01 09:27:56 +08:00
涵曦
089bafd693 fix: 修复搜索失败的问题 2024-11-01 09:22:55 +08:00
涵曦
13b72bff97 Update build-base-image.yml 2024-10-31 09:29:35 +08:00
涵曦
3e959a08f0 bump: version 0.3.42 → 0.3.43 2024-10-30 21:39:08 +08:00
涵曦
3d1517854c build: update static version 2024-10-30 21:39:08 +08:00
涵曦
e65c2ca3c9 build: 镜像编译优化 2024-10-30 19:51:10 +08:00
涵曦
6d7ff48913 build: 修改编译脚本 2024-10-30 18:50:26 +08:00
涵曦
7291826b56 Update build-base-image.yml 2024-10-30 16:08:22 +08:00
涵曦
fa06a7ad17 Update build-base-image.yml 2024-10-30 14:27:44 +08:00
涵曦
614cff3f05 Update release.yml 2024-10-30 13:14:54 +08:00
涵曦
a8d924e31f Update build-base-image.yml 2024-10-30 13:14:23 +08:00
涵曦
a9a6ead73f Update release.yml 2024-10-30 12:58:18 +08:00
涵曦
bd41735df2 Update build-base-image.yml 2024-10-30 12:57:01 +08:00
涵曦
655f0f9ab7 Update ci.yml 2024-10-30 12:56:03 +08:00
涵曦
a003b6d0bf Update build-base-image.yml 2024-10-30 12:53:45 +08:00
涵曦
16f224b8e5 Update build-base-image.yml 2024-10-30 12:53:00 +08:00
涵曦
d4ca2c886f Update install_dependencies.sh 2024-10-30 12:48:27 +08:00
涵曦
48b9e0a837 Update ci.yml 2024-10-30 12:34:15 +08:00
涵曦
38df7aa6b9 Update ci.yml 2024-10-30 12:32:56 +08:00
涵曦
29756c29a7 Update ci.yml 2024-10-30 11:29:06 +08:00
涵曦
89287164ad Update build-base-image.yml 2024-10-30 11:11:33 +08:00
涵曦
81fb330db9 Update ci.yml 2024-10-30 11:11:10 +08:00
涵曦
365954ecb0 Update ci.yml 2024-10-30 11:09:32 +08:00
涵曦
d7fd7c43dc Update ci.yml 2024-10-30 11:07:55 +08:00
涵曦
e48c6456fc Update ci.yml 2024-10-30 10:45:41 +08:00
涵曦
193e1885e0 Update ci.yml 2024-10-30 10:41:04 +08:00
涵曦
9acd5820ef Update ci.yml 2024-10-30 10:39:20 +08:00
涵曦
119283693a Update ci.yml 2024-10-30 10:31:14 +08:00
涵曦
dc9eb96a27 Update ci.yml 2024-10-30 10:23:26 +08:00
涵曦
c022b5a0b1 Update ci.yml 2024-10-30 10:16:46 +08:00
涵曦
bcbbfc5f52 Update ci.yml 2024-10-30 10:05:32 +08:00
涵曦
3da07ce816 Update ci.yml 2024-10-30 09:54:19 +08:00
涵曦
8629c16fe4 build: 编译镜像 2024-10-30 09:50:22 +08:00
涵曦
ac6fbd1b82 Update Dockerfile.base 2024-10-30 09:25:29 +08:00
涵曦
6995afed16 Update ci.yml 2024-10-30 09:19:41 +08:00
涵曦
b49e250488 build: 修改镜像 2024-10-30 09:03:14 +08:00
涵曦
5032747f1e build: 修改镜像 2024-10-30 08:51:59 +08:00
涵曦
f749edcf16 build: 新增编译基础镜像 2024-10-30 08:42:32 +08:00
52fisher
14598aedee feat: 播放列表可以删除当前歌曲(!危险操作,请在设置中心开启相关功能) (#250) 2024-10-30 06:38:23 +08:00
涵曦
8845148cce fix: 修复谷歌统计导致的卡顿问题 2024-10-30 00:13:41 +08:00
涵曦
1513a59726 fix: 解决挂载网盘卡死的问题 2024-10-28 02:56:23 +08:00
涵曦
497c1f34ef test: 修改测试用例 2024-10-27 09:30:29 +08:00
52fisher
77920dffac feature: Update Theme (#248)
* pure:修复群内反馈的部分分辨率按钮错位问题
xmusicplayer: 新增收藏、取消收藏功能

* fix: 安卓某些浏览器duration一直为0的问题
2024-10-26 15:55:12 +08:00
涵曦
c452136537 feat: 插件自定义口令支持获取语音输入内容 #105 2024-10-24 19:35:09 +08:00
涵曦
aa2992b5d7 bump: version 0.3.41 → 0.3.42 2024-10-24 11:00:00 +08:00
涵曦
7fcd3eeae5 build: update static version 2024-10-24 10:59:59 +08:00
涵曦
30194272d9 Update ci.yml 2024-10-23 19:01:53 +08:00
涵曦
1b71301b06 Update Dockerfile 2024-10-23 18:26:14 +08:00
涵曦
13bbff8d67 Update ci.yml 2024-10-23 17:22:47 +08:00
涵曦
ae3507b811 Update ci.yml 2024-10-23 16:53:37 +08:00
涵曦
bdfb0a4127 ci: update ci 2024-10-23 15:31:04 +08:00
涵曦
3d0a38cbb8 build: 尝试修复 arm 平台的容器下不能启动的问题 2024-10-23 14:08:29 +08:00
涵曦
f469f63d97 fix: 尝试修复缺少 libtiff.so.6 文件的问题 #244 2024-10-23 06:22:00 +08:00
涵曦
6544bb2ff1 fix: 修复默认主题播放歌曲输入框空的情况 2024-10-21 22:59:38 +08:00
涵曦
668237401e fix: 尝试修复停止后自动播放的问题 2024-10-21 22:53:37 +08:00
涵曦
139ebf37c4 Update README.md 2024-10-21 19:40:33 +08:00
涵曦
7146d61fcb Update README.md 2024-10-18 01:14:41 +08:00
涵曦
6033c1a6fc Update README.md 2024-10-18 01:14:02 +08:00
涵曦
e77a4fc10d bump: version 0.3.40 → 0.3.41 2024-10-17 23:49:21 +08:00
涵曦
bf2909d35a build: update static version 2024-10-17 23:49:20 +08:00
涵曦
b3255a17ce fix: 修复获取标签信息报错问题 2024-10-17 23:35:38 +08:00
Gao, Ruiyuan
a3140ff23a fix: remove_id3_tags return None if no id3 tag (#238)
* fix: remove_id3_tags return None if no id3 tag

* Auto-format code 🧹🌟🤖

---------

Co-authored-by: Formatter [BOT] <runner@fv-az885-171.m43iyx0u3v4e1h2pvvlabjukza.cx.internal.cloudapp.net>
2024-10-17 11:36:48 +08:00
Gao, Ruiyuan
becfdbf338 fix: bug in del_music (#237) 2024-10-17 10:25:25 +08:00
涵曦
8b74b664f0 feat: 设置默认时区为东八区 closed #236 2024-10-17 08:18:54 +08:00
涵曦
0daba20885 bump: version 0.3.39 → 0.3.40 2024-10-16 21:44:05 +08:00
涵曦
8ac39af8cd build: update static version 2024-10-16 21:44:04 +08:00
涵曦
d6fb62eb8e feat: 默认主题的播放列表上显示歌曲数量 2024-10-16 13:00:09 +08:00
涵曦
24ac876632 fix: 修复播放卡顿问题(谷歌统计地址无法访问的情况) 2024-10-16 12:42:22 +08:00
涵曦
fcdb7bf035 Update README.md 2024-10-16 01:14:22 +08:00
涵曦
4c927c56c0 bump: version 0.3.38 → 0.3.39 2024-10-15 21:58:42 +08:00
涵曦
3a7672982f build: update static version 2024-10-15 21:58:41 +08:00
涵曦
a3ddca05a3 style: 修改提示 2024-10-15 20:34:39 +08:00
涵曦
e9dba716b7 feat: 固定的播放列表全部初始化 2024-10-15 12:02:19 +08:00
Gao, Ruiyuan
6ed5d0cb5f bug: sort results from keyword search (#232)
* bug: sort results from keyword search

* Auto-format code 🧹🌟🤖

---------

Co-authored-by: Formatter [BOT] <runner@fv-az1538-928.upsp13a5k4ou3ds4kr34xzh2lh.cx.internal.cloudapp.net>
2024-10-15 06:42:11 +08:00
Gao, Ruiyuan
44819de3a5 refactor: 修改默认UI播放提示词 (#233) 2024-10-15 06:41:05 +08:00
52fisher
043ad12dec fix: pure主题 当前设备与远程设备未正确区分的问题 (#234)
feat: 生产环境与开发环境接口分离、关于页面增加返回到主页的链接
update: 支持https页面未及时更新的问题
2024-10-15 06:36:46 +08:00
Gao, Ruiyuan
cc5facdf4f fix: static和doc添加basic auth (#231)
* bug: static和doc添加basic auth

* Auto-format code 🧹🌟🤖

---------

Co-authored-by: Formatter [BOT] <runner@fv-az1114-199.rwkmm4horakexguyojjdchjroh.cx.internal.cloudapp.net>
2024-10-14 14:00:55 +08:00
涵曦
0a603ad507 bump: version 0.3.37 → 0.3.38 2024-10-14 06:31:19 +08:00
涵曦
05a193fb4b build: update static version 2024-10-14 06:31:19 +08:00
涵曦
5e604c5bac refactor: 新增清理缓存按钮 2024-10-14 05:39:31 +08:00
52fisher
bfd1b313ab fix: xplayer 收藏歌曲、取消收藏 (#230)
* fix: 收藏歌曲、取消收藏
feat: 列表加载优化,现在渲染速度飞起
2024-10-14 04:52:35 +08:00
徒言
5c095b2395 feat: 播放状态接口返回当前播放列表 (#229) 2024-10-14 04:50:32 +08:00
涵曦
5fc3348cfc feat: 新增口令收藏歌曲用来收藏当前播放的歌曲 2024-10-13 20:58:30 +08:00
涵曦
42e44caf0d fix: 修复型号M01获取对话记录时间戳的问题 2024-10-13 12:31:25 +08:00
涵曦
2e864eed7c fix: 修复型号M01无法获取到对话记录的问题 2024-10-13 01:06:38 +08:00
Gao, Ruiyuan
f249edbd6a feat: 默认UI搜索框动态显示 (#228) 2024-10-10 21:45:45 +08:00
Gao, Ruiyuan
382bc7a620 refactor: 优化默认UI的搜索框#226 (#227) 2024-10-09 15:12:45 +08:00
Gao, Ruiyuan
a0eddd429e bug: 可以精确匹配结果时不采用模糊搜索策略 (#225) 2024-10-09 15:08:51 +08:00
Gao, Ruiyuan
e60dc12a12 bug: fix redirect (#223) 2024-10-09 15:05:27 +08:00
涵曦
c9384aac08 refactor: 修复告警 2024-10-09 07:02:58 +08:00
涵曦
9d9939be9f feat: 文件转换逻辑延迟到读取文件的时候 see #218 2024-10-09 01:40:59 +08:00
52fisher
e25e1748c4 refactor: 体验优化,音乐列表缓存 (#222)
feat: 重写播放组件,现在支持歌词显示了
2024-10-09 01:19:44 +08:00
52fisher
4d5d120e39 fix: 使用小爱设备播放时组件异常的问题 (#217) 2024-10-07 10:02:24 +08:00
涵曦
9c85daf712 feat: 使用 /cmdstatus 接口来判断异步任务是否完成 2024-10-07 04:28:07 +08:00
涵曦
01ed21f83d feat: 新增接口 /cmdstatus 用于查询异步任务是否执行完毕 2024-10-07 04:19:45 +08:00
52fisher
b3af44f42c feat: XMusicPlayer播放器主题优化 (#216)
* Update: XMusicPlayer播放器主题

* fix: 播放全部使用sticky不生效的问题
feat: 播放全部功能可用,新的音量条和进度条, 音乐组件全屏化优化
fix: 横向滚动条

* feat: 代码优化
refactor: 重写懒加载,移除不必要的组件,重写弹窗组件
feat: 自适应夜间模式,音乐列表缓存优化
feat: 组件渲染和数据分离,现在打开更加丝滑了

* update: 无用css处理
2024-10-04 23:34:57 +08:00
涵曦
9306a50123 fix: 修复图片获取失败的问题 2024-10-03 17:41:22 +08:00
涵曦
54d2a5f0af refactor: 修改为播放选中歌曲 2024-10-03 07:23:48 +08:00
涵曦
8c8e8de142 Update README.md 2024-10-02 17:57:37 +08:00
52fisher
20fe4739b5 feat: XMusicPlayer播放器主题 (#214) 2024-10-02 09:04:04 +08:00
涵曦
774ac81b4b fix: 修复 yt-dlp-cookies 报错 2024-09-30 00:53:57 +08:00
涵曦
10a529220c Update README.md 2024-09-30 00:11:11 +08:00
涵曦
7c9d48a9fa build: 修复依赖库问题 2024-09-29 23:57:03 +08:00
涵曦
a76d526569 Update README.md 2024-09-29 22:51:59 +08:00
涵曦
e2d71266c5 fix: 修复自定义口令末尾多余逗号的情况 2024-09-29 10:22:01 +08:00
涵曦
86110a2e65 feat: 新增 yt-dlp cookies 文件参数支持 2024-09-29 10:19:50 +08:00
涵曦
4330f61888 feat: 新增批量下载歌曲工具 2024-09-27 19:03:49 +08:00
涵曦
4951cea269 feat: 新增后台网站图标 2024-09-27 10:57:18 +08:00
涵曦
ed1a4e77f6 Update README.md 2024-09-26 22:44:06 +08:00
Gao, Ruiyuan
40a3e24071 bugfix: cannot can async.run when there is a running loop (#207) 2024-09-26 22:34:00 +08:00
Gao, Ruiyuan
daeb0ae4b6 bugfix: async func should have await (#205) 2024-09-26 11:52:40 +08:00
涵曦
02d9987ad7 fix: 修复windows下路径问题 2024-09-26 07:32:02 +08:00
涵曦
aa7b25cd33 Update README.md 2024-09-26 00:42:32 +08:00
涵曦
e42537d591 Update README.md 2024-09-26 00:41:00 +08:00
Gao, Ruiyuan
410d4452d1 drop request.url.path (#204) 2024-09-25 23:56:33 +08:00
涵曦
092dd8a532 Fix code scanning alert no. 33: Use of a broken or weak cryptographic hashing algorithm on sensitive data (#202)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2024-09-25 22:27:08 +08:00
涵曦
2b6619b4da Fix code scanning alert no. 32: Use of a broken or weak cryptographic hashing algorithm on sensitive data (#203)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2024-09-25 22:26:48 +08:00
Gao, Ruiyuan
db8b90487f feat: 加密音乐和图片访问链接 (#200)
* use basic auth, cannot work

* Revert "use basic auth, cannot work"

This reverts commit 16a9683855.

* use access key/code control

* Auto-format code 🧹🌟🤖

---------

Co-authored-by: Formatter [BOT] <runner@fv-az1766-921.lyuwioyq51hutffh0ei52p4blg.dx.internal.cloudapp.net>
2024-09-25 18:41:28 +08:00
涵曦
dec21aa57c perf: 对歌曲信息中的图片缩小到300 #190 2024-09-25 12:51:53 +08:00
涵曦
baf9a83e50 feat: 歌曲信息中的图片改为url #190 2024-09-25 03:11:29 +08:00
Gao, Ruiyuan
d214cc8df3 format action (#199) 2024-09-24 14:18:53 +08:00
Gao, Ruiyuan
55b8b4e966 fix: 解决 L05C 无提示音问题 support MiIOService tts (#198)
* support MiIOService tts

* Auto-format code 🧹🌟🤖

---------

Co-authored-by: Formatter [BOT] <runner@fv-az1773-635.5c5iphqueiuuvdxclnr0txjlua.dx.internal.cloudapp.net>
2024-09-24 14:18:09 +08:00
涵曦
6e8830c4e6 feat: 新增更新提醒 2024-09-24 09:26:39 +08:00
Gao, Ruiyuan
609cb4f10f use async task for building tags (#195)
* use threading for building tags

* change tag cache to use asyncio

* sleep 0 is not effective, change to 0.001
2024-09-24 07:24:33 +08:00
Gao, Ruiyuan
917c6d21c8 hotfix: multi-stage search may result in duplicates (#194) 2024-09-23 15:42:29 +08:00
涵曦
2095ea0d45 feat: 定时任务新增刷新播放列表接口 2024-09-23 08:44:08 +08:00
涵曦
ec8099b7a0 feat: 后台设置名称优化 2024-09-23 07:54:50 +08:00
涵曦
b077dbedce Update README.md 2024-09-23 07:49:52 +08:00
涵曦
e6b030e7f1 refactor: 更新静态文件 2024-09-23 03:29:07 +08:00
涵曦
5145590b1e feat: 新增按钮刷新 tag 信息 2024-09-23 03:27:01 +08:00
涵曦
329c6b26bd fix: 解决歌曲信息乱码问题 2024-09-23 02:31:47 +08:00
涵曦
44860d495e feat: 新增 musicinfos 接口用于批量查询歌曲信息 2024-09-23 02:31:47 +08:00
Gao, Ruiyuan
7c9576874b feat: 增加 tags 缓存 (#193) 2024-09-23 02:31:28 +08:00
Gao, Ruiyuan
3110c2221b feat: 使用 opencc 将歌曲名转化为简体 (#192) 2024-09-22 23:18:36 +08:00
Gao, Ruiyuan
a428f377d4 feat: 搜索的歌曲存成列表供前端显示,实现额外索引 (#188)
* bug fix after cd54dae; implement #186

* bug fix

* convert musiclist to basename

* revert to basename in all_music

* move list2str to utils.py

* use _extra_index_search in search
2024-09-22 21:22:52 +08:00
涵曦
c1915fb6b1 fix: 修复搜索补全不生效的问题 2024-09-21 23:33:19 +08:00
涵曦
cd54dae176 fix: 修复默认主题没有选中上次播放列表的问题 2024-09-21 23:25:20 +08:00
Gao, Ruiyuan
c72a619df0 feat: 搜索多个结果,并更新“当前”播放列表 (#185)
* local search returns multiple match, and refill play list

* bug fix

* bug fix in play: sometimes we do not need to update play_list

* bug fix in find_best_match; do not update play_list when only 1 match
2024-09-21 21:28:00 +08:00
涵曦
425214d453 feat: musicinfo接口新增musictag参数,用于返回歌曲额外信息 2024-09-21 21:18:56 +08:00
Gao, Ruiyuan
10693e103e fix: ffmpeg only output audio (#184) 2024-09-21 13:19:03 +08:00
涵曦
8f045ceaf3 Update README.md 2024-09-21 07:39:09 +08:00
涵曦
ff883142f7 Update README.md 2024-09-21 07:32:08 +08:00
涵曦
91f1586ab0 Update README.md 2024-09-21 01:12:04 +08:00
涵曦
cef5278f16 feat: 新增口令【播放列表第几个+列表名】来播放列表里的第几个 #158 2024-09-21 01:10:50 +08:00
涵曦
c923ad00f8 Update README.md 2024-09-21 00:25:27 +08:00
涵曦
ec3dc578b8 feat: 新增定时任务功能 #182 2024-09-20 17:36:20 +08:00
Gao, Ruiyuan
c5e0d4f3ca feat: hostname can take protocol,域名支持 https 格式 (#181) 2024-09-20 14:52:02 +08:00
涵曦
53c6c20d5e bump: version 0.3.36 → 0.3.37 2024-09-20 06:41:45 +08:00
涵曦
1ffc8e7175 build: update static version 2024-09-20 06:41:44 +08:00
52fisher
1c8d5fe423 feat: Pure主题更新 设置中心新增主题音乐列表样式选择、夜间模式、其他多项优化 (#180)
* fixed 账号设置页面未拉取最新设置的问题
* fixed 播放页面 在小爱设备上播放 按钮状态不对的问题
* feat 导航面板在小尺寸设备上可自动折叠
* Update 设置中心新增主题音乐列表样式选择、夜间模式
* fixed 修复播放列表因数量过大而卡死的情况(手风琴风格)
* feat 播放列表新增手风琴风格
2024-09-20 06:15:29 +08:00
涵曦
6299ad3b55 Update README.md 2024-09-19 08:17:44 +08:00
涵曦
e4efa0d879 bump: version 0.3.35 → 0.3.36 2024-09-19 07:08:47 +08:00
涵曦
2ebc0f11d2 build: update static version 2024-09-19 07:08:46 +08:00
52fisher
180f28800e feat: Pure 主题更新 (#178)
* 使用hash路由以改进后端服务器无法返回正确* 路由的结果
* 更新关于界面
* 播放列表界面优化
* 增加欢迎页的功能说明
2024-09-19 06:48:19 +08:00
涵曦
7f5692d6cd feat: 支持配置获取对话记录间隔时间 #169 2024-09-19 00:45:08 +08:00
涵曦
8d7b5337eb feat: 允许在后台设置监听端口 2024-09-18 23:32:00 +08:00
hui
dcbf4330be fix: 修复开启继续播放时歌曲播放不完整问题 (#177) 2024-09-18 20:14:55 +08:00
涵曦
2e53f20d80 Update README.md 2024-09-18 12:01:02 +08:00
涵曦
2914ddcc36 bump: version 0.3.34 → 0.3.35 2024-09-18 07:17:12 +08:00
涵曦
442756e3cc build: update static version 2024-09-18 07:17:11 +08:00
涵曦
b55a2a67c9 feat: 允许跨域访问 #172 2024-09-18 06:57:49 +08:00
52fisher
09545c7015 fix: 修复 Pure 主题白屏无法打开的问题 (#176)
* 修复 白屏无法打开的问题
* 增加清空已缓存的设置和拉取最新设置功能
* 增加刷新页面按钮及说明
2024-09-18 06:48:38 +08:00
涵曦
1f82efa2a1 bump: version 0.3.33 → 0.3.34 2024-09-18 01:08:13 +08:00
涵曦
e509052242 build: update static version 2024-09-18 01:08:12 +08:00
涵曦
046cdade5a fix: 主页适配移动端 2024-09-18 00:32:53 +08:00
涵曦
7f8e639b86 Update README.md 2024-09-17 19:46:02 +08:00
涵曦
bfbd36f7f9 feat: 新增 pure 主题 vue + elementUI (#172)
* allow CROS

* feat: vue + elementUI

* fixed 设置后打开主页白屏的问题

* fixed 设置中获取device_list失败的问题

* Update Theme

* Update Theme

---------

Co-authored-by: 52fisher <32198215+52fisher@users.noreply.github.com>
Co-authored-by: fisher <i@qnmlgb.trade>
2024-09-17 19:42:58 +08:00
涵曦
c5d623547c Update README.md 2024-09-17 01:53:22 +08:00
涵曦
31c61675bf refactor: 优化代码:输入框处理抖动问题,网页播放修改实现方式 see #166 2024-09-17 01:17:38 +08:00
涵曦
9900bd9ee9 fix: 修复网页播放点击后没有关闭旧声音的问题 #166 2024-09-16 22:35:36 +08:00
涵曦
8459494c61 fix: 修复单曲循环的情况下歌曲不在当前播放列表时失效的情况 2024-09-16 21:43:49 +08:00
涵曦
9852feec81 bump: version 0.3.32 → 0.3.33 2024-09-15 23:55:45 +08:00
涵曦
ce79c0f0f7 build: update static version 2024-09-15 23:55:44 +08:00
涵曦
51037fc714 Update README.md 2024-09-15 23:47:15 +08:00
涵曦
f6b9178688 Update README.md 2024-09-15 23:40:12 +08:00
涵曦
7ccbd6ce79 refactor: 优化谷歌统计 2024-09-15 23:10:00 +08:00
涵曦
30926c6b79 feat: 调整页面布局 2024-09-15 22:53:59 +08:00
涵曦
270076b9a7 fix: #168 安全优化: 设置数据接口密码隐藏处理 2024-09-15 15:51:44 +08:00
hui
ba58d45d8b feat: 支持继续播放 (#171) 2024-09-15 14:44:47 +08:00
涵曦
0543c92f37 Update README.md 2024-09-15 10:03:29 +08:00
涵曦
f40a4c5c7b Update README.md 2024-09-15 09:13:07 +08:00
涵曦
cc05933992 Update README.md 2024-09-14 19:59:05 +08:00
涵曦
896eae92ff fix: 修复谷歌统计报错问题 2024-09-14 15:08:38 +08:00
涵曦
07676e8c5d Update README.md 2024-09-14 12:13:12 +08:00
涵曦
4ec70a210b bump: version 0.3.31 → 0.3.32 2024-09-14 08:37:30 +08:00
涵曦
0b395f26ed build: update static version 2024-09-14 08:37:30 +08:00
涵曦
965a8be5bb Update README.md 2024-09-13 20:42:30 +08:00
hui
36ddfc8885 fix: 优化audio_id查询方式 (#165) 2024-09-13 20:14:40 +08:00
涵曦
f82957c73f Update README.md 2024-09-13 18:49:17 +08:00
涵曦
f1625e7d92 fix: 播放链接接口支持复杂的链接 2024-09-13 08:55:33 +08:00
涵曦
48797ddf8f feat: 新增谷歌统计 2024-09-12 20:06:32 +08:00
hui
6f67f515b2 feat: 增加播放进度 (#160)
* fix: windows下保存配置失败

* feat: 增加播放进度
2024-09-12 17:46:21 +08:00
涵曦
9a0146b04e Update README.md 2024-09-12 08:45:20 +08:00
涵曦
7fdb28c352 Update README.md 2024-09-11 17:36:50 +08:00
涵曦
5619584481 bump: version 0.3.30 → 0.3.31 2024-09-10 16:42:15 +00:00
涵曦
3f0a1cb8f5 build: update static version 2024-09-10 16:42:14 +00:00
涵曦
2f6105843b fix: 修复插件示例报错 #105 2024-09-10 16:24:11 +00:00
涵曦
d71f99de53 feat: 新增播放上一首歌曲功能 #90 2024-09-10 16:13:44 +00:00
涵曦
d7385405d9 fix: 修复当前播放歌曲没保存的问题 #90 2024-09-10 14:38:14 +00:00
涵曦
781e5ebb2f feat: 新增所有歌曲列表 2024-09-10 06:42:16 +00:00
Yan Zenghui
980772bf9c feat: 触屏版显示歌曲名称 (#156)
* feat:触屏版显示歌曲名称

* fix:修改日志级别

* fix:修改歌曲名称获取方式
2024-09-09 17:32:02 +08:00
涵曦
632e411c6e Update README.md 2024-09-08 16:43:51 +08:00
涵曦
789d442029 Update README.md 2024-09-08 16:41:02 +08:00
涵曦
0452d49930 Update README.md 2024-09-08 16:39:28 +08:00
涵曦
19d781fa1f Update README.md 2024-09-08 14:30:40 +08:00
涵曦
ad82d13a7e Update README.md 2024-09-08 14:28:28 +08:00
涵曦
9c4d757dc0 bump: version 0.3.29 → 0.3.30 2024-09-07 14:34:42 +00:00
涵曦
e369d80875 build: update static version 2024-09-07 14:34:41 +00:00
涵曦
9b0c8510a3 feat: 修改设置按钮位置 2024-09-07 13:58:20 +00:00
涵曦
94fb158d7d Update README.md 2024-09-07 10:02:47 +08:00
涵曦
11df6e9f0c Update README.md 2024-09-07 09:51:21 +08:00
涵曦
4e8550a56c Update README.md 2024-09-07 09:39:56 +08:00
涵曦
7f349410a0 feat: 新增网页播放接口 #138 2024-09-07 00:16:55 +00:00
涵曦
6610c29fe4 Update README.md 2024-09-07 07:42:10 +08:00
涵曦
3da1b8eac1 Update README.md 2024-09-07 07:38:07 +08:00
涵曦
003068e62c Update README.md 2024-09-07 07:34:50 +08:00
涵曦
1d12f0d508 Update README.md 2024-09-07 07:29:56 +08:00
涵曦
1ee4667a79 Update README.md 2024-09-07 02:36:16 +08:00
涵曦
521605e9c8 Update README.md 2024-09-07 02:31:35 +08:00
涵曦
9add14408c bump: version 0.3.28 → 0.3.29 2024-09-06 18:07:48 +00:00
涵曦
e554ace7ae build: update static version 2024-09-06 18:07:47 +00:00
涵曦
cdd0cdd237 Update README.md 2024-09-07 01:40:18 +08:00
涵曦
c713d32230 Update README.md 2024-09-07 01:37:49 +08:00
涵曦
8ee4e88f82 Update README.md 2024-09-07 01:34:47 +08:00
涵曦
ca7679e9d3 Update README.md 2024-09-07 01:19:09 +08:00
涵曦
b8c18ef33b feat: 设置页面新增接口文档入口 2024-09-06 17:17:17 +00:00
涵曦
1ce324151e fix: 修复网页开启秘密验证无法播歌的问题 #149 2024-09-06 17:13:35 +00:00
涵曦
faa452253c Update README.md 2024-09-07 00:29:47 +08:00
涵曦
ae41ae57b3 Update README.md 2024-09-04 18:52:57 +08:00
涵曦
6d3fe9381d Update README.md 2024-09-04 18:43:48 +08:00
涵曦
f879c0aeb9 Update release.yml 2024-09-04 06:56:59 +08:00
涵曦
58ffb93d3f Update release.yml 2024-09-04 06:56:38 +08:00
涵曦
8e5df7094c bump: version 0.3.27 → 0.3.28 2024-09-03 16:35:08 +00:00
涵曦
64c2f54ff0 build: update static version 2024-09-03 16:35:07 +00:00
涵曦
d1b869ae43 feat: 新增歌曲收藏功能 #87 2024-09-03 15:55:19 +00:00
涵曦
d3895f2632 refactor: ffmpeg_location 从配置里读取 2024-09-03 14:33:20 +00:00
hui
5bf62c4b1a fix: docker下minetypes无法判断m4a 2024-09-03 18:33:57 +08:00
hui
406e09922c fix:m4a无法正确获取播放时长 2024-09-03 18:33:57 +08:00
hui
ae34572d13 fix:指定文件编码,修复windows下的文件读取错误 2024-09-03 18:33:57 +08:00
涵曦
1e3c69ea90 Update release.yml 2024-09-02 09:09:49 +08:00
涵曦
3c232505f8 bump: version 0.3.26 → 0.3.27 2024-09-02 00:37:48 +00:00
涵曦
44177db9b6 build: update static version 2024-09-02 00:37:47 +00:00
涵曦
e72ae973bc refactor: 处理 code review 问题' 2024-09-01 16:09:33 +00:00
Hi-Jiajun
4ab3c5cbee feat: Add feature as requested in issue #143 2024-09-01 16:09:20 +00:00
涵曦
4e532d298d fix: 默认下载目录修改 2024-08-18 02:39:22 +00:00
涵曦
3372440f4e bump: version 0.3.25 → 0.3.26 2024-08-17 15:00:11 +00:00
涵曦
1255239912 build: update static version 2024-08-17 15:00:10 +00:00
涵曦
e401a73595 feat: 删除网关模式 2024-08-17 11:22:23 +00:00
涵曦
cca6e47da5 bump: version 0.3.24 → 0.3.25 2024-08-16 11:18:21 +00:00
涵曦
415e75d4b4 build: update static version 2024-08-16 11:18:20 +00:00
涵曦
3c5573a2fc feat: 设置页面支持配置 use_music_api 选项 2024-08-16 11:18:11 +00:00
涵曦
7275b59d40 Update README.md 2024-08-08 09:35:17 +08:00
涵曦
a8d0631c33 bump: version 0.3.23 → 0.3.24 2024-08-01 23:47:58 +00:00
涵曦
3cfc96b779 build: update static version 2024-08-01 23:47:57 +00:00
涵曦
489f3f1d6f fix: #131 修复多设备切换时播放模式显示错误问题 2024-08-01 23:21:31 +00:00
涵曦
a5f2fc195e bump: version 0.3.22 → 0.3.23 2024-08-01 16:00:06 +00:00
涵曦
393dbabf4b build: update static version 2024-08-01 16:00:05 +00:00
涵曦
444e697f9d fix: 修复部分文件获取不到播放时长问题 2024-08-01 15:52:20 +00:00
涵曦
cf01039b53 fix: 处理安全问题 2024-08-01 00:46:18 +00:00
涵曦
a8fefc6f82 bump: version 0.3.21 → 0.3.22 2024-08-01 00:23:17 +00:00
涵曦
ae0b6066d9 build: update static version 2024-08-01 00:23:17 +00:00
涵曦
53f5e7db8c feat: 网关模式支持配置,默认关闭 2024-08-01 00:22:48 +00:00
涵曦
2a1fa9f8cf fix: 继续优化延迟问题 2024-08-01 00:05:19 +00:00
涵曦
6e2d674758 bump: version 0.3.20 → 0.3.21 2024-07-30 22:55:35 +00:00
涵曦
3bb6573ec0 build: update static version 2024-07-30 22:55:34 +00:00
涵曦
4ae3774a0e fix: 使用前置网关处理静态文件来加速,尝试解决延迟的问题 2024-07-30 11:49:29 +00:00
涵曦
b34215560c feat: 尝试加个网关在前面处理静态文件来加速文件获取 2024-07-30 11:36:51 +00:00
涵曦
4426017ba8 fix: 播放前先立即暂停之前的音乐 2024-07-30 11:36:35 +00:00
涵曦
f1635f8e32 build: 限定python版本必须是3.10和3.11 2024-07-30 06:41:52 +00:00
涵曦
4de032e193 bump: version 0.3.19 → 0.3.20 2024-07-30 06:09:27 +00:00
涵曦
d655157e1d build: update static version 2024-07-30 06:09:26 +00:00
涵曦
ff06958ab3 fix: 尝试修复延迟问题,修复播放停止不了的问题 2024-07-30 06:09:18 +00:00
涵曦
6e98b5ee2b bump: version 0.3.18 → 0.3.19 2024-07-30 01:59:32 +00:00
涵曦
da90fe2633 build: update static version 2024-07-30 01:59:31 +00:00
涵曦
eaedef452c fix: 调整配置,优化获取歌曲时长接口 2024-07-30 01:09:01 +00:00
涵曦
2d5f3799a3 bump: version 0.3.17 → 0.3.18 2024-07-29 10:16:10 +00:00
涵曦
b52bfe0848 build: update static version 2024-07-29 10:16:09 +00:00
涵曦
e2261b2d19 fix: #135 修复获取不到播放时长时只播放3秒的问题 2024-07-29 10:15:49 +00:00
涵曦
2443444165 Update release.yml 2024-07-29 05:51:04 +08:00
涵曦
7c912a51be bump: version 0.3.16 → 0.3.17 2024-07-28 21:42:49 +00:00
涵曦
bda55a1faa build: update static version 2024-07-28 21:42:48 +00:00
涵曦
5b5f957f8e fix: 优化日志输出,尝试排查延迟播放的问题 2024-07-28 21:42:23 +00:00
涵曦
12f54e3ad4 Update release.yml 2024-07-29 04:48:39 +08:00
quxiaozha
d6594e1270 Fix docker run command 2024-07-29 04:42:02 +08:00
涵曦
b678447417 bump: version 0.3.15 → 0.3.16 2024-07-28 13:58:52 +00:00
涵曦
02508f6997 build: update static version 2024-07-28 13:58:52 +00:00
涵曦
d50fff9e31 build: 修复版本打包问题 2024-07-28 13:58:44 +00:00
涵曦
2d7d7ddc95 bump: version 0.3.14 → 0.3.15 2024-07-28 13:07:36 +00:00
涵曦
9c9825d423 build: update static version 2024-07-28 13:07:35 +00:00
涵曦
f788c0f37b fix: 修复自定义口令重复的问题 2024-07-28 13:07:14 +00:00
涵曦
36d72d1eca fix: 修复日志输出问题 2024-07-28 12:59:32 +00:00
涵曦
6b17779c59 Update release.yml 2024-07-28 17:33:43 +08:00
涵曦
759130e38d fix: 修复退出异常问题 2024-07-28 08:48:39 +00:00
涵曦
49f727477e style: 优化日志输出,方便排查问题 2024-07-28 08:09:34 +00:00
涵曦
43886116c1 bump: version 0.3.13 → 0.3.14 2024-07-28 02:02:09 +00:00
涵曦
a38194027d build: update static version 2024-07-28 02:02:08 +00:00
涵曦
eab4f4bd46 feat: 优化播放延迟问题,并新增配置下一首播放的延迟秒数 2024-07-28 00:51:20 +00:00
涵曦
6b38676766 build: 打包优化 2024-07-26 14:21:43 +00:00
涵曦
3830f58c0b bump: version 0.3.12 → 0.3.13 2024-07-24 23:07:11 +00:00
涵曦
34cdea1731 build: update static version 2024-07-24 23:07:10 +00:00
涵曦
22f545b99c fix: 解决 docker 镜像问题 2024-07-24 21:58:28 +00:00
涵曦
37f73dc31f bump: version 0.3.11 → 0.3.12 2024-07-24 13:34:37 +00:00
涵曦
10e52f0b63 build: update static version 2024-07-24 13:34:36 +00:00
涵曦
b0ac1034d2 feat: 优化获取文件播放时长接口,尝试解决播放延迟和操作面板卡顿的问题 2024-07-24 01:47:46 +00:00
涵曦
48d663a764 bump: version 0.3.10 → 0.3.11 2024-07-22 15:30:55 +00:00
涵曦
08ab75b390 build: update static version 2024-07-22 15:30:54 +00:00
涵曦
71dfc6d468 fix: #130 单曲循环的模式下,播放列表的指令不生效 2024-07-22 15:30:30 +00:00
涵曦
7877775495 refactor: 优化代码 2024-07-19 14:25:56 +00:00
涵曦
7744a75773 build: 更新依赖库 2024-07-19 14:14:49 +00:00
bowji
a28a0267e4 feat: Add remove mp3 id3 tag function
Signed-off-by: bowji <bowji@bowji-m-dnpx.dclife.fun>

Signed-off-by: Bowen Ji <jibwf@hotmail.com>
2024-07-19 14:09:05 +00:00
涵曦
417a5c924a bump: version 0.3.9 → 0.3.10 2024-07-19 12:18:50 +00:00
涵曦
d4bc1c49ea build: update static version 2024-07-19 12:18:49 +00:00
涵曦
51ef9e08fe fix: 修复软连接目录不能播放的问题 2024-07-19 06:10:14 +00:00
涵曦
285203a342 style: 优化日志输出 2024-07-18 09:54:31 +00:00
涵曦
af6077693e fix: 修复自定义语音口令设置不生效的问题 2024-07-18 01:52:04 +00:00
涵曦
40ac67cce0 Update FUNDING.yml 2024-07-18 00:15:38 +08:00
涵曦
96d03c5c29 feat: 支持软连接的接口直接用os.walk即可 2024-07-17 14:58:03 +00:00
涵曦
82aa453e50 bump: version 0.3.8 → 0.3.9 2024-07-17 10:59:13 +00:00
涵曦
1718211619 build: update static version 2024-07-17 10:59:13 +00:00
涵曦
09310675fc feat: #119 音乐目录支持软连接 2024-07-17 09:57:24 +00:00
涵曦
ca711bbdb8 fix: 修复日志下载报错问题 2024-07-17 06:05:15 +00:00
涵曦
fb44f88df2 fix: 兼容旧的setting.json文件中conf_path为空的情况 2024-07-17 06:05:15 +00:00
涵曦
e5b32b2831 fix: 修复设置页面可能打不开的问题 2024-07-17 06:05:15 +00:00
涵曦
19ddbb7ca9 Update README.md 2024-07-17 00:32:42 +08:00
涵曦
3e82d7acdc build: 默认映射目录设置 2024-07-16 16:25:01 +00:00
涵曦
3921c70c86 bump: version 0.3.7 → 0.3.8 2024-07-16 13:58:04 +00:00
涵曦
aea9333e57 build: update static version 2024-07-16 13:58:03 +00:00
涵曦
7043ca31cf fix: 修复播放url接口问题 2024-07-16 13:57:37 +00:00
涵曦
963d86de7c bump: version 0.3.6 → 0.3.7 2024-07-16 09:38:51 +00:00
涵曦
d6b0e974b7 build: update static version 2024-07-16 09:38:50 +00:00
涵曦
8a8340a159 feat: 播放链接按钮对应给个默认的链接用于测试 2024-07-16 09:38:36 +00:00
涵曦
20d3c9fce9 feat: Uvicorn 的日志信息合并到 xiaomusic 日志里显示 2024-07-16 05:01:50 +00:00
涵曦
729549a7a9 bump: version 0.3.5 → 0.3.6 2024-07-15 17:22:30 +00:00
涵曦
d28614177c build: update static version 2024-07-15 17:22:30 +00:00
涵曦
db53517784 fix: #126 修复pip安装时主页打不开的问题 2024-07-15 17:22:24 +00:00
涵曦
186e9c1417 bump: version 0.3.4 → 0.3.5 2024-07-15 17:17:26 +00:00
涵曦
7498016d61 build: update static version 2024-07-15 17:17:26 +00:00
涵曦
7114ea2e6e fix: #116 播放失败自动切下首歌 2024-07-15 17:17:08 +00:00
涵曦
e8ca1f8678 bump: version 0.3.3 → 0.3.4 2024-07-15 16:46:06 +00:00
涵曦
2275bc2600 build: update static version 2024-07-15 16:46:06 +00:00
涵曦
45a94f4bfe fix: #125 修复本地英文歌曲匹大小写字母配不到的问题 2024-07-15 16:45:27 +00:00
涵曦
5cb2c84715 build: update code 2024-07-15 15:40:16 +00:00
涵曦
a3bf8d8aaa build: 静态文件版本自动更新脚本修改 2024-07-15 15:37:45 +00:00
涵曦
0a88b7be26 build: update static version 2024-07-15 15:24:26 +00:00
涵曦
05da3e8fc4 bump: version 0.3.2 → 0.3.3 2024-07-15 15:19:40 +00:00
涵曦
560ea1aeca fix: 尝试修复播放卡顿问题 see #124 2024-07-15 15:16:38 +00:00
涵曦
5ef1f2d940 ci: 发版本自动更新静态文件版本号 2024-07-15 14:53:25 +00:00
涵曦
e01fcdcecd bump: version 0.3.1 → 0.3.2 2024-07-15 14:51:09 +00:00
涵曦
8ff04dd0f6 fix: #122 pip安装方式下,static目录找不到报错 2024-07-15 14:44:57 +00:00
涵曦
4a7b5ac2b0 fix: 版本更新时更新页面缓存 2024-07-15 07:07:16 +00:00
涵曦
ac74ef3c15 Update README.md 2024-07-15 10:15:35 +08:00
涵曦
8913057c27 bump: version 0.3.0 → 0.3.1 2024-07-15 00:59:55 +00:00
涵曦
a75030a73c fix: 修复主页选择设备不生效的问题 see #120 2024-07-15 00:58:59 +00:00
涵曦
e2e74f9459 Update README.md 2024-07-14 23:14:16 +08:00
涵曦
09febb66dc bump: version 0.2.0 → 0.3.0 2024-07-14 14:00:05 +00:00
涵曦
ad0db8c9a9 Update README.md 2024-07-14 21:49:01 +08:00
涵曦
90b0fecd3b feat: 建议音乐目录和配置目录分开不同目录 2024-07-14 13:46:22 +00:00
涵曦
a8f88e8bfc fix: #114 修复部分 mp3 文件长度识别错误 2024-07-14 13:20:38 +00:00
涵曦
d8035f0713 fix: 删除 armv6 的支持 2024-07-14 12:48:05 +00:00
涵曦
76c1a8952f fix: 修复编译问题 2024-07-14 12:38:48 +00:00
涵曦
d8a66ca152 fix: 修复音乐路径设置后找不到音乐的问题 2024-07-14 09:52:14 +00:00
涵曦
dd77176035 fix: 修复启动报错的问题 2024-07-14 08:39:16 +00:00
涵曦
6d7d90d642 feat: 优化后台网络设置,同时支持ipv4和ipv6 2024-07-14 07:24:23 +00:00
涵曦
514bf9b8b2 Update README.md 2024-07-13 12:28:48 +08:00
涵曦
cd5869ff84 feat: 使用fastapi替换flask,解决多线程问题 2024-07-13 01:56:40 +00:00
涵曦
ee6f4ee4e4 feat: #106 网页上显示音箱当前状态(播放中or空闲中)以及当前的播放模式 2024-07-11 11:09:12 +00:00
涵曦
0189a00155 feat: 优化首页加载慢的问题 2024-07-11 10:34:23 +00:00
涵曦
e3d60d3f2e fix: 修复CI警告问题 2024-07-11 04:03:27 +00:00
涵曦
27fecd788b feat: 优化设置页面布局,方便配置必须项 2024-07-11 03:54:25 +00:00
涵曦
e9b1d94fb3 feat: 优化配置界面,支持配置分组 2024-07-11 03:40:10 +00:00
涵曦
043a9303a5 feat: 支持多设备分开播放 see #65 2024-07-11 03:05:56 +00:00
涵曦
aa698667c9 bump: version 0.1.101 → 0.2.0 2024-07-09 15:12:14 +00:00
涵曦
700e17854c Update README.md 2024-07-09 23:11:18 +08:00
涵曦
40258c9fa1 feat: 触屏版可以不用设置 XIAOMUSIC_USE_MUSIC_API 2024-07-09 15:07:46 +00:00
涵曦
eb59bf0db5 feat: 升级依赖库 2024-07-09 15:03:20 +00:00
涵曦
a9df78af97 Update README.md 2024-07-08 18:00:02 +08:00
涵曦
901506a32d feat: 唤醒口令配置支持配语音词,简化自定义口令配置 see #105 2024-07-08 01:06:01 +00:00
涵曦
0ddbe58fbd bump: version 0.1.100 → 0.1.101 2024-07-07 08:47:33 +00:00
涵曦
350d82184f fix: #81 修复播放列表时,当前歌曲不在列表没有更换歌曲的问题 2024-07-07 08:43:55 +00:00
涵曦
5b8054abd9 fix: #110 修复配置加载问题 2024-07-07 08:28:09 +00:00
涵曦
c5c691b653 bump: version 0.1.99 → 0.1.100 2024-07-07 06:59:44 +00:00
涵曦
7a44c8587c fix: 日志代码写错 2024-07-07 06:41:45 +00:00
涵曦
5092ffc91a bump: version 0.1.98 → 0.1.99 2024-07-07 06:39:08 +00:00
涵曦
2da12e12d5 fix: #81 修复播放列表没有继续播放上次播放的歌曲,并把随机播放,全部循环,单曲循环状态落地 2024-07-07 06:21:15 +00:00
涵曦
5aff72dbb6 bump: version 0.1.97 → 0.1.98 2024-07-07 05:48:40 +00:00
涵曦
043f452e71 Update README.md 2024-07-07 13:47:30 +08:00
涵曦
5cedf8a907 fix: 修复多设备获取不到对话记录的问题 see #65 2024-07-07 04:46:33 +00:00
涵曦
ae77c7232e Update README.md 2024-07-07 11:42:05 +08:00
涵曦
d559413d46 fix: #93 修复目录深度设置后导致目录下的歌曲无法加到播放列表里的问题 2024-07-07 02:03:17 +00:00
涵曦
8b185d8768 bump: version 0.1.96 → 0.1.97 2024-07-06 16:05:28 +00:00
涵曦
202105a11f fix: 修复网页控制台设置页面保存报错 2024-07-06 16:04:07 +00:00
涵曦
7da80594e3 bump: version 0.1.95 → 0.1.96 2024-07-06 15:46:36 +00:00
涵曦
a032a1d50a feat: 使用commitizen管理版本号 2024-07-06 15:46:15 +00:00
涵曦
be62d8abc8 feat: 页面版本号链接到CHANGELOG页面 2024-07-06 15:06:51 +00:00
涵曦
aaf9f4b6a7 feat: 规范版本管理 2024-07-06 14:49:41 +00:00
涵曦
bb5d82097e new version v0.1.95 2024-07-06 11:25:57 +00:00
涵曦
d6ba656641 new version v0.1.94 2024-07-06 11:25:33 +00:00
涵曦
742cae0543 style: 调整调试日志输出 2024-07-06 11:19:50 +00:00
涵曦
a4ab1af160 fix: 新增参数配置强制打断小爱说话 2024-07-06 11:16:42 +00:00
涵曦
86f158532a fix: 修复多设备获取对话记录的问题 2024-07-06 11:00:47 +00:00
Zhang Tinmix
9ea7935cfb fix: 修复windows下路径分隔符被视为转移符导致音箱无法播放音乐的问题
由于Windows路径中使用反斜杠(\)作为目录分隔符,而在URL编码中反斜杠被视为特殊字符所导致windows下拼接得到的url不符合预期
示例
filename:music\outside.mp3
预期:
url: /music/outside.mp3
实际:
url:/music%5Coutside.mp3

需要一个办法来处理windows下的\\路径分隔符,我给出了一种简单的解法,作者可以考虑看看怎么处理会完全一点
2024-07-06 11:00:40 +00:00
涵曦
20c7e14076 fix: 修复播放链接报错 2024-07-06 03:50:47 +00:00
涵曦
e10f5b89b6 fix: 修复配置页面默认配置被置空的问题 2024-07-06 00:32:10 +00:00
涵曦
cb0bae5ae7 feat: 优化多设备接口执行效果,尽量做到同时执行 2024-07-05 17:06:13 +00:00
涵曦
6a583119d0 new version v0.1.93 2024-07-05 15:28:51 +00:00
涵曦
9349070e8b Update README.md 2024-07-05 23:21:43 +08:00
涵曦
01d99dc699 feat: 访问账号密码默认为空 2024-07-05 15:15:31 +00:00
涵曦
f4d9a6c1fd feat: 支持下载的目录与本地音乐目录分开 see #98 2024-07-05 15:12:36 +00:00
涵曦
1919bc84d9 feat: 新增m4a文件格式支持 2024-07-05 14:55:47 +00:00
涵曦
4c1761468f fix: 修复设置页面没成功初始化设置问题 2024-07-05 14:52:14 +00:00
涵曦
c75230a67d Update ci.yml 2024-07-05 22:15:14 +08:00
涵曦
ee7ffa55cb fix: 修复镜像缺少文件问题 2024-07-05 13:45:44 +00:00
涵曦
45bbc8af42 Update ci.yml 2024-07-05 21:22:36 +08:00
涵曦
ab8bf8fa62 fix: 尝试解决插件路径问题 2024-07-05 13:12:03 +00:00
涵曦
493cad080e Update README.md 2024-07-05 18:43:06 +08:00
涵曦
eaa159c5cb feat: 设置页面支持配置多设备 2024-07-05 10:34:38 +00:00
涵曦
96e3b8c2ff feat: 默认用空的后台账号和密码 2024-07-05 09:10:48 +00:00
涵曦
794e8dcd06 feat: 支持多个设备同时播放 see #65 2024-07-05 09:10:01 +00:00
涵曦
f2675e4340 Update README.md 2024-07-05 13:08:18 +08:00
涵曦
e5059840fb fix: 设置页面日志路径写错了 2024-07-05 04:57:20 +00:00
涵曦
f3e57789fa feat: 新增自定义口令功能 #105 2024-07-05 04:26:21 +00:00
涵曦
c151b826f7 Update README.md 2024-07-04 23:31:43 +08:00
涵曦
1b3ed3b35a fix: 修复口令导致异常关闭的问题 2024-07-04 13:07:34 +00:00
涵曦
77a37a9438 new version v0.1.92 2024-07-04 11:12:00 +00:00
涵曦
f22de9f906 Update README.md 2024-07-04 19:11:03 +08:00
涵曦
090e8c3f4c feat: 启动参数新增 --port 配置监听端口 2024-07-04 10:42:31 +00:00
涵曦
aef51fb65d Update README.md 2024-07-04 18:32:15 +08:00
涵曦
a5a3a2dc62 Update README.md 2024-07-04 18:26:38 +08:00
涵曦
ce9adcee7f feat: 外网访问端口可独立配置 2024-07-04 10:15:48 +00:00
涵曦
485a42a9a0 feat: 优化设置页面,新增更多配置项 2024-07-04 09:36:06 +00:00
涵曦
3754970c84 feat: 首次保存设置后不需要重启容器 2024-07-04 04:42:19 +00:00
涵曦
924fbc208b fix: 日志文件配置的环境变量写错了 2024-07-04 04:03:23 +00:00
涵曦
c1a2ee4577 new version v0.1.91 2024-07-03 16:08:00 +00:00
涵曦
e84ee5de1e feat:触屏版显示界面的歌曲id支持配置 2024-07-03 13:48:52 +00:00
涵曦
0414830539 fix: 尝试解决触屏版不能播放的问题 2024-07-03 13:48:52 +00:00
涵曦
385f23842d Update README.md 2024-07-03 12:05:01 +08:00
涵曦
1deceaa5a5 Update README.md 2024-07-03 01:02:26 +08:00
涵曦
b8f1157e27 new version v0.1.90 2024-07-02 16:33:30 +00:00
涵曦
06558c24b7 feat: 优化触屏版播放页面显示歌曲 2024-07-02 16:33:06 +00:00
涵曦
6bd399b654 new version v0.1.89 2024-07-02 16:12:14 +00:00
涵曦
228d89f1f8 fix: 播放歌曲写成固定的了 2024-07-02 15:55:02 +00:00
涵曦
e97639302f feat: 尝试解决触屏版无法播放的问题 2024-07-02 13:40:56 +00:00
涵曦
7f4e51be08 fix: 播放歌曲时被其他指令打断后没有继续播放 2024-07-02 13:25:38 +00:00
涵曦
cdab5fc92d new version v0.1.88 2024-07-02 09:40:22 +00:00
涵曦
6efe498f2a feat: 日志里不要输出敏感信息 2024-07-02 05:27:17 +00:00
涵曦
0f3f2e47f5 feat: 优化下载 ffmpeg 脚本,尝试解决 armv7 环境问题 2024-07-02 05:24:29 +00:00
涵曦
3b720b7367 fix: 是否下载中判断错误导致播放无法自动重新开始播放 2024-07-02 03:29:01 +00:00
涵曦
9a3e513b6c fix: 升级yt-dlp到2024.07.01 2024-07-02 03:28:31 +00:00
涵曦
5a8e5dfa82 feat: 优化日志输出信息 2024-07-02 03:10:33 +00:00
涵曦
70d9ad93cb fix: 修复部分型号关机失败的问题 2024-07-01 16:57:04 +00:00
涵曦
87b3411f5e feat: 尝试解决触屏版无法播放的问题 2024-07-01 15:28:57 +00:00
涵曦
5c88c79ac6 new version v0.1.87 2024-07-01 14:43:59 +00:00
涵曦
5df91e7a59 fix: 修复XIAOMUSIC_USE_MUSIC_API=true时播放不了的问题 2024-07-01 14:42:20 +00:00
涵曦
71e9c15b5d new version v0.1.86 2024-07-01 13:34:38 +00:00
涵曦
c151144a5a Update README.md 2024-07-01 20:28:45 +08:00
涵曦
82a3373e72 Update README.md 2024-07-01 20:24:43 +08:00
涵曦
29ef5f238f feat: 优化 ffmpeg 安装脚本 2024-07-01 11:47:31 +00:00
涵曦
1809a2ab54 fix: 尝试修复 armv7 的 ffmpeg 问题 2024-07-01 11:14:55 +00:00
涵曦
3b1684f553 feat: 新增调试工具用来调试 player_play_music 接口 2024-07-01 11:08:54 +00:00
涵曦
d088374333 Update README.md 2024-07-01 09:15:07 +08:00
涵曦
80da6bd1e6 fix: 尝试修复关机失败的问题 2024-07-01 01:11:35 +00:00
涵曦
619bb9c853 fix: 修复口令不能播放的问题 2024-07-01 01:11:35 +00:00
涵曦
5add7b7a5c feat: 升级依赖库 MiService 2024-07-01 01:11:35 +00:00
涵曦
f61f14e16c Update README.md 2024-07-01 05:30:21 +08:00
leic4u
125421db22 为支持的设备增加产品名称和官方产品百科链接
为支持的设备增加产品名称和官方产品百科链接
2024-07-01 05:23:47 +08:00
涵曦
98b73f72df new version v0.1.85 2024-06-30 10:20:06 +00:00
涵曦
e68bc3b937 fix: 修复电台删除后没有从电台列表中删除的问题 2024-06-30 10:19:53 +00:00
涵曦
ab447a4633 feat: 版本号链接到github的release页面,方便查看版本更新日志 2024-06-30 10:16:58 +00:00
涵曦
23d321a722 new version v0.1.84 2024-06-30 09:44:25 +00:00
涵曦
20945954b1 feat: config.json 支持更多配置选项 2024-06-30 09:43:51 +00:00
涵曦
d6c2078917 docs: 文档更新 2024-06-30 09:43:28 +00:00
涵曦
a5b8dc639c feat: 新增 XIAOMUSIC_STOP_TTS_MSG 配置关机提示音 2024-06-30 07:32:33 +00:00
涵曦
84751e0d68 new version v0.1.83 2024-06-30 06:38:09 +00:00
涵曦
e759658481 bugfix: pip安装运行名字错误 2024-06-30 06:38:04 +00:00
涵曦
d83100588f new version v0.1.82 2024-06-30 06:34:03 +00:00
涵曦
83d0e02eb4 feat: 优化指令匹配规则 2024-06-30 06:33:53 +00:00
涵曦
20f1f33b6c update readme 2024-06-30 05:53:54 +00:00
涵曦
fbb5d26c28 new version v0.1.81 2024-06-30 05:42:26 +00:00
涵曦
2c21778675 update project 2024-06-30 05:40:08 +00:00
涵曦
959acd8fb7 优化关机提示 2024-06-30 05:27:44 +00:00
涵曦
148c5b7621 命令行新增LOGO 2024-06-30 05:19:38 +00:00
涵曦
d7a2afba48 Update README.md 2024-06-30 12:06:01 +08:00
涵曦
559ed23214 Update README.md 2024-06-30 12:05:10 +08:00
涵曦
1d1e63df8a Update README.md 2024-06-30 12:04:29 +08:00
涵曦
27e9d92a0a 提交config.json模板文件 2024-06-30 03:54:39 +00:00
涵曦
69573f3fa4 new version v0.1.80 2024-06-30 01:07:29 +00:00
涵曦
edafd79140 fix: #91 修复下载歌曲报错 2024-06-30 01:07:25 +00:00
涵曦
7c45d93fea new version v0.1.79 2024-06-29 15:37:38 +00:00
涵曦
f19a7e1080 优化关机的问题 2024-06-29 15:37:08 +00:00
涵曦
5e0ae07978 new version v0.1.78 2024-06-29 15:35:15 +00:00
涵曦
116a05ce4b 修复播放列表的问题 2024-06-29 15:35:15 +00:00
涵曦
d6fdee5905 Update README.md 2024-06-29 21:33:09 +08:00
涵曦
af25300917 new version v0.1.77 2024-06-29 13:24:37 +00:00
涵曦
f18b2f49bf fix: #52 支持配置模糊匹配本地歌曲 2024-06-29 13:21:37 +00:00
涵曦
db1e4e6fc4 整理代码 2024-06-29 12:42:15 +00:00
涵曦
dc49f63a37 新增直接播放链接的接口 2024-06-29 12:41:29 +00:00
涵曦
6837841872 Update README.md 2024-06-29 09:32:41 +08:00
涵曦
637347ae0c Update README.md 2024-06-29 09:18:26 +08:00
涵曦
ff968c4db4 Update README.md 2024-06-29 00:14:52 +08:00
涵曦
ca547e0d81 new version v0.1.76 2024-06-28 16:10:50 +00:00
涵曦
f0931c447b 下载日志链接改为按钮样式 2024-06-28 16:10:34 +00:00
涵曦
d098b5eb60 Update README.md 2024-06-28 23:45:18 +08:00
涵曦
09111e849d Update README.md 2024-06-28 23:43:48 +08:00
涵曦
49a76dee60 Update README.md 2024-06-28 23:20:45 +08:00
涵曦
637672473e Update README.md 2024-06-28 23:14:16 +08:00
涵曦
e4e1d13b69 Update README.md 2024-06-28 23:00:01 +08:00
涵曦
7736f8c5b4 Update README.md 2024-06-28 22:55:31 +08:00
涵曦
29db69b52f Update README.md 2024-06-28 22:52:04 +08:00
涵曦
37fe51771b Update README.md 2024-06-28 22:48:42 +08:00
涵曦
77ece713dc new version v0.1.75 2024-06-28 14:46:51 +00:00
涵曦
4436cc3a15 新增ogg格式文件 2024-06-28 14:46:46 +00:00
涵曦
c875350112 new version v0.1.74 2024-06-28 14:34:36 +00:00
涵曦
d6df2f6bfe 支持在设置页面下载日志 2024-06-28 14:34:12 +00:00
涵曦
03e3312218 新增日志文件 2024-06-28 14:03:09 +00:00
涵曦
5d7451c3f2 new version v0.1.73 2024-06-28 10:14:12 +00:00
涵曦
551dfa0c7f 播放列表口令加到默认唤醒词里 2024-06-28 10:14:08 +00:00
涵曦
3cc35e8f97 Update README.md 2024-06-28 15:32:19 +08:00
涵曦
01c68ba64e Create FUNDING.yml 2024-06-28 13:43:16 +08:00
涵曦
61d167d347 Update README.md 2024-06-28 12:18:18 +08:00
涵曦
9393e9f1ca new version v0.1.72 2024-06-28 02:43:35 +00:00
涵曦
c08ef030e9 优化配置页面,只允许选did see #83 2024-06-28 02:41:56 +00:00
涵曦
bad13f01f4 优化获取播放时长的问题 2024-06-28 02:33:12 +00:00
涵曦
049e1a2c38 测试触屏版本不能播放的问题 2024-06-28 01:47:27 +00:00
涵曦
a9fb829563 new version v0.1.71 2024-06-28 00:57:31 +00:00
涵曦
e66f731301 fix: #83 2024-06-28 00:56:39 +00:00
涵曦
538ac1d485 优化获取播放时长的逻辑 2024-06-27 23:20:43 +00:00
涵曦
88fbc503e7 加点调试日志 2024-06-27 16:36:53 +00:00
涵曦
1dc3ccbc16 更新依赖库 2024-06-27 15:34:03 +00:00
涵曦
df007d8e1b Update README.md 2024-06-27 23:15:53 +08:00
涵曦
0876551795 Update README.md 2024-06-27 23:09:40 +08:00
涵曦
51acb3ac8e Update README.md 2024-06-27 23:06:34 +08:00
涵曦
786af1c79e Update README.md 2024-06-27 22:46:10 +08:00
涵曦
7fba78e44b Update README.md 2024-06-27 22:03:06 +08:00
涵曦
9434cf3216 Update README.md 2024-06-27 14:17:00 +08:00
涵曦
f71ab25407 new version v0.1.70 2024-06-27 05:38:12 +00:00
涵曦
a634813c21 update readme 2024-06-27 05:33:58 +00:00
涵曦
17279aaae0 新增XIAOMUSIC_USE_MUSIC_API=true时使用player_play_music接口,兼容部分不能播放的设备型号 see #30 2024-06-27 05:33:53 +00:00
涵曦
4a234e8829 优化m3u文件转换工具 2024-06-27 05:16:33 +00:00
涵曦
805b3c41c8 优化下载文件的逻辑 2024-06-27 05:07:34 +00:00
涵曦
7a154fd847 修复停止播放导致退出问题 see #80 2024-06-26 23:33:15 +00:00
涵曦
3cdc836e9e 播放本地歌曲口令也作为唤醒词 see #80 2024-06-26 17:41:38 +00:00
涵曦
d232627796 优化播放歌曲口令和关机口令,新增播放本地歌曲口令,且都支持自定义配置这三个口令 see #80 2024-06-26 17:34:23 +00:00
涵曦
579820e606 new version v0.1.69 2024-06-26 16:24:12 +00:00
涵曦
a24a5166f9 new version v0.1.68 2024-06-26 16:24:09 +00:00
涵曦
ad67894244 Update README.md 2024-06-27 00:23:22 +08:00
涵曦
725d4c4ab3 新增XIAOMUSIC_DISABLE_DOWNLOAD=true时关闭音乐下载功能 see #82 2024-06-26 16:14:41 +00:00
涵曦
6a0310fe05 new version v0.1.67 2024-06-26 15:57:07 +00:00
涵曦
463fd9dd38 新增m3u文件转电台歌单工具 2024-06-26 15:56:19 +00:00
涵曦
34fe19abd1 new version v0.1.66 2024-06-26 13:41:24 +00:00
涵曦
3365f082f7 修复主页滚动问题 2024-06-26 13:41:05 +00:00
涵曦
13361c57b8 new version v0.1.65 2024-06-26 13:35:43 +00:00
涵曦
2a22b00d53 优化下载json文件逻辑 2024-06-26 10:14:11 +00:00
涵曦
925b52d979 优化下载文件的接口 2024-06-26 09:09:55 +00:00
涵曦
468efb63fb 优化下载文件的接口 2024-06-26 08:56:43 +00:00
涵曦
38aae7eca3 Update README.md 2024-06-26 12:53:05 +08:00
涵曦
44df1134a8 new version v0.1.64 2024-06-26 01:24:30 +00:00
涵曦
243c1673db new version v0.1.63 2024-06-26 01:24:27 +00:00
涵曦
82eab4810c 修复自动播放的问题 2024-06-26 01:24:27 +00:00
涵曦
e0a59b5729 修复自动播放的问题 2024-06-26 01:24:27 +00:00
涵曦
ad3bad85db 优化日志输出 2024-06-26 01:24:27 +00:00
涵曦
743d85de32 优化不定长参数arg1的用法 2024-06-26 01:24:27 +00:00
涵曦
8bd32f878f Update README.md 2024-06-26 02:03:38 +08:00
涵曦
eccb52c197 new version v0.1.62 2024-06-25 17:34:47 +00:00
涵曦
603d60d8b8 优化图标大小 2024-06-25 17:30:50 +00:00
涵曦
3ef04f4159 优化获取音乐时长接口 2024-06-25 17:27:32 +00:00
涵曦
7888ee7938 new version v0.1.61 2024-06-25 11:13:57 +00:00
涵曦
b2a3cda7b5 #78 支持配置自定义网络歌单 2024-06-25 11:13:46 +00:00
涵曦
80c6d29079 new version v0.1.60 2024-06-25 06:20:28 +00:00
涵曦
0b020deaef new version v0.1.59 2024-06-25 06:17:41 +00:00
涵曦
74c8bea756 cue不是音乐文件,排除下 2024-06-25 06:17:41 +00:00
涵曦
af10d6261f Update README.md 2024-06-25 10:50:32 +08:00
涵曦
a178278576 new version v0.1.58 2024-06-25 01:03:13 +00:00
涵曦
474fea8434 优化播放被打断的问题 2024-06-25 01:03:01 +00:00
涵曦
d271f7b0f7 代码优化 2024-06-24 16:03:10 +00:00
涵曦
7e2af515ed fix: 登陆失败不阻塞启动 2024-06-24 15:38:41 +00:00
涵曦
2d403ff18c new version v0.1.57 2024-06-24 14:28:08 +00:00
涵曦
f08244a990 update readme 2024-06-24 14:28:05 +00:00
涵曦
4869e5cf80 新增ape和cue格式文件 2024-06-24 14:27:31 +00:00
涵曦
b887504f9f Update README.md 2024-06-24 12:24:47 +08:00
涵曦
ad43a4f732 new version v0.1.56 2024-06-24 01:04:13 +00:00
涵曦
8699938b61 删除用不上的配置参数 2024-06-24 00:45:41 +00:00
涵曦
40bd099153 支持wav格式文件 2024-06-24 00:19:49 +00:00
涵曦
750923d5ca Update README.md 2024-06-24 00:38:33 +08:00
涵曦
6d99b30e2d Update README.md 2024-06-24 00:35:02 +08:00
涵曦
a155f16560 Update README.md 2024-06-24 00:30:42 +08:00
涵曦
d4aa045487 Update README.md 2024-06-23 20:05:20 +08:00
涵曦
8080dd9822 Update Dockerfile 2024-06-23 19:05:13 +08:00
涵曦
2eab8d8113 update dockerfile 2024-06-23 10:23:46 +00:00
涵曦
b51e56718a update dockerfile 2024-06-23 10:18:07 +00:00
涵曦
088d448e10 优化 Dockerfile 2024-06-23 17:40:28 +08:00
涵曦
4a89b4bce5 Update README.md 2024-06-23 16:27:22 +08:00
涵曦
b351b4bcd4 new version v0.1.55 2024-06-23 07:09:42 +00:00
涵曦
b2edaf48e4 fix: #47 支持配置基础的BaseAuth登录 2024-06-23 07:07:59 +00:00
涵曦
33e02cee82 new version v0.1.54 2024-06-23 04:17:03 +00:00
涵曦
91a8c9eb50 fix: #76 新增XIAOMUSIC_MUSIC_PATH_DEPTH配置生成播放列表的目录深度,默认10 2024-06-23 03:30:32 +00:00
涵曦
50da8a0554 fix: #74 配置目录可以和下载目录分开配置, 新增XIAOMUSIC_CONF_PATH用来设置配置目录,不配置时使用下载目录 2024-06-23 02:49:13 +00:00
涵曦
42b5978d89 new version v0.1.53 2024-06-23 01:52:42 +00:00
涵曦
bf29fc67b4 增加调试日志 2024-06-23 01:52:38 +00:00
涵曦
dbc68d6b56 new version v0.1.52 2024-06-21 14:16:52 +00:00
涵曦
5dabf66e7c 增加日志 2024-06-21 14:16:28 +00:00
涵曦
d6e4478eb6 new version v0.1.51 2024-06-20 23:10:21 +00:00
涵曦
23ef4719ba update log 2024-06-20 15:45:23 +00:00
涵曦
d799a85ab9 new version v0.1.49 2024-06-20 04:12:33 +00:00
涵曦
4ad6bcc636 播放列表排序显示,修复顺序播放问题 2024-06-20 04:12:23 +00:00
涵曦
d2473ec7e8 全部循环为顺序播放,和随机播放区分开 2024-06-18 07:06:16 +00:00
涵曦
28797edc7c new version v0.1.48 2024-06-16 06:14:37 +00:00
涵曦
be1a643071 忽略目录默认值修改 2024-06-16 06:14:33 +00:00
涵曦
ee6b9778ac new version v0.1.47 2024-06-16 05:40:38 +00:00
涵曦
881c34bcb5 新增忽略目录的环境变量 2024-06-16 05:40:30 +00:00
涵曦
c22fc99235 new version v0.1.46 2024-06-15 15:56:22 +00:00
涵曦
0874efe58b 播放歌曲指令默认播放最后一次播放的歌曲 2024-06-15 15:56:15 +00:00
涵曦
f01665c998 new version v0.1.45 2024-06-15 15:04:07 +00:00
涵曦
ac23080f6a 播放列表歌曲前打乱顺序 2024-06-15 15:03:56 +00:00
涵曦
15ee6c4dd1 new version v0.1.44 2024-06-14 15:47:02 +00:00
涵曦
aeaa8f8925 fmt 2024-06-14 15:46:47 +00:00
涵曦
59d7e056c4 new version v0.1.43 2024-06-14 15:14:56 +00:00
涵曦
e79afa46b3 新增删除歌曲按钮 2024-06-14 15:14:34 +00:00
涵曦
9714f3d064 new version v0.1.41 2024-06-14 14:11:00 +00:00
涵曦
2c35c6cfd6 add XIAOMUSIC_VERBOSE env 2024-06-14 14:10:56 +00:00
涵曦
88f0ce7e51 use ruff lint and fmt code 2024-06-14 01:58:10 +00:00
涵曦
e484164fad 修复刷新列表问题 2024-06-13 14:49:36 +00:00
涵曦
aa6bce75cd update readme 2024-06-12 17:26:00 +00:00
涵曦
512efb595a new version v0.1.40 2024-06-12 17:21:13 +00:00
涵曦
e5dea8e693 新增刷新列表指令 2024-06-12 17:21:09 +00:00
涵曦
a704f8003c new version v0.1.39 2024-06-12 17:13:00 +00:00
涵曦
349a25ad58 新增播放列表功能 #51 2024-06-12 17:12:07 +00:00
涵曦
746f46edb3 new version v0.1.38 2024-06-12 15:39:23 +00:00
涵曦
4a29c7a124 fix: #70 下一首歌曲不存在时从播放列表中删除并继续找下一首 2024-06-12 01:18:08 +00:00
涵曦
0e1e412ee9 new version v0.1.37 2024-06-04 12:17:50 +00:00
涵曦
2e84f7c830 Update ci.yml 2024-06-04 18:46:17 +08:00
涵曦
61a0d68b6a Update release.yml 2024-06-04 18:45:53 +08:00
涵曦
ae90029d8e Update README.md 2024-06-04 15:14:14 +08:00
涵曦
ccc83a518c new version v0.1.36 2024-05-30 14:42:32 +00:00
涵曦
7884a5769f 继续修复启动失败问题 2024-05-30 14:42:12 +00:00
涵曦
a663bb330e new version v0.1.35 2024-05-30 13:49:52 +00:00
涵曦
346f0af543 fix: #67 没配置did时也允许启动 http 服务 2024-05-27 14:47:40 +00:00
涵曦
49ec1bb7c0 Update README.md 2024-05-20 00:03:27 +08:00
涵曦
fc0cc75dea new version v0.1.34 2024-05-19 15:53:50 +00:00
涵曦
6fc2be5d31 消除flask启动告警 2024-05-19 15:53:31 +00:00
涵曦
db680bf1ba update readme 2024-05-19 15:22:52 +00:00
涵曦
1c2b97c0d2 new version v0.1.33 2024-05-19 15:19:19 +00:00
涵曦
59cfbb06a4 优化页面 2024-05-19 15:18:28 +00:00
涵曦
9291676543 fix: #50 新增配置页面 2024-05-19 15:11:43 +00:00
涵曦
0d2ba60728 删除不用的配置 2024-05-18 10:01:51 +00:00
firstuanl
cd1461df6d Update config.py
# 第一代小爱,型号MDZ-25-DA
2024-05-18 17:41:15 +08:00
涵曦
37abfd9ce2 fix: #62 2024-05-18 00:12:34 +00:00
涵曦
c6de3dfd00 new version v0.1.32 2024-05-17 12:27:55 +00:00
涵曦
ae297c780a update miservice 2024-05-17 12:27:47 +00:00
涵曦
e07a06c8e4 new version v0.1.31 2024-05-16 23:43:44 +00:00
涵曦
1c91f39417 日志里输出版本号 2024-05-16 23:43:39 +00:00
涵曦
13d26be0a2 new version v0.1.30 2024-05-16 23:19:11 +00:00
涵曦
c2740533a8 fix: 控制台显示版本号 #59 2024-05-16 23:19:05 +00:00
涵曦
abbc2f25bb 修复音量获取 2024-05-16 23:06:30 +00:00
涵曦
04b9738a77 new version v0.1.29 2024-05-16 22:44:38 +00:00
涵曦
f5932a301e 优化日志输出 2024-05-16 22:43:10 +00:00
涵曦
f11e194e6a fix: #57 #55 2024-05-16 22:39:05 +00:00
涵曦
259e47d4d3 new version v0.1.28 2024-05-16 00:22:16 +00:00
涵曦
96c3e00902 更新依赖,解决播放时量灯的问题 2024-05-16 00:22:08 +00:00
涵曦
066414a380 new version v0.1.27 2024-05-16 00:20:50 +00:00
涵曦
b6c7bbb2b7 Update README.md 2024-05-15 09:20:08 +08:00
涵曦
cdce558984 Update README.md 2024-05-15 09:18:50 +08:00
涵曦
a48bb89763 update dependencies 2024-05-12 14:04:08 +00:00
涵曦
e10a8137f4 Update README.md 2024-05-09 12:19:54 +08:00
涵曦
76aeb20268 new version v0.1.26 2024-05-08 14:15:15 +00:00
涵曦
de36ff7d24 默认允许播放歌曲和随机播放唤醒 2024-05-08 14:15:10 +00:00
涵曦
4b5d5b3a0a Update README.md 2024-05-07 07:22:37 +08:00
涵曦
59f150ebbd Update README.md 2024-05-07 07:21:28 +08:00
涵曦
4e2d39abac new version v0.1.25 2024-05-06 10:54:43 +00:00
涵曦
a35cde5d4a Merge pull request #43 from ominkk/main
可配置xiaomuisc激活指令,其他指令需要激活后才能使用
2024-05-02 00:21:05 +08:00
lv99
bbdf41f334 可配置xiaomuisc激活指令,其他指令需要激活后才能使用 2024-05-01 09:35:14 +08:00
涵曦
529aedede0 update readme 2024-04-30 14:04:58 +00:00
涵曦
55eaa6e751 new version v0.1.24 2024-04-30 13:52:20 +00:00
涵曦
ca59e594b4 歌名不换行 2024-04-30 13:52:16 +00:00
涵曦
836fde01b7 new version v0.1.23 2024-04-30 13:48:35 +00:00
涵曦
050ded6b2e 新增显示当前正在播放的音乐 2024-04-30 13:47:45 +00:00
涵曦
b9e1abff6b new version v0.1.22 2024-04-30 12:48:10 +00:00
涵曦
f962fcaa99 新增本地音乐模糊搜索 2024-04-30 12:47:57 +00:00
涵曦
eb35da595f Update README.md 2024-04-29 22:47:54 +08:00
183 changed files with 8911 additions and 1780 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: [hanxi]
custom: ['https://afdian.com/a/imhanxi']

45
.github/workflows/build-base-image.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Build Docker Base Image
on:
push:
paths:
- 'Dockerfile.builder'
- 'Dockerfile.runtime'
- 'install_dependencies.sh'
- '.github/workflows/build-base-image.yml'
workflow_dispatch:
jobs:
build-image:
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push runtime
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.runtime
platforms: linux/amd64, linux/arm64, linux/arm/v7
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:runtime
- name: Build and push builder
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.builder
platforms: linux/amd64, linux/arm64, linux/arm/v7
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:builder

View File

@@ -1,30 +1,88 @@
name: ci
on:
push:
branches: [ main ]
branches:
- "*"
workflow_dispatch:
env:
TEST_TAG: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:${{ github.ref_name }}
jobs:
build-image:
build-test-publish:
runs-on: ubuntu-latest
# run unless event type is pull_request
if: github.event_name != 'pull_request'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
- name: Build Docker image (linux/amd64)
uses: docker/build-push-action@v6
with:
platforms: linux/amd64
context: .
push: false
load: true
tags: ${{ env.TEST_TAG }}-linux-amd64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Build Docker image (linux/arm64)
uses: docker/build-push-action@v6
with:
platforms: linux/arm64
context: .
push: false
load: true
tags: ${{ env.TEST_TAG }}-linux-arm64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Build Docker image (linux/arm/v7)
uses: docker/build-push-action@v6
with:
platforms: linux/arm/v7
context: .
push: false
load: true
tags: ${{ env.TEST_TAG }}-linux-arm-v7
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# We test all the images on amd64 host here. This uses QEMU to emulate
# the other platforms.
- run: docker run --rm ${TEST_TAG}-linux-amd64 -h
- run: docker run --rm ${TEST_TAG}-linux-arm64 -h
- run: docker run --rm ${TEST_TAG}-linux-arm-v7 -h
# This will only push the previously built images.
- name: Publish to Docker Hub
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:${{ github.ref_name }}
tags: ${{ env.TEST_TAG }}
cache-from: type=local,src=/tmp/.buildx-cache-new
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: hanxi/xiaomusic
- name: Move cache to limit growth
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

47
.github/workflows/fmt.yaml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: fmt
on:
push:
branches:
- "*"
workflow_dispatch:
permissions:
contents: read
jobs:
format:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup PDM
uses: pdm-project/setup-pdm@v4
- name: install ruff
run: pip install ruff
- name: Format code
run: pdm fmt && pdm lint --fix
- name: Check for changes
id: check_changes
run: |
if [ -n "$(git diff)" ]; then
echo "changed=true" >> $GITHUB_OUTPUT
else
echo "changed=false" >> $GITHUB_OUTPUT
fi
continue-on-error: true
# Optionally, customize the user name and commit message, and can add an email as well such as Github Actions' email
- name: Set up Git and Commit Changes
run: |
if [ "${{ steps.check_changes.outputs.changed }}" == "true" ]; then
git config --local user.name "Formatter [BOT]"
git add .
git commit -m "Auto-format code 🧹🌟🤖"
git push
fi

View File

@@ -6,44 +6,38 @@ permissions:
on:
push:
tags:
- "*"
- "v*"
workflow_dispatch:
jobs:
release-pypi:
name: Build and Release PyPI
pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v4
- uses: actions/setup-node@v4
with:
python-version: "3.10"
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Build artifacts
run: |
pip install build
python -m build
- uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
registry-url: https://registry.npmjs.org/
node-version: lts/*
- run: npx changelogithub
continue-on-error: true
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- uses: pdm-project/setup-pdm@v3
- name: Publish package distributions to PyPI
run: pdm publish
build-image:
runs-on: ubuntu-latest
#needs: release-pypi
# run unless event type is pull_request
if: github.event_name != 'pull_request'
steps:
@@ -61,6 +55,6 @@ jobs:
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
platforms: linux/amd64, linux/arm64, linux/arm/v7
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:${{ github.ref_name }}, ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:latest, ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:stable

9
.gitignore vendored
View File

@@ -25,7 +25,7 @@ share/python-wheels/
.installed.cfg
*.egg
MANIFEST
*_bak/
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
@@ -163,3 +163,10 @@ cython_debug/
ffmpeg
music
test.sh
conf
setting.json
.DS_Store
cache
tmp/
xiaomusic.log.txt

8
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,8 @@
repos:
- hooks:
- id: commitizen
- id: commitizen-branch
stages:
- push
repo: https://github.com/commitizen-tools/commitizen
rev: v3.27.0

973
CHANGELOG.md Normal file
View File

@@ -0,0 +1,973 @@
## v0.3.55 (2024-12-04)
### Fix
- 修复播放接口报错问题
## v0.3.54 (2024-12-04)
### Feat
- 新增最近新增歌单 close #273
### Fix
- 安卓低版本webview对audio的src为空值的报错 (#289)
- 修复M01语音播放问题X08C X08E X8F 型号默认采用型号兼容模式 see #30
## v0.3.53 (2024-12-03)
### Fix
- 解决播放接口修改后播放失败的问题
## v0.3.52 (2024-12-03)
### Fix
- 修复播放接口参数错误的问题
## v0.3.51 (2024-12-03)
### Fix
- 修复空配置启动失败问题 close #284
## v0.3.50 (2024-12-03)
### Feat
- 修改日志文件的默认值
- 新增修改tag缓存信息的接口 close #266
- 新增专用的播放歌曲和播放歌单接口,解决默认口令提示词被修改了导致后台失效的问题
- 统计设备型号
- 页面与设置中的HOST不一致时弹窗提醒 (#281)
- 未发现小爱设备时给予提示 (#278)
- 优化设置页面提示
### Fix
- 更新 yt-dlp ,解决 B 站下载问题 close #279
- 网页播放audio获取到错误url无法播放时提醒用户 (#280)
- input标签自闭合
### Refactor
- 调整设置页面
## v0.3.49 (2024-11-28)
### Feat
- 临时文件目录支持配置 #99
- 新增单曲播放和顺序播放功能 close #277
- 设置播放类型支持配置语音提示词,定时任务支持设置播放类型
### Fix
- 修复中文数字转换函数对'十、十一'等数字的处理 (#275)
## v0.3.48 (2024-11-20)
### Feat
- 支持替换默认口令,而不是追加 close #259
- 新增自定义个歌单接口 #242
### Fix
- 锁定 PWA 应用旋转方向
## v0.3.47 (2024-11-14)
### Feat
- 支持 PWA 应用安装
- 新增模糊匹配测试用例
### Fix
- 修复 PWA 应用有密码时报错的问题
- 修复播放顺序没有按数字排序的问题 close #249
## v0.3.46 (2024-11-08)
### Feat
- 升级依赖库
### Fix
- 添加依赖库 requests
### Refactor
- 依赖库已经支持分段获取静态文件,重构代码
## v0.3.45 (2024-11-08)
### Feat
- 定时任务支持设置音量
- 播放歌单口令支持配置
### Fix
- 修复定时任务报错问题
## v0.3.44 (2024-11-01)
### Feat
- 日志时间里加上日期
### Fix
- 修复搜索失败的问题
## v0.3.43 (2024-10-30)
### Feat
- 播放列表可以删除当前歌曲(!危险操作,请在设置中心开启相关功能) (#250)
- 插件自定义口令支持获取语音输入内容 #105
### Fix
- 修复谷歌统计导致的卡顿问题
- 解决挂载网盘卡死的问题
## v0.3.42 (2024-10-24)
### Fix
- 尝试修复缺少 libtiff.so.6 文件的问题 #244
- 修复默认主题播放歌曲输入框空的情况
- 尝试修复停止后自动播放的问题
## v0.3.41 (2024-10-17)
### Feat
- 设置默认时区为东八区 closed #236
### Fix
- 修复获取标签信息报错问题
- remove_id3_tags return None if no id3 tag (#238)
- bug in del_music (#237)
## v0.3.40 (2024-10-16)
### Feat
- 默认主题的播放列表上显示歌曲数量
### Fix
- 修复播放卡顿问题(谷歌统计地址无法访问的情况)
## v0.3.39 (2024-10-15)
### Feat
- 固定的播放列表全部初始化
- 生产环境与开发环境接口分离、关于页面增加返回到主页的链接
update: 支持https页面未及时更新的问题
### Fix
- pure主题 当前设备与远程设备未正确区分的问题 (#234)
- static和doc添加basic auth (#231)
### Refactor
- 修改默认UI播放提示词 (#233)
## v0.3.38 (2024-10-14)
### Feat
- 播放状态接口返回当前播放列表 (#229)
- 新增口令收藏歌曲用来收藏当前播放的歌曲
- 默认UI搜索框动态显示 (#228)
- 文件转换逻辑延迟到读取文件的时候 see #218
- 重写播放组件,现在支持歌词显示了
- 使用 /cmdstatus 接口来判断异步任务是否完成
- 新增接口 /cmdstatus 用于查询异步任务是否执行完毕
- XMusicPlayer播放器主题优化 (#216)
- XMusicPlayer播放器主题 (#214)
- 新增 yt-dlp cookies 文件参数支持
- 新增批量下载歌曲工具
- 新增后台网站图标
- 加密音乐和图片访问链接 (#200)
- 歌曲信息中的图片改为url #190
- 新增更新提醒
- 定时任务新增刷新播放列表接口
- 后台设置名称优化
- 新增按钮刷新 tag 信息
- 新增 musicinfos 接口用于批量查询歌曲信息
- 增加 tags 缓存 (#193)
- 使用 opencc 将歌曲名转化为简体 (#192)
- 搜索的歌曲存成列表供前端显示,实现额外索引 (#188)
- 搜索多个结果,并更新“当前”播放列表 (#185)
- musicinfo接口新增musictag参数用于返回歌曲额外信息
- 新增口令【播放列表第几个+列表名】来播放列表里的第几个 #158
- 新增定时任务功能 #182
- hostname can take protocol域名支持 https 格式 (#181)
### Fix
- xplayer 收藏歌曲、取消收藏 (#230)
- 修复型号M01获取对话记录时间戳的问题
- 修复型号M01无法获取到对话记录的问题
- 使用小爱设备播放时组件异常的问题 (#217)
- 修复图片获取失败的问题
- 修复 yt-dlp-cookies 报错
- 修复自定义口令末尾多余逗号的情况
- 修复windows下路径问题
- 解决 L05C 无提示音问题 support MiIOService tts (#198)
- 解决歌曲信息乱码问题
- 修复搜索补全不生效的问题
- 修复默认主题没有选中上次播放列表的问题
- ffmpeg only output audio (#184)
### Refactor
- 新增清理缓存按钮
- 优化默认UI的搜索框#226 (#227)
- 修复告警
- 体验优化,音乐列表缓存 (#222)
- 修改为播放选中歌曲
- 更新静态文件
### Perf
- 对歌曲信息中的图片缩小到300 #190
## v0.3.37 (2024-09-20)
### Feat
- Pure主题更新 设置中心新增主题音乐列表样式选择、夜间模式、其他多项优化 (#180)
## v0.3.36 (2024-09-19)
### Feat
- Pure 主题更新 (#178)
- 支持配置获取对话记录间隔时间 #169
- 允许在后台设置监听端口
### Fix
- 修复开启继续播放时歌曲播放不完整问题 (#177)
## v0.3.35 (2024-09-18)
### Feat
- 允许跨域访问 #172
### Fix
- 修复 Pure 主题白屏无法打开的问题 (#176)
## v0.3.34 (2024-09-18)
### Feat
- 新增 pure 主题 vue + elementUI (#172)
### Fix
- 主页适配移动端
- 修复网页播放点击后没有关闭旧声音的问题 #166
- 修复单曲循环的情况下歌曲不在当前播放列表时失效的情况
### Refactor
- 优化代码:输入框处理抖动问题,网页播放修改实现方式 see #166
## v0.3.33 (2024-09-15)
### Feat
- 调整页面布局
- 支持继续播放 (#171)
### Fix
- #168 安全优化: 设置数据接口密码隐藏处理
- 修复谷歌统计报错问题
### Refactor
- 优化谷歌统计
## v0.3.32 (2024-09-14)
### Feat
- 新增谷歌统计
- 增加播放进度 (#160)
### Fix
- 优化audio_id查询方式 (#165)
- 播放链接接口支持复杂的链接
## v0.3.31 (2024-09-10)
### Feat
- 新增播放上一首歌曲功能 #90
- 新增所有歌曲列表
- 触屏版显示歌曲名称 (#156)
### Fix
- 修复插件示例报错 #105
- 修复当前播放歌曲没保存的问题 #90
## v0.3.30 (2024-09-07)
### Feat
- 修改设置按钮位置
- 新增网页播放接口 #138
## v0.3.29 (2024-09-06)
### Feat
- 设置页面新增接口文档入口
### Fix
- 修复网页开启秘密验证无法播歌的问题 #149
## v0.3.28 (2024-09-03)
### Feat
- 新增歌曲收藏功能 #87
### Fix
- docker下minetypes无法判断m4a
### Refactor
- ffmpeg_location 从配置里读取
## v0.3.27 (2024-09-02)
### Feat
- Add feature as requested in issue #143
### Fix
- 默认下载目录修改
### Refactor
- 处理 code review 问题'
## v0.3.26 (2024-08-17)
### Feat
- 删除网关模式
## v0.3.25 (2024-08-16)
### Feat
- 设置页面支持配置 use_music_api 选项
## v0.3.24 (2024-08-01)
### Fix
- #131 修复多设备切换时播放模式显示错误问题
## v0.3.23 (2024-08-01)
### Fix
- 修复部分文件获取不到播放时长问题
- 处理安全问题
## v0.3.22 (2024-08-01)
### Feat
- 网关模式支持配置,默认关闭
### Fix
- 继续优化延迟问题
## v0.3.21 (2024-07-30)
### Feat
- 尝试加个网关在前面处理静态文件来加速文件获取
### Fix
- 使用前置网关处理静态文件来加速,尝试解决延迟的问题
- 播放前先立即暂停之前的音乐
## v0.3.20 (2024-07-30)
### Fix
- 尝试修复延迟问题,修复播放停止不了的问题
## v0.3.19 (2024-07-30)
### Fix
- 调整配置,优化获取歌曲时长接口
## v0.3.18 (2024-07-29)
### Fix
- #135 修复获取不到播放时长时只播放3秒的问题
## v0.3.17 (2024-07-28)
### Fix
- 优化日志输出,尝试排查延迟播放的问题
## v0.3.16 (2024-07-28)
## v0.3.15 (2024-07-28)
### Fix
- 修复自定义口令重复的问题
- 修复日志输出问题
- 修复退出异常问题
## v0.3.14 (2024-07-28)
### Feat
- 优化播放延迟问题,并新增配置下一首播放的延迟秒数
## v0.3.13 (2024-07-24)
### Fix
- 解决 docker 镜像问题
## v0.3.12 (2024-07-24)
### Feat
- 优化获取文件播放时长接口,尝试解决播放延迟和操作面板卡顿的问题
## v0.3.11 (2024-07-22)
### Feat
- Add remove mp3 id3 tag function
### Fix
- #130 单曲循环的模式下,播放列表的指令不生效
### Refactor
- 优化代码
## v0.3.10 (2024-07-19)
### Feat
- 支持软连接的接口直接用os.walk即可
### Fix
- 修复软连接目录不能播放的问题
- 修复自定义语音口令设置不生效的问题
## v0.3.9 (2024-07-17)
### Feat
- #119 音乐目录支持软连接
### Fix
- 修复日志下载报错问题
- 兼容旧的setting.json文件中conf_path为空的情况
- 修复设置页面可能打不开的问题
## v0.3.8 (2024-07-16)
### Fix
- 修复播放url接口问题
## v0.3.7 (2024-07-16)
### Feat
- 播放链接按钮对应给个默认的链接用于测试
- Uvicorn 的日志信息合并到 xiaomusic 日志里显示
## v0.3.6 (2024-07-15)
### Fix
- #126 修复pip安装时主页打不开的问题
## v0.3.5 (2024-07-15)
### Fix
- #116 播放失败自动切下首歌
## v0.3.4 (2024-07-15)
### Fix
- #125 修复本地英文歌曲匹大小写字母配不到的问题
## v0.3.3 (2024-07-15)
### Fix
- 尝试修复播放卡顿问题 see #124
## v0.3.2 (2024-07-15)
### Fix
- #122 pip安装方式下static目录找不到报错
- 版本更新时更新页面缓存
## v0.3.1 (2024-07-15)
### Fix
- 修复主页选择设备不生效的问题 see #120
## v0.3.0 (2024-07-14)
### Feat
- 建议音乐目录和配置目录分开不同目录
- 优化后台网络设置同时支持ipv4和ipv6
- 使用fastapi替换flask,解决多线程问题
- #106 网页上显示音箱当前状态播放中or空闲中以及当前的播放模式
- 优化首页加载慢的问题
- 优化设置页面布局,方便配置必须项
- 优化配置界面,支持配置分组
- 支持多设备分开播放 see #65
### Fix
- #114 修复部分 mp3 文件长度识别错误
- 删除 armv6 的支持
- 修复编译问题
- 修复音乐路径设置后找不到音乐的问题
- 修复启动报错的问题
- 修复CI警告问题
## v0.2.0 (2024-07-09)
### Feat
- 触屏版可以不用设置 XIAOMUSIC_USE_MUSIC_API
- 升级依赖库
- 唤醒口令配置支持配语音词,简化自定义口令配置 see #105
## v0.1.101 (2024-07-07)
### Fix
- #81 修复播放列表时,当前歌曲不在列表没有更换歌曲的问题
- #110 修复配置加载问题
## v0.1.100 (2024-07-07)
### Fix
- 日志代码写错
## v0.1.99 (2024-07-07)
### Fix
- #81 修复播放列表没有继续播放上次播放的歌曲,并把随机播放,全部循环,单曲循环状态落地
## v0.1.98 (2024-07-07)
### Fix
- 修复多设备获取不到对话记录的问题 see #65
- #93 修复目录深度设置后导致目录下的歌曲无法加到播放列表里的问题
## v0.1.97 (2024-07-06)
### Fix
- 修复网页控制台设置页面保存报错
## v0.1.96 (2024-07-06)
### Feat
- 使用commitizen管理版本号
- 页面版本号链接到CHANGELOG页面
- 规范版本管理
## v0.1.95 (2024-07-06)
## v0.1.94 (2024-07-06)
### Feat
- 优化多设备接口执行效果,尽量做到同时执行
### Fix
- 新增参数配置强制打断小爱说话
- 修复多设备获取对话记录的问题
- 修复windows下路径分隔符被视为转移符导致音箱无法播放音乐的问题
- 修复播放链接报错
- 修复配置页面默认配置被置空的问题
## v0.1.93 (2024-07-05)
### Feat
- 访问账号密码默认为空
- 支持下载的目录与本地音乐目录分开 see #98
- 新增m4a文件格式支持
- 设置页面支持配置多设备
- 默认用空的后台账号和密码
- 支持多个设备同时播放 see #65
- 新增自定义口令功能 #105
### Fix
- 修复设置页面没成功初始化设置问题
- 修复镜像缺少文件问题
- 尝试解决插件路径问题
- 设置页面日志路径写错了
- 修复口令导致异常关闭的问题
## v0.1.92 (2024-07-04)
### Feat
- 启动参数新增 --port 配置监听端口
- 外网访问端口可独立配置
- 优化设置页面,新增更多配置项
- 首次保存设置后不需要重启容器
### Fix
- 日志文件配置的环境变量写错了
## v0.1.91 (2024-07-03)
### Fix
- 尝试解决触屏版不能播放的问题
## v0.1.90 (2024-07-02)
### Feat
- 优化触屏版播放页面显示歌曲
## v0.1.89 (2024-07-02)
### Feat
- 尝试解决触屏版无法播放的问题
### Fix
- 播放歌曲写成固定的了
- 播放歌曲时被其他指令打断后没有继续播放
## v0.1.88 (2024-07-02)
### Feat
- 日志里不要输出敏感信息
- 优化下载 ffmpeg 脚本,尝试解决 armv7 环境问题
- 优化日志输出信息
- 尝试解决触屏版无法播放的问题
### Fix
- 是否下载中判断错误导致播放无法自动重新开始播放
- 升级yt-dlp到2024.07.01
- 修复部分型号关机失败的问题
## v0.1.87 (2024-07-01)
### Fix
- 修复XIAOMUSIC_USE_MUSIC_API=true时播放不了的问题
## v0.1.86 (2024-07-01)
### Feat
- 优化 ffmpeg 安装脚本
- 新增调试工具用来调试 player_play_music 接口
- 升级依赖库 MiService
### Fix
- 尝试修复 armv7 的 ffmpeg 问题
- 尝试修复关机失败的问题
- 修复口令不能播放的问题
## v0.1.85 (2024-06-30)
### Feat
- 版本号链接到github的release页面方便查看版本更新日志
### Fix
- 修复电台删除后没有从电台列表中删除的问题
## v0.1.84 (2024-06-30)
### Feat
- config.json 支持更多配置选项
- 新增 XIAOMUSIC_STOP_TTS_MSG 配置关机提示音
## v0.1.83 (2024-06-30)
## v0.1.82 (2024-06-30)
### Feat
- 优化指令匹配规则
## v0.1.81 (2024-06-30)
## v0.1.80 (2024-06-30)
### Fix
- #91 修复下载歌曲报错
## v0.1.79 (2024-06-29)
## v0.1.77 (2024-06-29)
### Fix
- #52 支持配置模糊匹配本地歌曲
## v0.1.76 (2024-06-28)
## v0.1.75 (2024-06-28)
## v0.1.74 (2024-06-28)
## v0.1.73 (2024-06-28)
## v0.1.72 (2024-06-28)
## v0.1.71 (2024-06-28)
### Fix
- #83
## v0.1.70 (2024-06-27)
## v0.1.69 (2024-06-26)
## v0.1.67 (2024-06-26)
## v0.1.66 (2024-06-26)
## v0.1.65 (2024-06-26)
## v0.1.64 (2024-06-26)
## v0.1.62 (2024-06-25)
## v0.1.61 (2024-06-25)
## v0.1.60 (2024-06-25)
## v0.1.58 (2024-06-25)
### Fix
- 登陆失败不阻塞启动
## v0.1.57 (2024-06-24)
## v0.1.56 (2024-06-24)
## v0.1.55 (2024-06-23)
### Fix
- #47 支持配置基础的BaseAuth登录
## v0.1.54 (2024-06-23)
### Fix
- #76 新增XIAOMUSIC_MUSIC_PATH_DEPTH配置生成播放列表的目录深度默认10
- #74 配置目录可以和下载目录分开配置, 新增XIAOMUSIC_CONF_PATH用来设置配置目录不配置时使用下载目录
## v0.1.53 (2024-06-23)
## v0.1.52 (2024-06-21)
## v0.1.51 (2024-06-20)
## v0.1.49 (2024-06-20)
## v0.1.48 (2024-06-16)
## v0.1.47 (2024-06-16)
## v0.1.46 (2024-06-15)
## v0.1.45 (2024-06-15)
## v0.1.44 (2024-06-14)
## v0.1.43 (2024-06-14)
## v0.1.41 (2024-06-14)
## v0.1.40 (2024-06-12)
## v0.1.39 (2024-06-12)
## v0.1.38 (2024-06-12)
### Fix
- #70 下一首歌曲不存在时从播放列表中删除并继续找下一首
## v0.1.37 (2024-06-04)
## v0.1.36 (2024-05-30)
## v0.1.35 (2024-05-30)
### Fix
- #67 没配置did时也允许启动 http 服务
## v0.1.34 (2024-05-19)
## v0.1.33 (2024-05-19)
### Fix
- #50 新增配置页面
- #62
## v0.1.32 (2024-05-17)
## v0.1.31 (2024-05-16)
## v0.1.30 (2024-05-16)
### Fix
- 控制台显示版本号 #59
## v0.1.29 (2024-05-16)
### Fix
- #57 #55
## v0.1.28 (2024-05-16)
## v0.1.27 (2024-05-16)
## v0.1.26 (2024-05-08)
## v0.1.25 (2024-05-06)
## v0.1.24 (2024-04-30)
## v0.1.23 (2024-04-30)
## v0.1.22 (2024-04-30)
## v0.1.21 (2024-04-08)
## v0.1.20 (2024-04-08)
## v0.1.19 (2024-04-04)
## v0.1.18 (2024-02-24)
## v0.1.16 (2024-02-24)
## v0.1.15 (2024-02-03)
## v0.1.14 (2024-02-03)
## v0.1.13 (2024-02-02)
## v0.1.12 (2024-01-30)
### Fix
- set volume failed
## v0.1.11 (2024-01-29)
## v0.1.10 (2024-01-29)
## v0.1.9 (2024-01-28)
### Fix
- arg1 漏修改
## v0.1.8 (2024-01-28)
### Fix
- http server listen host
## v0.1.7 (2024-01-28)
## v0.1.6 (2024-01-28)
## v0.1.5 (2024-01-27)
## v0.1.4 (2024-01-27)
### Fix
- error when play next
## v0.1.3 (2023-10-15)
## v0.1.2 (2023-10-15)
## v0.1.1 (2023-10-14)

View File

@@ -1,21 +1,25 @@
FROM python:3.10 AS builder
FROM hanxi/xiaomusic:builder AS builder
ENV DEBIAN_FRONTEND=noninteractive
RUN pip install -U pdm
ENV PDM_CHECK_UPDATE=false
WORKDIR /app
COPY requirements.txt .
RUN python3 -m venv .venv && .venv/bin/pip install --no-cache-dir -r requirements.txt
COPY install_dependencies.sh .
RUN bash install_dependencies.sh
FROM python:3.10-slim
COPY pyproject.toml README.md .
COPY xiaomusic/ ./xiaomusic/
COPY plugins/ ./plugins/
COPY xiaomusic.py .
RUN pdm install --prod --no-editable
FROM hanxi/xiaomusic:runtime
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app/ffmpeg /app/ffmpeg
COPY xiaomusic/ ./xiaomusic/
COPY xiaomusic.py .
ENV XDG_CONFIG_HOME=/config
COPY --from=builder /app/xiaomusic/ ./xiaomusic/
COPY --from=builder /app/plugins/ ./plugins/
COPY --from=builder /app/xiaomusic.py .
ENV XIAOMUSIC_HOSTNAME=192.168.2.5
ENV XIAOMUSIC_PORT=8090
VOLUME /config
VOLUME /app/conf
VOLUME /app/music
EXPOSE 8090
ENV TZ=Asia/Shanghai
ENV PATH=/app/.venv/bin:$PATH
ENTRYPOINT [".venv/bin/python3","xiaomusic.py"]

11
Dockerfile.builder Normal file
View File

@@ -0,0 +1,11 @@
FROM python:3.10
ENV DEBIAN_FRONTEND=noninteractive
RUN pip install -U pdm
ENV PDM_CHECK_UPDATE=false
WORKDIR /app
COPY pyproject.toml README.md .
COPY xiaomusic/ ./xiaomusic/
COPY plugins/ ./plugins/
COPY xiaomusic.py .
RUN pdm install --prod --no-editable

14
Dockerfile.runtime Normal file
View File

@@ -0,0 +1,14 @@
FROM python:3.10-slim
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y \
wget \
xz-utils \
libtiff6 \
libopenjp2-7 \
libxcb1 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY install_dependencies.sh .
RUN bash install_dependencies.sh

416
README.md
View File

@@ -1,147 +1,357 @@
# xiaomusic
# XiaoMusic: 无限听歌,解放小爱音箱
[![GitHub License](https://img.shields.io/github/license/hanxi/xiaomusic)](https://github.com/hanxi/xiaomusic)
[![Docker Image Version](https://img.shields.io/docker/v/hanxi/xiaomusic?sort=semver&label=docker%20image)](https://hub.docker.com/r/hanxi/xiaomusic)
[![Docker Pulls](https://img.shields.io/docker/pulls/hanxi/xiaomusic)](https://hub.docker.com/r/hanxi/xiaomusic)
[![PyPI - Version](https://img.shields.io/pypi/v/xiaomusic)](https://pypi.org/project/xiaomusic/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/xiaomusic)](https://pypi.org/project/xiaomusic/)
[![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fhanxi%2Fxiaomusic%2Fmain%2Fpyproject.toml)](https://pypi.org/project/xiaomusic/)
[![GitHub Release](https://img.shields.io/github/v/release/hanxi/xiaomusic)](https://github.com/hanxi/xiaomusic/releases)
[![Visitors](https://api.visitorbadge.io/api/daily?path=hanxi%2Fxiaomusic&label=daily%20visitor&countColor=%232ccce4&style=flat)](https://visitorbadge.io/status?path=hanxi%2Fxiaomusic)
[![Visitors](https://api.visitorbadge.io/api/visitors?path=hanxi%2Fxiaomusic&label=total%20visitor&countColor=%232ccce4&style=flat)](https://visitorbadge.io/status?path=hanxi%2Fxiaomusic)
使用小爱/红米音箱播放音乐,音乐使用 yt-dlp 下载。
## 运行
使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。
<https://github.com/hanxi/xiaomusic>
> [!TIP]
> 初次安装遇到问题请查阅 [💬 FAQ问题集合](https://github.com/hanxi/xiaomusic/issues/99) ,一般遇到的问题都已经有解决办法。
## 👋 最简配置运行
已经支持在 web 页面配置其他参数docker 启动命令如下:
```bash
docker run -p 8090:8090 -v /xiaomusic/music:/app/music -v /xiaomusic/conf:/app/conf hanxi/xiaomusic
```
🔥 国内:
```bash
docker run -p 8090:8090 -v /xiaomusic/music:/app/music -v /xiaomusic/conf:/app/conf m.daocloud.io/docker.io/hanxi/xiaomusic
```
对应的 docker compose 配置如下:
```yaml
services:
xiaomusic:
image: hanxi/xiaomusic
container_name: xiaomusic
restart: unless-stopped
ports:
- 8090:8090
volumes:
- /xiaomusic/music:/app/music
- /xiaomusic/conf:/app/conf
```
🔥 国内:
```yaml
services:
xiaomusic:
image: m.daocloud.io/docker.io/hanxi/xiaomusic
container_name: xiaomusic
restart: unless-stopped
ports:
- 8090:8090
volumes:
- /xiaomusic/music:/app/music
- /xiaomusic/conf:/app/conf
```
其中 conf 目录为配置文件存放目录music 目录为音乐存放目录,建议分开配置为不同的目录。
> [!NOTE]
> 上面配置的 /xiaomusic/music 和 /xiaomusic/conf 是 docker 主机里的 /xiaomusic 目录下的,可以修改为其他目录。如果报错找不到 /xiaomusic/music 目录,可以先执行 `mkdir -p /xiaomusic/{music,conf}` 命令新建目录。
docker 和 docker compose 二选一即可,启动成功后,在 web 页面可以配置其他参数,带有 `*` 号的配置是必须要配置的,其他的用不上时不用修改。初次配置时需要在页面上输入小米账号和密码保存后才能获取到设备列表。
> [!TIP]
> 目前安装步骤已经是最简化了,如果还是嫌安装麻烦,可以微信或者 QQ 约我远程安装,我一般周末和晚上才有时间,收个辛苦费 :moneybag: 50 元一次,安装失败不收费。
### 🔥 修改默认8090端口映射
#### 方法1 不修改监听端口 8090
【监听端口】保持为默认的 8090 不变,把【外网访问端口】改为 5678 。
```yaml
services:
xiaomusic:
image: hanxi/xiaomusic
container_name: xiaomusic
restart: unless-stopped
ports:
- 5678:8090
volumes:
- /xiaomusic/music:/app/music
- /xiaomusic/conf:/app/conf
environment:
XIAOMUSIC_PUBLIC_PORT: 5678
```
XIAOMUSIC_PUBLIC_PORT 对应后台设置里的【外网访问端口】,修改后可以不用重启。
#### 方法2 修改监听端口 8090 为 5678
如果需要修改 8090 端口为其他端口,比如 5678需要这样配3个数字都需要是 5678 。见 <https://github.com/hanxi/xiaomusic/issues/19>
```yaml
services:
xiaomusic:
image: hanxi/xiaomusic
container_name: xiaomusic
restart: unless-stopped
ports:
- 5678:5678
volumes:
- /xiaomusic/music:/app/music
- /xiaomusic/conf:/app/conf
environment:
XIAOMUSIC_PORT: 5678
```
如果不是首次修改端口,还需要修改 /xiaomusic/conf/setting.json 文件里的端口(也可以在后台修改监听端口后重启)。
遇到问题可以去 web 设置页面底部点击【下载日志文件】按钮,然后搜索一下日志文件内容确保里面没有账号密码信息后(有就删除这些敏感信息),然后在提 issues 反馈问题时把下载的日志文件带上。
> [!IMPORTANT]
> XIAOMUSIC_PORT 也可以在后台配置,对应的是监听端口,修改后记得重启。
### 🤐 支持语音口令
- 【播放歌曲】,播放本地的歌曲
- 【播放歌曲+歌名】,比如:播放歌曲周杰伦晴天
- 【上一首】
- 【下一首】
- 【单曲循环】
- 【全部循环】
- 【随机播放】
- 【关机】,【停止播放】,两个效果是一样的。
- 【刷新列表】,当复制了歌曲进 music 目录后,可以用这个口令刷新歌单。
- 【播放列表+列表名】,比如:播放列表其他。
- 【加入收藏】,把当前播放的歌曲加入收藏歌单。
- 【取消收藏】,把当前播放的歌曲从收藏歌单里移除。
- 【播放列表收藏】,这个用于播放收藏歌单。
- 【播放本地歌曲+歌名】,这个口令和播放歌曲的区别是本地找不到也不会去下载。
- 【播放列表第几个+列表名】,具体见: <https://github.com/hanxi/xiaomusic/issues/158>
- 【播放歌曲+关键词】,会搜索关键词作为临时搜索列表播放,比如说【播放歌曲林俊杰】,会播放所有林俊杰的歌。
> [!TIP]
> 隐藏玩法: 对小爱同学说播放歌曲小猪佩奇的故事,会先下载小猪佩奇的故事,然后再播放小猪佩奇的故事。
## 🛠️ pip 方式安装运行
```shell
> pip install -U xiaomusic
> xiaomusic --help
__ __ _ __ __ _
\ \/ / (_) __ _ ___ | \/ | _ _ ___ (_) ___
\ / | | / _` | / _ \ | |\/| | | | | | / __| | | / __|
/ \ | | | (_| | | (_) | | | | | | |_| | \__ \ | | | (__
/_/\_\ |_| \__,_| \___/ |_| |_| \__,_| |___/ |_| \___|
XiaoMusic v0.3.37 by: github.com/hanxi
usage: xiaomusic [-h] [--port PORT] [--hardware HARDWARE] [--account ACCOUNT]
[--password PASSWORD] [--cookie COOKIE] [--verbose]
[--config CONFIG] [--ffmpeg_location FFMPEG_LOCATION]
options:
-h, --help show this help message and exit
--port PORT 监听端口
--hardware HARDWARE 小爱音箱型号
--account ACCOUNT xiaomi account
--password PASSWORD xiaomi password
--cookie COOKIE xiaomi cookie
--verbose show info
--config CONFIG config file path
--ffmpeg_location FFMPEG_LOCATION
ffmpeg bin path
> xiaomusic --config config.json
```
其中 `config.json` 文件可以参考 `config-example.json` 文件配置。见 <https://github.com/hanxi/xiaomusic/issues/94>
不修改默认端口 8090 的情况下,只需要执行 `xiaomusic` 即可启动。
## 🔩 开发环境运行
- 使用 install_dependencies.sh 下载依赖
- 使用 pdm 安装环境
- 参考 [xiaogpt](https://github.com/yihong0618/xiaogpt) 设置好环境变量
```shell
export MI_USER="xxxxx"
export MI_PASS="xxxx"
export MI_DID=00000
export XIAOMUSIC_SEARCH='bilisearch:'
```
然后启动即可。默认监听了端口 8090 , 使用其他端口自行修改。
- 默认监听了端口 8090 , 使用其他端口自行修改。
```shell
pdm run xiaomusic.py
````
### 支持口令
如果是开发前端界面,可以通过 <http://localhost:8090/docs>
查看有什么接口。目前的 web 控制台非常简陋,欢迎有兴趣的朋友帮忙实现一个漂亮的前端,需要什么接口可以随时提需求。
- **播放歌曲**
- **播放歌曲**+歌名 比如:播放歌曲周杰伦晴天
- 下一首
- 单曲循环
- 全部循环
### 🚦 代码提交规范
> 隐藏玩法: 对小爱同学说播放歌曲小猪佩奇的故事,会播放小猪佩奇的故事。
提交前请执行
## 已测试设备
```txt
"L07A": ("5-1", "5-5"), # Redmi小爱音箱Play(l7a)
````
## 支持音乐格式
- mp3
- flac
> 本地音乐会搜索 mp3 和 flac 格式的文件,下载的歌曲是 mp3 格式的。
## 在 Docker 里使用
```shell
docker run -e MI_USER=<your-xiaomi-account> -e MI_PASS=<your-xiaomi-password> -e MI_DID=<your-xiaomi-speaker-mid> -e MI_HARDWARE='L07A' -e XIAOMUSIC_PROXY=<proxy-for-yt-dlp> -e XIAOMUSIC_HOSTNAME=192.168.2.5 -e XIAOMUSIC_SEARCH='bilisearch:' -p 8090:8090 -v ./music:/app/music hanxi/xiaomusic
```
- XIAOMUSIC_SEARCH 可以配置为 'bilisearch:' 表示歌曲从哔哩哔哩下载;
- 配置为 'ytsearch:' 表示歌曲从 youtube 下载。
- XIAOMUSIC_PROXY 用于配置代理,默认为空;
- 当 XIAOMUSIC_SEARCH 配置为 'ytsearch:' 时在国内需要用到。
- MI_HARDWARE 是小米音箱的型号,默认为'L07A'
- 注意端口必须映射为与容器内一致, XIAOMUSIC_HOSTNAME 需要设置为宿主机的 IP 地址,否则小爱无法正常播放。
- 可以把 /app/music 目录映射到本地,用于保存下载的歌曲。
XIAOMUSIC_PROXY 参数格式参考 yt-dlp 文档说明:
```
Use the specified HTTP/HTTPS/SOCKS proxy. To
enable SOCKS proxy, specify a proper scheme,
e.g. socks5://user:pass@127.0.0.1:1080/.
Pass in an empty string (--proxy "") for
direct connection
pdm fmt
pdm lint --fix
```
<https://github.com/hanxi/xiaomusic/issues/2><https://github.com/hanxi/xiaomusic/issues/11>
用于检查代码和格式化代码。
### 本地编译Docker Image
### 本地编译 Docker Image
```shell
docker build -t xiaomusic .
```
### docker compose 示例
## 已测试支持的设备
使用哔哩哔哩下载歌曲:
| 型号 | 名称 |
| ---- | ---------------------------------------------------------------------------------------------- |
| L06A | [小爱音箱](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.l06a) |
| L07A | [Redmi小爱音箱 Play](https://home.mi.com/webapp/content/baike/product/index.html?model=xiaomi.wifispeaker.l7a) |
| S12/S12A/MDZ-25-DA | [小米AI音箱](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.s12) |
| LX5A | [小爱音箱 万能遥控版](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.lx5a) |
| LX05 | [小爱音箱Play2019款](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.lx05) |
| L15A | [小米AI音箱第二代](https://home.mi.com/webapp/content/baike/product/index.html?model=xiaomi.wifispeaker.l15a#/) |
| L16A | [Xiaomi Sound](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.l16a) |
| L17A | [Xiaomi Sound Pro](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.l17a) |
| LX06 | [小爱音箱Pro](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.lx06) |
| LX01 | [小爱音箱mini](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.lx01) |
| L05B | [小爱音箱Play](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.l05b) |
| L05C | [小米小爱音箱Play 增强版](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.l05c) |
| L09A | [小米音箱Art](https://home.mi.com/webapp/content/baike/product/index.html?model=xiaomi.wifispeaker.l09a) |
| LX04 X10A X08A | 已经支持的触屏版 |
| X08C X08E X8F | 需要设置【型号兼容模式】选项为 true |
| M01/XMYX01JY | 小米小爱音箱HD 需要设置【特殊型号获取对话记录】选项为 true 才能语音播放|
```yaml
version: '3'
型号与产品名称对照可以在这里查询 <https://home.miot-spec.com/s/xiaomi.wifispeaker>
services:
xiaomusic:
image: hanxi/xiaomusic
container_name: xiaomusic
restart: unless-stopped
ports:
- 8090:8090
volumes:
- ./music:/app/music
environment:
MI_USER: '小米账号'
MI_PASS: '小米密码'
MI_DID: 00000
MI_HARDWARE: 'L07A'
XIAOMUSIC_SEARCH: 'bilisearch:'
XIAOMUSIC_HOSTNAME: '192.168.2.5'
```
> [!NOTE]
> 如果你的设备支持播放,请反馈给我添加到支持列表里,谢谢。
> 目前应该所有设备类型都已经支持播放,有问题随时反馈。
> 其他触屏版不能播放可以设置【型号兼容模式】选项为 true 试试。见 <https://github.com/hanxi/xiaomusic/issues/30>
## 🎵 支持音乐格式
- mp3
- flac
- wav
- ape
- ogg
- m4a
> [!NOTE]
> 本地音乐会搜索目录下上面格式的文件,下载的歌曲是 mp3 格式的。
> 已知 L05B L05C LX06 L16A 不支持 flac 格式。
> 如果格式不能播放可以打开【转换为MP3】和【型号兼容模式】选项。具体见 <https://github.com/hanxi/xiaomusic/issues/153#issuecomment-2328168689>
使用 youtobe 下载歌曲:
```yaml
version: '3'
services:
xiaomusic:
image: hanxi/xiaomusic
container_name: xiaomusic
restart: unless-stopped
ports:
- 8090:8090
volumes:
- ./music:/app/music
environment:
MI_USER: '小米账号'
MI_PASS: '小米密码'
MI_DID: 00000
MI_HARDWARE: 'L07A'
XIAOMUSIC_SEARCH: 'ytsearch:'
XIAOMUSIC_PROXY: 'http://192.168.2.5:8080'
XIAOMUSIC_HOSTNAME: '192.168.2.5'
```
## 简易的控制面板
## 💡 简易的控制面板
浏览器进入 <http://192.168.2.5:8090>
- ip 是 XIAOMUSIC_HOSTNAME 设置的
- 8090 是默认端口
- 支持功能
- 显示正在播放的歌曲
- 模糊搜索本地歌曲
- 播放列表
- 删除歌曲
- 设置页面
- 配置网络歌单
- 日志文件下载
采用新的设置页面之后,没有必须在启动前配置的环境变量了,除非是改默认的 8090 端口才需要配置环境变量。
## 感谢
## 🌏 网络歌单功能
可以配置一个 json 格式的歌单,支持电台和歌曲,也可以直接用别人分享的链接,同时配备了 m3u 文件格式转换工具,可以很方便的把 m3u 电台文件转换成网络歌单格式的 json 文件,具体用法见 <https://github.com/hanxi/xiaomusic/issues/78>
> [!NOTE]
> 欢迎有想法的朋友们制作更多的歌单转换工具。
## 🍺 更多其他可选配置
- XIAOMUSIC_ACTIVE_CMD 环境变量,对应后台的 【允许唤醒的命令】,用于唤醒口令,配置成'play,random_play'在非播放状态下只有这两个指令播放歌曲和随机播放可以触发触发后xiaomusic进入playing状态其他指令则可以正常触发。具体见 <https://github.com/hanxi/xiaomusic/pull/43>
- XIAOMUSIC_EXCLUDE_DIRS 配置歌曲目录里需要忽略的目录,对应后台的 【忽略目录】
- XIAOMUSIC_MUSIC_PATH_DEPTH 配置歌曲目录搜索深度,对应后台的 【目录深度】,具体见 <https://github.com/hanxi/xiaomusic/issues/76>
- XIAOMUSIC_DISABLE_HTTPAUTH 配置成 false 表示开启密码访问web控制台对应后台的 【关闭密码验证】,具体见 <https://github.com/hanxi/xiaomusic/issues/47>
- XIAOMUSIC_HTTPAUTH_USERNAME 配置 web 控制台用户,对应后台的 【控制台账户】
- XIAOMUSIC_HTTPAUTH_PASSWORD 配置 web 控制台密码,对应后台的 【控制台密码】
- XIAOMUSIC_CONF_PATH 用来存放配置文件的目录,对应后台的 【配置文件目录】,记得把目录映射到主机,默认为 `/app/config` ,具体见 <https://github.com/hanxi/xiaomusic/issues/74>
- XIAOMUSIC_CACHE_DIR 用来音乐 tag 缓存,默认为 `/app/cache`,对应后台的 【缓存文件目录】。
- XIAOMUSIC_DISABLE_DOWNLOAD 设为 true 时关闭下载功能,对应后台的 【关闭下载功能】,见 <https://github.com/hanxi/xiaomusic/issues/82>
- XIAOMUSIC_USE_MUSIC_API 设为 true 时使用 player_play_music 接口播放音乐,对应后台的 【型号兼容模式】,用于兼容不能播放的型号,如果发现需要设置这个选项的时候请告知我加一下设备型号,方便以后不用设置。 见 <https://github.com/hanxi/xiaomusic/issues/30>
- XIAOMUSIC_KEYWORDS_PLAY 用来播放歌曲的口令前缀,对应后台的 【播放歌曲口令】,默认是 "播放歌曲,放歌曲" ,可以用英文逗号分割配置多个
- XIAOMUSIC_KEYWORDS_STOP 用来关机的口令,对应后台的 【停止口令】,默认是 "关机,暂停,停止" ,可以用英文逗号分割配置多个。
- XIAOMUSIC_KEYWORDS_PLAYLOCAL 用来播放本地歌曲的口令前缀,对应后台的 【播放本地歌曲口令】,本地找不到时不会下载歌曲,默认是 "播放本地歌曲,本地播放歌曲" ,可以用英文逗号分割配置多个。
- XIAOMUSIC_ENABLE_FUZZY_MATCH 设为 true 时开启模糊匹配(默认),设为 false 时关闭模糊匹配,对应后台的 【开启模糊搜索】,支持模糊匹配歌名和歌单名。 具体见 <https://github.com/hanxi/xiaomusic/issues/52>
- XIAOMUSIC_FUZZY_MATCH_CUTOFF 设置模糊搜索匹配的最低相似度阈值默认0.6可以配0到1直接的小数越小越模糊越大越精准对应后台的 【模糊匹配阈值】。具体见 <https://github.com/hanxi/xiaomusic/issues/52>
- XIAOMUSIC_PUBLIC_PORT 用于设置外网端口,对应后台的 【外网访问端口】当使用反向代理时可以设置为外网端口XIAOMUSIC_HOSTNAME 设为外网IP或者域名即可。
- XIAOMUSIC_DOWNLOAD_PATH 变量可以配置下载目录,默认为空,表示使用 music 目录为下载目录,对应后台的 【音乐下载目录】。设置这个目录必须是 music 的子目录,否则刷新列表后会找不到歌曲。具体见 <https://github.com/hanxi/xiaomusic/issues/98>
- XIAOMUSIC_PROXY 用于配置国内使用 youtube 源下载歌曲时使用的代理,参数格式参考 yt-dlp 文档说明。 见 <https://github.com/hanxi/xiaomusic/issues/2><https://github.com/hanxi/xiaomusic/issues/11>
- MIIO_TTS_CMD 用于部分机型(如:`L05C`)使用 MiIO 支持 tts 能力,默认为空,命令选择见 [MiService-fork 文档](https://github.com/yihong0618/MiService)
### ⚠️ 安全提醒
> [!IMPORTANT]
> 如果配置了公网访问 xiaomusic ,请一定要开启密码登陆,并设置复杂的密码。且不要在公共场所的 WiFi 环境下使用,否则可能造成小米账号密码泄露。
## 🤔 高级篇
- 自定义口令功能 <https://github.com/hanxi/xiaomusic/issues/105>
- [ ] 缺少一篇教程 [如何写自定义插件](https://github.com/hanxi/xiaomusic/issues/105)
## 📢 讨论区
- [点击链接加入QQ频道【xiaomusic】](https://pd.qq.com/s/e2jybz0ss)
- [点击链接加入群聊【xiaomusic】 604526973](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=13St5PLVcTxYlWTAs_iAawazjtdD1l-a&authKey=dJWEpaT2fDBDpdUUOWj%2FLt6NS1ePBfShDfz7a6seNURi05VvVnAGQzXF%2FM%2F5HgIm&noverify=0&group_code=604526973)
- <https://github.com/hanxi/xiaomusic/issues>
- [微信群二维码](https://github.com/hanxi/xiaomusic/issues/86)
## ❤️ 感谢
- [xiaomi](https://www.mi.com/)
- [PDM](https://pdm.fming.dev/latest/)
- [xiaogpt](https://github.com/yihong0618/xiaogpt)
- [MiService](https://github.com/yihong0618/MiService)
- [实现原理](https://github.com/yihong0618/gitblog/issues/258)
- [yt-dlp](https://github.com/yt-dlp/yt-dlp)
- [NAS部署教程](https://post.m.smzdm.com/p/avpe7n99/)
- [awesome-xiaoai](https://github.com/zzz6519003/awesome-xiaoai)
- [微信小程序: XIAO晓音](https://github.com/F-loat/xiaoplayer)
- [pure 主题 xiaomusicUI](https://github.com/52fisher/xiaomusicUI)
- [移动端的播放器主题](https://github.com/52fisher/XMusicPlayer)
- [一个第三方的主题](https://github.com/DarrenWen/xiaomusicui)
- 所有帮忙调试和测试的朋友
- 所有反馈问题和建议的朋友
### 👉 其他教程
更多功能见 [📝 文档汇总](https://github.com/hanxi/xiaomusic/issues/211)
## 🚨 免责声明
本项目仅供学习和研究目的,不得用于任何商业活动。用户在使用本项目时应遵守所在地区的法律法规,对于违法使用所导致的后果,本项目及作者不承担任何责任。
本项目可能存在未知的缺陷和风险(包括但不限于设备损坏和账号封禁等),使用者应自行承担使用本项目所产生的所有风险及责任。
作者不保证本项目的准确性、完整性、及时性、可靠性,也不承担任何因使用本项目而产生的任何损失或损害责任。
使用本项目即表示您已阅读并同意本免责声明的全部内容。
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=hanxi/xiaomusic&type=Date)](https://star-history.com/#hanxi/xiaomusic&Date)
## 赞赏
- :moneybag: 爱发电 <https://afdian.com/a/imhanxi>
- 点个 Star :star:
- 谢谢 :heart:
- ![喝杯奶茶](https://i.v2ex.co/7Q03axO5l.png)
## License
[MIT](https://github.com/hanxi/xiaomusic/blob/main/LICENSE) License © 2024 涵曦

84
config-example.json Normal file
View File

@@ -0,0 +1,84 @@
{
"account": "",
"password": "",
"mi_did": "",
"miio_tts_command": null,
"cookie": "",
"verbose": false,
"music_path": "music",
"download_path": "",
"conf_path": null,
"tag_cache_dir": null,
"hostname": "192.168.2.5",
"port": 8090,
"public_port": 0,
"proxy": null,
"search_prefix": "bilisearch:",
"ffmpeg_location": "./ffmpeg/bin",
"active_cmd": "play,set_random_play,playlocal,play_music_list,stop",
"exclude_dirs": "@eaDir",
"music_path_depth": 10,
"disable_httpauth": true,
"httpauth_username": "",
"httpauth_password": "",
"music_list_url": "",
"music_list_json": "",
"disable_download": false,
"key_word_dict": {
"播放歌曲": "play",
"播放本地歌曲": "playlocal",
"关机": "stop",
"下一首": "play_next",
"单曲循环": "set_play_type_one",
"全部循环": "set_play_type_all",
"随机播放": "set_random_play",
"分钟后关机": "stop_after_minute",
"播放列表": "play_music_list",
"刷新列表": "gen_music_list",
"本地播放歌曲": "playlocal",
"放歌曲": "play",
"暂停": "stop",
"停止": "stop",
"停止播放": "stop",
"测试自定义口令": "exec#code1(\"hello\")",
"测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")"
},
"key_match_order": [
"分钟后关机",
"播放歌曲",
"下一首",
"单曲循环",
"全部循环",
"随机播放",
"关机",
"刷新列表",
"播放列表",
"播放本地歌曲",
"本地播放歌曲",
"放歌曲",
"暂停",
"停止",
"停止播放",
"测试自定义口令",
"测试链接"
],
"use_music_api": false,
"use_music_audio_id": "1582971365183456177",
"use_music_id": "355454500",
"log_file": "/tmp/xiaomusic.txt",
"fuzzy_match_cutoff": 0.6,
"enable_fuzzy_match": true,
"stop_tts_msg": "收到,再见",
"enable_config_example": true,
"keywords_playlocal": "播放本地歌曲,本地播放歌曲",
"keywords_play": "播放歌曲,放歌曲",
"keywords_stop": "关机,暂停,停止,停止播放",
"user_key_word_dict": {
"测试自定义口令": "exec#code1(\"hello\")",
"测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")"
},
"enable_force_stop": false,
"devices": {},
"group_list": "",
"convert_to_mp3": false
}

View File

@@ -4,14 +4,49 @@
# https://github.com/yt-dlp/yt-dlp#dependencies
# 判断系统架构
arch=$(arch)
arch=$(uname -m)
pkg=ffmpeg-master-latest-linuxarm64-gpl
if [[ "${arch}" == "x86_64" ]]; then
pkg=ffmpeg-master-latest-linux64-gpl
fi
# 输出架构信息
echo "当前系统架构是:$arch"
#export ALL_PROXY=http://192.168.2.5:8080
wget https://github.com/yt-dlp/FFmpeg-Builds/releases/download/latest/$pkg.tar.xz
tar -xvJf $pkg.tar.xz
mv $pkg ffmpeg
install_from_github() {
pkg=$1
wget https://github.com/yt-dlp/FFmpeg-Builds/releases/download/latest/$pkg.tar.xz
tar -xvJf $pkg.tar.xz
mkdir -p ffmpeg/bin
mv $pkg/bin/ffmpeg ffmpeg/bin/
mv $pkg/bin/ffprobe ffmpeg/bin/
rm -rf $pkg $pkg.tar.xz
}
install_from_ffmpeg() {
pkg=$1
wget https://johnvansickle.com/ffmpeg/builds/$pkg.tar.xz
mkdir -p $pkg
tar -xvJf $pkg.tar.xz -C $pkg
mkdir -p ffmpeg/bin
mv $pkg/*/ffmpeg ffmpeg/bin/
mv $pkg/*/ffprobe ffmpeg/bin/
rm -rf $pkg $pkg.tar.xz
}
# 基于架构执行不同的操作
case "$arch" in
x86_64)
echo "64位 x86 架构"
install_from_github ffmpeg-master-latest-linux64-gpl
#install_from_ffmpeg ffmpeg-git-amd64-static
;;
arm64 | aarch64)
echo "64位 ARM 架构"
install_from_github ffmpeg-master-latest-linuxarm64-gpl
#install_from_ffmpeg ffmpeg-git-arm64-static
;;
armv7l)
echo "armv7l 架构"
install_from_ffmpeg ffmpeg-git-armhf-static
;;
*)
echo "未知架构 $arch"
;;
esac

9
newpatch.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
./update-static-version.py
git add xiaomusic/static
git commit -m 'build: update static version'
cz bump --check-consistency --increment patch
git push -u origin main --tags

View File

@@ -1,34 +1,9 @@
#!/bin/bash
set -e
./update-static-version.py
git add xiaomusic/static
git commit -m 'build: update static version'
version_file=./pyproject.toml
# 获取当前版本号
current_version=$(grep -oE "version = \"[0-9]+\.[0-9]+\.[0-9]+\"" $version_file | cut -d'"' -f2)
echo "当前版本号: "$current_version
cz bump --check-consistency
# 将版本号分割成三部分
major=$(echo $current_version | cut -d'.' -f1)
minor=$(echo $current_version | cut -d'.' -f2)
patch=$(echo $current_version | cut -d'.' -f3)
echo "major: $major"
echo "minor: $minor"
echo "patch: $patch"
# 将补丁号加1
patch=$((patch + 1))
# 生成新版本号
new_version="$major.$minor.$patch"
# 将新版本号写入文件
sed -i "s/version.*/version = \"$new_version\"/g" $version_file
echo "新版本号:$new_version"
git diff
git add $version_file
git commit -m "new version v$new_version"
git tag v$new_version
git push -u origin main --tags

1333
pdm.lock generated

File diff suppressed because it is too large Load Diff

0
plugins/__init__.py Normal file
View File

9
plugins/code1.py Normal file
View File

@@ -0,0 +1,9 @@
async def code1(arg1):
global log, xiaomusic
log.info(f"code1:{arg1}")
did = xiaomusic._cur_did
await xiaomusic.do_tts(did, "你好,我是自定义的测试口令")
last_record = xiaomusic.last_record
query = last_record.get("query", "").strip()
await xiaomusic.do_tts(did, f"你说的是: {query}")

10
plugins/httpget.py Normal file
View File

@@ -0,0 +1,10 @@
import requests
def httpget(url):
global log
# 发起请求
response = requests.get(url, timeout=5) # 增加超时以避免长时间挂起
response.raise_for_status() # 如果响应不是200引发HTTPError异常
log.info(f"httpget url:{url} response:{response.text}")

View File

@@ -1,23 +1,88 @@
[project]
name = "xiaomusic"
version = "0.1.21"
version = "0.3.55"
description = "Play Music with xiaomi AI speaker"
authors = [
{name = "涵曦", email = "im.hanxi@gmail.com"},
{name = "涵曦", email = "im.hanxi@gmail.com"},
]
dependencies = [
"rich>=13.6.0",
"requests>=2.31.0",
"aiohttp>=3.8.6",
"miservice-fork>=2.2.1",
"miservice-fork>=2.7.0",
"mutagen>=1.47.0",
"yt-dlp>=2024.2.2.232707.dev0",
"flask[async]>=3.0.1",
"yt-dlp[default]>=2024.12.1.232904.dev0",
"uvicorn>=0.30.1",
"fastapi>=0.115.4",
"starlette>=0.37.2",
"aiofiles>=24.1.0",
"ga4mp>=2.0.4",
"apscheduler>=3.10.4",
"opencc-python-reimplemented==0.1.7",
"pillow>=10.4.0",
"python-multipart>=0.0.12",
"requests>=2.32.3",
]
requires-python = ">=3.10"
requires-python = ">=3.10,<=3.12"
readme = "README.md"
license = {text = "MIT"}
[project.urls]
Homepage = "https://github.com/hanxi/xiaomusic"
[project.scripts]
xiaomusic = "xiaomusic.cli:main"
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
[tool.pdm]
[tool.pdm.dev-dependencies]
lint = [
"ruff>=0.4.8",
]
dev = [
"commitizen>=3.27.0",
]
[tool.ruff]
lint.select = [
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"E", # pycodestyle - Error
"F", # Pyflakes
"I", # isort
"W", # pycodestyle - Warning
"UP", # pyupgrade
]
lint.ignore = [
"E501", # line-too-long
"W191", # tab-indentation
]
include = ["**/*.py", "**/*.pyi", "**/pyproject.toml"]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.ruff.lint.flake8-bugbear]
extend-immutable-calls = [
"fastapi.Depends",
"fastapi.params.Depends",
"fastapi.Query",
"fastapi.params.Query",
"fastapi.File"
]
[tool.pdm.scripts]
lint = "ruff check ."
fmt = "ruff format ."
lintfmt = {composite = ["ruff check --fix .", "ruff format ."]}
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "v$version"
version_scheme = "pep440"
version_provider = "pep621"
update_changelog_on_bump = true
major_version_zero = true
version_files = [
"xiaomusic/__init__.py",
]

View File

@@ -1,403 +0,0 @@
# This file is @generated by PDM.
# Please do not edit it manually.
aiohttp==3.9.3 \
--hash=sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb \
--hash=sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5 \
--hash=sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc \
--hash=sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4 \
--hash=sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc \
--hash=sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e \
--hash=sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60 \
--hash=sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38 \
--hash=sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b \
--hash=sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53 \
--hash=sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5 \
--hash=sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96 \
--hash=sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae \
--hash=sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce \
--hash=sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8 \
--hash=sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf \
--hash=sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d \
--hash=sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869 \
--hash=sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b \
--hash=sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52 \
--hash=sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5 \
--hash=sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4 \
--hash=sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7 \
--hash=sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5 \
--hash=sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54 \
--hash=sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3 \
--hash=sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5 \
--hash=sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c \
--hash=sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29 \
--hash=sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747 \
--hash=sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5 \
--hash=sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11 \
--hash=sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca \
--hash=sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768 \
--hash=sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6 \
--hash=sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2 \
--hash=sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6 \
--hash=sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266 \
--hash=sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d \
--hash=sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec \
--hash=sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5 \
--hash=sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1 \
--hash=sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679 \
--hash=sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283 \
--hash=sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b \
--hash=sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f
aiosignal==1.3.1 \
--hash=sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc \
--hash=sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17
asgiref==3.7.2 \
--hash=sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e \
--hash=sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed
async-timeout==4.0.3 \
--hash=sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f \
--hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028
attrs==23.1.0 \
--hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
--hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
blinker==1.7.0 \
--hash=sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9 \
--hash=sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182
brotli==1.1.0 \
--hash=sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128 \
--hash=sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9 \
--hash=sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3 \
--hash=sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd \
--hash=sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409 \
--hash=sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da \
--hash=sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50 \
--hash=sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180 \
--hash=sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d \
--hash=sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc \
--hash=sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265 \
--hash=sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327 \
--hash=sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd \
--hash=sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0 \
--hash=sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0 \
--hash=sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451 \
--hash=sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e \
--hash=sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248 \
--hash=sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91 \
--hash=sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724 \
--hash=sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966 \
--hash=sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951 \
--hash=sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8 \
--hash=sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d \
--hash=sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc \
--hash=sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61 \
--hash=sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1 \
--hash=sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2 \
--hash=sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6 \
--hash=sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9 \
--hash=sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2 \
--hash=sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf \
--hash=sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408 \
--hash=sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752 \
--hash=sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80 \
--hash=sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0 \
--hash=sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e
certifi==2023.7.22 \
--hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \
--hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9
charset-normalizer==3.3.0 \
--hash=sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786 \
--hash=sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e \
--hash=sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8 \
--hash=sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa \
--hash=sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d \
--hash=sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382 \
--hash=sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678 \
--hash=sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b \
--hash=sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e \
--hash=sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596 \
--hash=sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69 \
--hash=sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c \
--hash=sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459 \
--hash=sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7 \
--hash=sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908 \
--hash=sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a \
--hash=sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8 \
--hash=sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d \
--hash=sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d \
--hash=sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34 \
--hash=sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6 \
--hash=sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e \
--hash=sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c \
--hash=sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078 \
--hash=sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4 \
--hash=sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403 \
--hash=sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0 \
--hash=sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9 \
--hash=sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05 \
--hash=sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec \
--hash=sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56 \
--hash=sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e \
--hash=sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455 \
--hash=sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65 \
--hash=sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78 \
--hash=sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df \
--hash=sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1 \
--hash=sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989 \
--hash=sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63 \
--hash=sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649 \
--hash=sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2 \
--hash=sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd \
--hash=sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5 \
--hash=sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe \
--hash=sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293 \
--hash=sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e \
--hash=sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e
click==8.1.7 \
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
flask==3.0.1 \
--hash=sha256:6489f51bb3666def6f314e15f19d50a1869a19ae0e8c9a3641ffe66c77d42403 \
--hash=sha256:ca631a507f6dfe6c278ae20112cea3ff54ff2216390bf8880f6b035a5354af13
frozenlist==1.4.0 \
--hash=sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01 \
--hash=sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251 \
--hash=sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9 \
--hash=sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b \
--hash=sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0 \
--hash=sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b \
--hash=sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c \
--hash=sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467 \
--hash=sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1 \
--hash=sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300 \
--hash=sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea \
--hash=sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab \
--hash=sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb \
--hash=sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8 \
--hash=sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62 \
--hash=sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326 \
--hash=sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c \
--hash=sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431 \
--hash=sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3 \
--hash=sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956 \
--hash=sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472 \
--hash=sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc \
--hash=sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839 \
--hash=sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b \
--hash=sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f \
--hash=sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559 \
--hash=sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b \
--hash=sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95 \
--hash=sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb \
--hash=sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963 \
--hash=sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f
idna==3.4 \
--hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
--hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
itsdangerous==2.1.2 \
--hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \
--hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a
Jinja2==3.1.3 \
--hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \
--hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90
markdown-it-py==3.0.0 \
--hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \
--hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb
MarkupSafe==2.1.4 \
--hash=sha256:0042d6a9880b38e1dd9ff83146cc3c9c18a059b9360ceae207805567aacccc69 \
--hash=sha256:0c26f67b3fe27302d3a412b85ef696792c4a2386293c53ba683a89562f9399b0 \
--hash=sha256:0fbad3d346df8f9d72622ac71b69565e621ada2ce6572f37c2eae8dacd60385d \
--hash=sha256:31f57d64c336b8ccb1966d156932f3daa4fee74176b0fdc48ef580be774aae74 \
--hash=sha256:396549cea79e8ca4ba65525470d534e8a41070e6b3500ce2414921099cb73e8d \
--hash=sha256:3aae9af4cac263007fd6309c64c6ab4506dd2b79382d9d19a1994f9240b8db4f \
--hash=sha256:3ab3a886a237f6e9c9f4f7d272067e712cdb4efa774bef494dccad08f39d8ae6 \
--hash=sha256:5045e892cfdaecc5b4c01822f353cf2c8feb88a6ec1c0adef2a2e705eef0f656 \
--hash=sha256:5244324676254697fe5c181fc762284e2c5fceeb1c4e3e7f6aca2b6f107e60dc \
--hash=sha256:54a7e1380dfece8847c71bf7e33da5d084e9b889c75eca19100ef98027bd9f56 \
--hash=sha256:55d03fea4c4e9fd0ad75dc2e7e2b6757b80c152c032ea1d1de487461d8140efc \
--hash=sha256:78bc995e004681246e85e28e068111a4c3f35f34e6c62da1471e844ee1446250 \
--hash=sha256:987d13fe1d23e12a66ca2073b8d2e2a75cec2ecb8eab43ff5624ba0ad42764bc \
--hash=sha256:9e9e3c4020aa2dc62d5dd6743a69e399ce3de58320522948af6140ac959ab863 \
--hash=sha256:a0b838c37ba596fcbfca71651a104a611543077156cb0a26fe0c475e1f152ee8 \
--hash=sha256:a4d176cfdfde84f732c4a53109b293d05883e952bbba68b857ae446fa3119b4f \
--hash=sha256:a76055d5cb1c23485d7ddae533229039b850db711c554a12ea64a0fd8a0129e2 \
--hash=sha256:a76cd37d229fc385738bd1ce4cba2a121cf26b53864c1772694ad0ad348e509e \
--hash=sha256:a7cc49ef48a3c7a0005a949f3c04f8baa5409d3f663a1b36f0eba9bfe2a0396e \
--hash=sha256:abf5ebbec056817057bfafc0445916bb688a255a5146f900445d081db08cbabb \
--hash=sha256:b83041cda633871572f0d3c41dddd5582ad7d22f65a72eacd8d3d6d00291df26 \
--hash=sha256:c669391319973e49a7c6230c218a1e3044710bc1ce4c8e6eb71f7e6d43a2c131 \
--hash=sha256:d5291d98cd3ad9a562883468c690a2a238c4a6388ab3bd155b0c75dd55ece858 \
--hash=sha256:dac1ebf6983148b45b5fa48593950f90ed6d1d26300604f321c74a9ca1609f8e \
--hash=sha256:de8153a7aae3835484ac168a9a9bdaa0c5eee4e0bc595503c95d53b942879c84 \
--hash=sha256:e1a0d1924a5013d4f294087e00024ad25668234569289650929ab871231668e7 \
--hash=sha256:e7902211afd0af05fbadcc9a312e4cf10f27b779cf1323e78d52377ae4b72bea \
--hash=sha256:e888ff76ceb39601c59e219f281466c6d7e66bd375b4ec1ce83bcdc68306796b \
--hash=sha256:f06e5a9e99b7df44640767842f414ed5d7bedaaa78cd817ce04bbd6fd86e2dd6 \
--hash=sha256:f6be2d708a9d0e9b0054856f07ac7070fbe1754be40ca8525d5adccdbda8f475 \
--hash=sha256:f9917691f410a2e0897d1ef99619fd3f7dd503647c8ff2475bf90c3cf222ad74
mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
miservice-fork==2.3.2 \
--hash=sha256:1c907289cd354c349ead5e5b1f08e22aa23b8fa01114ecc7a2800e463751ba5b \
--hash=sha256:f24c40366df99d5db706f09528692bc490390fe7db950aa32f4948bafdc1a03d
multidict==6.0.4 \
--hash=sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9 \
--hash=sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8 \
--hash=sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03 \
--hash=sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710 \
--hash=sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569 \
--hash=sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636 \
--hash=sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49 \
--hash=sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93 \
--hash=sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0 \
--hash=sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4 \
--hash=sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc \
--hash=sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8 \
--hash=sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed \
--hash=sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98 \
--hash=sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3 \
--hash=sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe \
--hash=sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988 \
--hash=sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c \
--hash=sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c \
--hash=sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0 \
--hash=sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5 \
--hash=sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a \
--hash=sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b \
--hash=sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982 \
--hash=sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7 \
--hash=sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461 \
--hash=sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc \
--hash=sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547 \
--hash=sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0 \
--hash=sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171 \
--hash=sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba
mutagen==1.47.0 \
--hash=sha256:719fadef0a978c31b4cf3c956261b3c58b6948b32023078a2117b1de09f0fc99 \
--hash=sha256:edd96f50c5907a9539d8e5bba7245f62c9f520aef333d13392a79a4f70aca719
pycryptodomex==3.19.0 \
--hash=sha256:09c9401dc06fb3d94cb1ec23b4ea067a25d1f4c6b7b118ff5631d0b5daaab3cc \
--hash=sha256:0b2f1982c5bc311f0aab8c293524b861b485d76f7c9ab2c3ac9a25b6f7655975 \
--hash=sha256:136b284e9246b4ccf4f752d435c80f2c44fc2321c198505de1d43a95a3453b3c \
--hash=sha256:2126bc54beccbede6eade00e647106b4f4c21e5201d2b0a73e9e816a01c50905 \
--hash=sha256:263de9a96d2fcbc9f5bd3a279f14ea0d5f072adb68ebd324987576ec25da084d \
--hash=sha256:50cb18d4dd87571006fd2447ccec85e6cec0136632a550aa29226ba075c80644 \
--hash=sha256:5b883e1439ab63af976656446fb4839d566bb096f15fc3c06b5a99cde4927188 \
--hash=sha256:5d73e9fa3fe830e7b6b42afc49d8329b07a049a47d12e0ef9225f2fd220f19b2 \
--hash=sha256:67c8eb79ab33d0fbcb56842992298ddb56eb6505a72369c20f60bc1d2b6fb002 \
--hash=sha256:7cb51096a6a8d400724104db8a7e4f2206041a1f23e58924aa3d8d96bcb48338 \
--hash=sha256:800a2b05cfb83654df80266692f7092eeefe2a314fa7901dcefab255934faeec \
--hash=sha256:a3866d68e2fc345162b1b9b83ef80686acfe5cec0d134337f3b03950a0a8bf56 \
--hash=sha256:a588a1cb7781da9d5e1c84affd98c32aff9c89771eac8eaa659d2760666f7139 \
--hash=sha256:a77b79852175064c822b047fee7cf5a1f434f06ad075cc9986aa1c19a0c53eb0 \
--hash=sha256:af83a554b3f077564229865c45af0791be008ac6469ef0098152139e6bd4b5b6 \
--hash=sha256:b801216c48c0886742abf286a9a6b117e248ca144d8ceec1f931ce2dd0c9cb40 \
--hash=sha256:bfb040b5dda1dff1e197d2ef71927bd6b8bfcb9793bc4dfe0bb6df1e691eaacb \
--hash=sha256:c01678aee8ac0c1a461cbc38ad496f953f9efcb1fa19f5637cbeba7544792a53 \
--hash=sha256:c74eb1f73f788facece7979ce91594dc177e1a9b5d5e3e64697dd58299e5cb4d \
--hash=sha256:d4dd3b381ff5a5907a3eb98f5f6d32c64d319a840278ceea1dcfcc65063856f3 \
--hash=sha256:edbe083c299835de7e02c8aa0885cb904a75087d35e7bab75ebe5ed336e8c3e2
pygments==2.16.1 \
--hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \
--hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29
requests==2.31.0 \
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
rich==13.7.0 \
--hash=sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa \
--hash=sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235
typing-extensions==4.9.0 \
--hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \
--hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd
urllib3==2.0.6 \
--hash=sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2 \
--hash=sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564
websockets==12.0 \
--hash=sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b \
--hash=sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6 \
--hash=sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df \
--hash=sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b \
--hash=sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2 \
--hash=sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed \
--hash=sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd \
--hash=sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b \
--hash=sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931 \
--hash=sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30 \
--hash=sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370 \
--hash=sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be \
--hash=sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf \
--hash=sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b \
--hash=sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402 \
--hash=sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f \
--hash=sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123 \
--hash=sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603 \
--hash=sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45 \
--hash=sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558 \
--hash=sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4 \
--hash=sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480 \
--hash=sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447 \
--hash=sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8 \
--hash=sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04 \
--hash=sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c \
--hash=sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb \
--hash=sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b \
--hash=sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c \
--hash=sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92 \
--hash=sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113 \
--hash=sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f \
--hash=sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468 \
--hash=sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611 \
--hash=sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d \
--hash=sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca \
--hash=sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f \
--hash=sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2 \
--hash=sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077 \
--hash=sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2 \
--hash=sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374 \
--hash=sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc \
--hash=sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e \
--hash=sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53 \
--hash=sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399 \
--hash=sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547 \
--hash=sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3 \
--hash=sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870 \
--hash=sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5 \
--hash=sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7
Werkzeug==3.0.1 \
--hash=sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc \
--hash=sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10
yarl==1.9.2 \
--hash=sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571 \
--hash=sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7 \
--hash=sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191 \
--hash=sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea \
--hash=sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4 \
--hash=sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095 \
--hash=sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde \
--hash=sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0 \
--hash=sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528 \
--hash=sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6 \
--hash=sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be \
--hash=sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a \
--hash=sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8 \
--hash=sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6 \
--hash=sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608 \
--hash=sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82 \
--hash=sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3 \
--hash=sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d \
--hash=sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8 \
--hash=sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac \
--hash=sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8 \
--hash=sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0 \
--hash=sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb \
--hash=sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2 \
--hash=sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7 \
--hash=sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051 \
--hash=sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9 \
--hash=sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5 \
--hash=sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9 \
--hash=sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3 \
--hash=sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560
yt-dlp==2024.2.2.232707.dev0 \
--hash=sha256:7ff05434979d89e2a7c344d6771cc60229901fe4182c19a756472bb49a50e3b8 \
--hash=sha256:fd8d85f64842038cbdaaf7ee5812d98beed64fdcb4612f6e80b2c721bad13262

33
test/test_difflib.py Normal file
View File

@@ -0,0 +1,33 @@
import difflib
from xiaomusic.utils import (
find_best_match,
keyword_detection,
)
if __name__ == "__main__":
user_input = "八年的爱"
s1 = "冰冰超人 - 八年的爱新版"
s2 = "冰冰超人 - 八年的爱"
r1 = difflib.SequenceMatcher(None, s1, user_input).ratio()
r2 = difflib.SequenceMatcher(None, s2, user_input).ratio()
print(s1, r1)
print(s2, r2)
s3 = "其他"
str_list = [s2, s1, s3]
matches, remains = keyword_detection(user_input, str_list, n=10)
print(matches, remains)
extra_search_index = {}
extra_search_index["1"] = s1
extra_search_index["2"] = s2
extra_search_index["3"] = s3
real_names = find_best_match(
user_input,
str_list,
cutoff=0.4,
n=100,
extra_search_index=extra_search_index,
)
print(real_names)

View File

@@ -0,0 +1,32 @@
import math
from xiaomusic.const import (
SUPPORT_MUSIC_TYPE,
)
from xiaomusic.utils import (
get_local_music_duration,
traverse_music_directory,
)
async def test_one_music(filename):
# 获取播放时长
duration = await get_local_music_duration(filename)
sec = math.ceil(duration)
print(f"本地歌曲 : {filename} 的时长 {duration} {sec}")
async def main(directory):
# 获取所有歌曲文件
local_musics = traverse_music_directory(directory, 10, [], SUPPORT_MUSIC_TYPE)
print(local_musics)
for _, files in local_musics.items():
for file in files:
await test_one_music(file)
if __name__ == "__main__":
import asyncio
directory = "./music" # 替换为你的音乐目录路径
asyncio.run(main(directory))

47
test/test_music_tags.py Normal file
View File

@@ -0,0 +1,47 @@
import traceback
from xiaomusic.const import (
SUPPORT_MUSIC_TYPE,
)
from xiaomusic.utils import (
extract_audio_metadata,
traverse_music_directory,
)
# title 标题
# artist 艺术家
# album 影集
# year 年
# genre 性
# picture 图片
# lyrics 歌词
async def test_one_music(filename):
# 获取播放时长
try:
metadata = extract_audio_metadata(filename, "cache/picture_cache")
print(metadata)
except Exception as e:
print(f"歌曲 : {filename} no tag {e}")
traceback.print_exc()
async def main(directory):
# 获取所有歌曲文件
local_musics = traverse_music_directory(directory, 10, [], SUPPORT_MUSIC_TYPE)
for _, files in local_musics.items():
for file in files:
print(file)
# await test_one_music(file)
pass
await test_one_music("music/4 In Love - 一千零一个愿望.mp3")
# await test_one_music("./music/程响-人间烟火.flac")
if __name__ == "__main__":
import asyncio
directory = "./music" # 替换为你的音乐目录路径
asyncio.run(main(directory))

View File

@@ -0,0 +1,8 @@
from xiaomusic.utils import (
remove_common_prefix,
)
if __name__ == "__main__":
remove_common_prefix(
"./tmp/【无损音质】2024年9月酷狗热歌榜TOP100合集只选热歌最高的首首王炸分P合集"
)

View File

@@ -1 +0,0 @@
pdm export -o requirements.txt

56
update-static-version.py Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python3
import re
from pathlib import Path
def get_html_files(directory):
"""
获取指定目录下所有HTML文件的列表。
:param directory: 搜索HTML文件的目录。
:return: 搜索到的HTML文件的路径列表。
"""
return list(Path(directory).rglob("*.html"))
def update_html_version(html_files, version):
"""
更新HTML文件中所有以 ./ 开头的CSS和JS文件引用的版本号。
:param html_files: 需要更新的HTML文件路径的列表。
:param version: 新的版本号字符串。
"""
pattern = re.compile(r'(\./.*(css|js))\?version=[^"]*"')
for html_file in html_files:
if not html_file.exists():
print(f"文件 {html_file} 不存在。")
continue
html_content = html_file.read_text()
# 更新CSS和JS版本号
html_content = pattern.sub(rf'\g<1>?version={version}"', html_content)
# html_content = pattern.sub(fr'\g<1>"', html_content)
# 保存更改到HTML文件
html_file.write_text(html_content)
print(f"文件 {html_file} 已更新为使用新的版本号: {version}")
# 使用案例
if __name__ == "__main__":
import time
t = str(int(time.time()))
# 指定目录
html_directory = "xiaomusic/static/default" # 修改为实际的HTML文件目录路径
# 获取HTML文件列表
html_files_to_update = get_html_files(html_directory)
# 执行更新
update_html_version(html_files_to_update, t)

View File

@@ -0,0 +1 @@
__version__ = "0.3.55"

84
xiaomusic/analytics.py Normal file
View File

@@ -0,0 +1,84 @@
import asyncio
from datetime import datetime
from ga4mp import GtagMP
from xiaomusic import __version__
class Analytics:
def __init__(self, log):
self.gtag = None
self.current_date = None
self.log = log
self.init()
def init(self):
if self.gtag is not None:
return
gtag = GtagMP(
api_secret="sVRsf3T9StuWc-ZiWZxDVA",
measurement_id="G-Z09NC1K7ZW",
client_id="",
)
gtag.client_id = gtag.random_client_id()
gtag.store.set_user_property(name="version", value=__version__)
self.gtag = gtag
self.log.info("analytics init ok")
async def run_with_cancel(self, func, *args, **kwargs):
try:
asyncio.ensure_future(asyncio.to_thread(func, *args, **kwargs))
self.log.info("analytics run_with_cancel success")
except Exception as e:
self.log.warning(f"analytics run_with_cancel failed {e}")
return None
async def send_startup_event(self):
try:
await self.run_with_cancel(self._send_startup_event)
except Exception as e:
self.log.warning(f"analytics send_startup_event failed {e}")
self.init()
def _send_startup_event(self):
event = self.gtag.create_new_event(name="startup")
event.set_event_param(name="version", value=__version__)
event_list = [event]
self.gtag.send(events=event_list)
async def send_daily_event(self):
try:
await self.run_with_cancel(self._send_daily_event)
except Exception as e:
self.log.warning(f"analytics send_daily_event failed {e}")
self.init()
def _send_daily_event(self):
current_date = datetime.now().strftime("%Y-%m-%d")
if self.current_date == current_date:
return
event = self.gtag.create_new_event(name="daily_active_user")
event.set_event_param(name="version", value=__version__)
event.set_event_param(name="date", value=current_date)
event_list = [event]
self.gtag.send(events=event_list)
self.current_date = current_date
async def send_play_event(self, name, sec, hardware):
try:
await self.run_with_cancel(self._send_play_event, name, sec, hardware)
except Exception as e:
self.log.warning(f"analytics send_play_event failed {e}")
self.init()
def _send_play_event(self, name, sec, hardware):
event = self.gtag.create_new_event(name="play")
event.set_event_param(name="version", value=__version__)
event.set_event_param(name="music", value=name)
event.set_event_param(name="sec", value=sec)
event.set_event_param(name="hardware", value=hardware)
event_list = [event]
self.gtag.send(events=event_list)

View File

@@ -1,16 +1,38 @@
#!/usr/bin/env python3
import argparse
import asyncio
import json
import os
import signal
import uvicorn
from xiaomusic import __version__
from xiaomusic.config import Config
from xiaomusic.httpserver import HttpInit
from xiaomusic.httpserver import app as HttpApp
from xiaomusic.xiaomusic import XiaoMusic
LOGO = r"""
__ __ _ __ __ _
\ \/ / (_) __ _ ___ | \/ | _ _ ___ (_) ___
\ / | | / _` | / _ \ | |\/| | | | | | / __| | | / __|
/ \ | | | (_| | | (_) | | | | | | |_| | \__ \ | | | (__
/_/\_\ |_| \__,_| \___/ |_| |_| \__,_| |___/ |_| \___|
{}
"""
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--port",
dest="port",
help="监听端口",
)
parser.add_argument(
"--hardware",
dest="hardware",
help="小爱 hardware",
help="小爱音箱型号",
)
parser.add_argument(
"--account",
@@ -27,20 +49,6 @@ def main():
dest="cookie",
help="xiaomi cookie",
)
parser.add_argument(
"--use_command",
dest="use_command",
action="store_true",
default=None,
help="use command to tts",
)
parser.add_argument(
"--mute_xiaoai",
dest="mute_xiaoai",
action="store_true",
default=None,
help="try to mute xiaoai answer",
)
parser.add_argument(
"--verbose",
dest="verbose",
@@ -58,13 +66,101 @@ def main():
dest="ffmpeg_location",
help="ffmpeg bin path",
)
parser.add_argument(
"--enable_config_example",
dest="enable_config_example",
help="是否输出示例配置文件",
action="store_true",
)
print(LOGO.format(f"XiaoMusic v{__version__} by: github.com/hanxi"))
options = parser.parse_args()
config = Config.from_options(options)
xiaomusic = XiaoMusic(config)
loop = asyncio.get_event_loop()
loop.run_until_complete(xiaomusic.run_forever())
LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"format": f"%(asctime)s [{__version__}] [%(levelname)s] %(message)s",
"datefmt": "[%X]",
"use_colors": False,
},
"access": {
"format": f"%(asctime)s [{__version__}] [%(levelname)s] %(message)s",
"datefmt": "[%X]",
},
},
"handlers": {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
},
"access": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
"file": {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"formatter": "access",
"filename": config.log_file,
"maxBytes": 10 * 1024 * 1024,
"backupCount": 1,
},
},
"loggers": {
"uvicorn": {
"handlers": [
"default",
"file",
],
"level": "INFO",
},
"uvicorn.error": {
"level": "INFO",
},
"uvicorn.access": {
"handlers": [
"access",
"file",
],
"level": "INFO",
"propagate": False,
},
},
}
try:
filename = config.getsettingfile()
with open(filename, encoding="utf-8") as f:
data = json.loads(f.read())
config.update_config(data)
except Exception as e:
print(f"Execption {e}")
def run_server(port):
xiaomusic = XiaoMusic(config)
HttpInit(xiaomusic)
uvicorn.run(
HttpApp,
host=["0.0.0.0", "::"],
port=port,
log_config=LOGGING_CONFIG,
)
def signal_handler(sig, frame):
print("主进程收到退出信号,准备退出...")
os._exit(0) # 退出主进程
# 捕获主进程的退出信号
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
port = int(config.port)
run_server(port)
if __name__ == "__main__":

View File

@@ -3,108 +3,228 @@ from __future__ import annotations
import argparse
import json
import os
from dataclasses import dataclass, field
from typing import Any, Iterable
from dataclasses import asdict, dataclass, field
from typing import get_type_hints
from xiaomusic.const import (
PLAY_TYPE_ALL,
PLAY_TYPE_ONE,
PLAY_TYPE_RND,
PLAY_TYPE_SEQ,
PLAY_TYPE_SIN,
)
from xiaomusic.utils import validate_proxy
LATEST_ASK_API = "https://userprofile.mina.mi.com/device_profile/v2/conversation?source=dialogu&hardware={hardware}&timestamp={timestamp}&limit=2"
COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={user_id}"
HARDWARE_COMMAND_DICT = {
# hardware: (tts_command, wakeup_command, volume_command)
"LX06": ("5-1", "5-5", "2-1"),
"L05B": ("5-3", "5-4", "2-1"),
"S12A": ("5-1", "5-5", "2-1"),
"LX01": ("5-1", "5-5", "2-1"),
"L06A": ("5-1", "5-5", "2-1"),
"LX04": ("5-1", "5-4", "2-1"),
"L05C": ("5-3", "5-4", "2-1"),
"L17A": ("7-3", "7-4", "2-1"),
"X08E": ("7-3", "7-4", "2-1"),
"LX05A": ("5-1", "5-5", "2-1"), # 小爱红外版
"LX5A": ("5-1", "5-5", "2-1"), # 小爱红外版
"L07A": ("5-1", "5-5", "2-1"), # Redmi小爱音箱Play(l7a)
"L15A": ("7-3", "7-4", "2-1"),
"X6A": ("7-3", "7-4", "2-1"), # 小米智能家庭屏6
"X10A": ("7-3", "7-4", "2-1"), # 小米智能家庭屏10
# add more here
}
# 默认口令
def default_key_word_dict():
return {
"下一首": "play_next",
"上一首": "play_prev",
"单曲循环": "set_play_type_one",
"全部循环": "set_play_type_all",
"随机播放": "set_play_type_rnd",
"单曲播放": "set_play_type_sin",
"顺序播放": "set_play_type_seq",
"分钟后关机": "stop_after_minute",
"刷新列表": "gen_music_list",
"加入收藏": "add_to_favorites",
"收藏歌曲": "add_to_favorites",
"取消收藏": "del_from_favorites",
"播放列表第": "play_music_list_index",
}
DEFAULT_COMMAND = ("5-1", "5-5", "2-1")
KEY_WORD_DICT = {
"播放歌曲": "play",
"放歌曲": "play",
"下一首": "play_next",
"单曲循环": "set_play_type_one",
"全部循环": "set_play_type_all",
"随机播放": "random_play",
"关机": "stop",
"停止播放": "stop",
"分钟后关机": "stop_after_minute",
"set_volume#": "set_volume",
}
def default_user_key_word_dict():
return {
"测试自定义口令": 'exec#code1("hello")',
"测试链接": 'exec#httpget("https://github.com/hanxi/xiaomusic")',
}
# 命令参数在前面
KEY_WORD_ARG_BEFORE_DICT = {
"分钟后关机": True,
}
# 匹配优先级
KEY_MATCH_ORDER = [
"set_volume#",
"分钟后关机",
"播放歌曲",
"放歌曲",
"下一首",
"单曲循环",
"全部循环",
"随机播放",
"关机",
"停止播放",
]
SUPPORT_MUSIC_TYPE = [
".mp3",
".flac",
]
# 口令匹配优先级
def default_key_match_order():
return [
"分钟后关机",
"下一首",
"上一首",
"单曲循环",
"全部循环",
"随机播放",
"单曲播放",
"顺序播放",
"关机",
"刷新列表",
"播放列表第",
"播放列表",
"加入收藏",
"收藏歌曲",
"取消收藏",
]
@dataclass
class Device:
did: str = ""
device_id: str = ""
hardware: str = ""
name: str = ""
play_type: int = ""
cur_music: str = ""
cur_playlist: str = ""
@dataclass
class Config:
hardware: str = os.getenv("MI_HARDWARE", "L07A")
account: str = os.getenv("MI_USER", "")
password: str = os.getenv("MI_PASS", "")
mi_did: str = os.getenv("MI_DID", "")
mute_xiaoai: bool = True
mi_did: str = os.getenv("MI_DID", "") # 逗号分割支持多设备
miio_tts_command: str = os.getenv("MIIO_TTS_CMD", "")
cookie: str = ""
use_command: bool = False
verbose: bool = False
verbose: bool = os.getenv("XIAOMUSIC_VERBOSE", "").lower() == "true"
music_path: str = os.getenv("XIAOMUSIC_MUSIC_PATH", "music")
temp_path: str = os.getenv("XIAOMUSIC_TEMP_PATH", "music/tmp")
download_path: str = os.getenv("XIAOMUSIC_DOWNLOAD_PATH", "music/download")
conf_path: str = os.getenv("XIAOMUSIC_CONF_PATH", "conf")
cache_dir: str = os.getenv("XIAOMUSIC_CACHE_DIR", "cache")
hostname: str = os.getenv("XIAOMUSIC_HOSTNAME", "192.168.2.5")
port: int = int(os.getenv("XIAOMUSIC_PORT", "8090"))
proxy: str | None = os.getenv("XIAOMUSIC_PROXY", None)
port: int = int(os.getenv("XIAOMUSIC_PORT", "8090")) # 监听端口
public_port: int = int(os.getenv("XIAOMUSIC_PUBLIC_PORT", 0)) # 歌曲访问端口
proxy: str = os.getenv("XIAOMUSIC_PROXY", None)
search_prefix: str = os.getenv(
"XIAOMUSIC_SEARCH", "ytsearch:"
"XIAOMUSIC_SEARCH", "bilisearch:"
) # "bilisearch:" or "ytsearch:"
ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin")
active_cmd: str = os.getenv(
"XIAOMUSIC_ACTIVE_CMD",
"play,set_play_type_rnd,playlocal,play_music_list,play_music_list_index,stop_after_minute,stop",
)
exclude_dirs: str = os.getenv("XIAOMUSIC_EXCLUDE_DIRS", "@eaDir,tmp")
music_path_depth: int = int(os.getenv("XIAOMUSIC_MUSIC_PATH_DEPTH", "10"))
disable_httpauth: bool = (
os.getenv("XIAOMUSIC_DISABLE_HTTPAUTH", "true").lower() == "true"
)
httpauth_username: str = os.getenv("XIAOMUSIC_HTTPAUTH_USERNAME", "")
httpauth_password: str = os.getenv("XIAOMUSIC_HTTPAUTH_PASSWORD", "")
music_list_url: str = os.getenv("XIAOMUSIC_MUSIC_LIST_URL", "")
music_list_json: str = os.getenv("XIAOMUSIC_MUSIC_LIST_JSON", "")
custom_play_list_json: str = os.getenv("XIAOMUSIC_CUSTOM_PLAY_LIST_JSON", "")
disable_download: bool = (
os.getenv("XIAOMUSIC_DISABLE_DOWNLOAD", "false").lower() == "true"
)
key_word_dict: dict[str, str] = field(default_factory=default_key_word_dict)
key_match_order: list[str] = field(default_factory=default_key_match_order)
use_music_api: bool = (
os.getenv("XIAOMUSIC_USE_MUSIC_API", "false").lower() == "true"
)
use_music_audio_id: str = os.getenv(
"XIAOMUSIC_USE_MUSIC_AUDIO_ID", "1582971365183456177"
)
use_music_id: str = os.getenv("XIAOMUSIC_USE_MUSIC_ID", "355454500")
log_file: str = os.getenv("XIAOMUSIC_LOG_FILE", "xiaomusic.log.txt")
# 模糊搜索匹配的最低相似度阈值
fuzzy_match_cutoff: float = float(os.getenv("XIAOMUSIC_FUZZY_MATCH_CUTOFF", "0.6"))
# 开启模糊搜索
enable_fuzzy_match: bool = (
os.getenv("XIAOMUSIC_ENABLE_FUZZY_MATCH", "true").lower() == "true"
)
stop_tts_msg: str = os.getenv("XIAOMUSIC_STOP_TTS_MSG", "收到,再见")
enable_config_example: bool = False
keywords_playlocal: str = os.getenv(
"XIAOMUSIC_KEYWORDS_PLAYLOCAL", "播放本地歌曲,本地播放歌曲"
)
keywords_play: str = os.getenv("XIAOMUSIC_KEYWORDS_PLAY", "播放歌曲,放歌曲")
keywords_stop: str = os.getenv("XIAOMUSIC_KEYWORDS_STOP", "关机,暂停,停止,停止播放")
keywords_playlist: str = os.getenv(
"XIAOMUSIC_KEYWORDS_PLAYLIST", "播放列表,播放歌单"
)
user_key_word_dict: dict[str, str] = field(
default_factory=default_user_key_word_dict
)
enable_force_stop: bool = (
os.getenv("XIAOMUSIC_ENABLE_FORCE_STOP", "false").lower() == "true"
)
devices: dict[str, Device] = field(default_factory=dict)
group_list: str = os.getenv(
"XIAOMUSIC_GROUP_LIST", ""
) # did1:group_name,did2:group_name
remove_id3tag: bool = (
os.getenv("XIAOMUSIC_REMOVE_ID3TAG", "false").lower() == "true"
)
convert_to_mp3: bool = os.getenv("CONVERT_TO_MP3", "false").lower() == "true"
delay_sec: int = int(os.getenv("XIAOMUSIC_DELAY_SEC", 3)) # 下一首歌延迟播放秒数
continue_play: bool = (
os.getenv("XIAOMUSIC_CONTINUE_PLAY", "false").lower() == "true"
)
pull_ask_sec: int = int(os.getenv("XIAOMUSIC_PULL_ASK_SEC", "1"))
crontab_json: str = os.getenv("XIAOMUSIC_CRONTAB_JSON", "") # 定时任务
enable_yt_dlp_cookies: bool = (
os.getenv("XIAOMUSIC_ENABLE_YT_DLP_COOKIES", "false").lower() == "true"
)
get_ask_by_mina: bool = (
os.getenv("XIAOMUSIC_GET_ASK_BY_MINA", "false").lower() == "true"
)
play_type_one_tts_msg: str = os.getenv(
"XIAOMUSIC_PLAY_TYPE_ONE_TTS_MSG", "已经设置为单曲循环"
)
play_type_all_tts_msg: str = os.getenv(
"XIAOMUSIC_PLAY_TYPE_ALL_TTS_MSG", "已经设置为全部循环"
)
play_type_rnd_tts_msg: str = os.getenv(
"XIAOMUSIC_PLAY_TYPE_RND_TTS_MSG", "已经设置为随机播放"
)
play_type_sin_tts_msg: str = os.getenv(
"XIAOMUSIC_PLAY_TYPE_SIN_TTS_MSG", "已经设置为单曲播放"
)
play_type_seq_tts_msg: str = os.getenv(
"XIAOMUSIC_PLAY_TYPE_SEQ_TTS_MSG", "已经设置为顺序播放"
)
recently_added_playlist_len: int = int(
os.getenv("XIAOMUSIC_RECENTLY_ADDED_PLAYLIST_LEN", "50")
)
def append_keyword(self, keys, action):
for key in keys.split(","):
if key:
self.key_word_dict[key] = action
if key not in self.key_match_order:
self.key_match_order.append(key)
def append_user_keyword(self):
for k, v in self.user_key_word_dict.items():
self.key_word_dict[k] = v
if k not in self.key_match_order:
self.key_match_order.append(k)
def init_keyword(self):
self.key_match_order = default_key_match_order()
self.key_word_dict = default_key_word_dict()
self.append_keyword(self.keywords_playlocal, "playlocal")
self.append_keyword(self.keywords_play, "play")
self.append_keyword(self.keywords_stop, "stop")
self.append_keyword(self.keywords_playlist, "play_music_list")
self.append_user_keyword()
self.key_match_order = [
x for x in self.key_match_order if x in self.key_word_dict
]
def __post_init__(self) -> None:
if self.proxy:
validate_proxy(self.proxy)
@property
def tts_command(self) -> str:
return HARDWARE_COMMAND_DICT.get(self.hardware, DEFAULT_COMMAND)[0]
@property
def wakeup_command(self) -> str:
return HARDWARE_COMMAND_DICT.get(self.hardware, DEFAULT_COMMAND)[1]
@property
def volume_command(self) -> str:
return HARDWARE_COMMAND_DICT.get(self.hardware, DEFAULT_COMMAND)[2]
self.init_keyword()
# 保存配置到 config-example.json 文件
if self.enable_config_example:
with open("config-example.json", "w") as f:
data = asdict(self)
json.dump(data, f, ensure_ascii=False, indent=2)
@classmethod
def from_options(cls, options: argparse.Namespace) -> Config:
@@ -116,12 +236,94 @@ class Config:
config[key] = value
return cls(**config)
@classmethod
def convert_value(cls, k, v, type_hints):
if v is not None and k in type_hints:
expected_type = type_hints[k]
try:
if expected_type is bool:
converted_value = False
if str(v).lower() == "true":
converted_value = True
elif expected_type == dict[str, Device]:
converted_value = {}
for kk, vv in v.items():
converted_value[kk] = Device(**vv)
else:
converted_value = expected_type(v)
return converted_value
except (ValueError, TypeError) as e:
print(f"Error converting {k}:{v} to {expected_type}: {e}")
return None
@classmethod
def read_from_file(cls, config_path: str) -> dict:
result = {}
with open(config_path, "rb") as f:
config = json.load(f)
for key, value in config.items():
if value is not None and key in cls.__dataclass_fields__:
result[key] = value
data = json.load(f)
type_hints = get_type_hints(cls)
for k, v in data.items():
converted_value = cls.convert_value(k, v, type_hints)
if converted_value is not None:
result[k] = converted_value
return result
def update_config(self, data):
type_hints = get_type_hints(self, globals(), locals())
for k, v in data.items():
converted_value = self.convert_value(k, v, type_hints)
if converted_value is not None:
setattr(self, k, converted_value)
self.init_keyword()
# 获取设置文件
def getsettingfile(self):
# 兼容旧配置空的情况
if not self.conf_path:
self.conf_path = "conf"
if not os.path.exists(self.conf_path):
os.makedirs(self.conf_path)
filename = os.path.join(self.conf_path, "setting.json")
return filename
@property
def tag_cache_path(self):
if not os.path.exists(self.cache_dir):
os.makedirs(self.cache_dir)
filename = os.path.join(self.cache_dir, "tag_cache.json")
return filename
@property
def picture_cache_path(self):
cache_path = os.path.join(self.cache_dir, "picture_cache")
if not os.path.exists(cache_path):
os.makedirs(cache_path)
return cache_path
@property
def yt_dlp_cookies_path(self):
if not os.path.exists(self.conf_path):
os.makedirs(self.conf_path)
cookies_path = os.path.join(self.conf_path, "yt-dlp-cookie.txt")
return cookies_path
@property
def temp_dir(self):
if not os.path.exists(self.temp_path):
os.makedirs(self.temp_path)
return self.temp_path
def get_play_type_tts(self, play_type):
if play_type == PLAY_TYPE_ONE:
return self.play_type_one_tts_msg
if play_type == PLAY_TYPE_ALL:
return self.play_type_all_tts_msg
if play_type == PLAY_TYPE_RND:
return self.play_type_rnd_tts_msg
if play_type == PLAY_TYPE_SIN:
return self.play_type_sin_tts_msg
if play_type == PLAY_TYPE_SEQ:
return self.play_type_seq_tts_msg
return ""

29
xiaomusic/const.py Normal file
View File

@@ -0,0 +1,29 @@
SUPPORT_MUSIC_TYPE = [
".mp3",
".flac",
".wav",
".ape",
".ogg",
".m4a",
]
LATEST_ASK_API = "https://userprofile.mina.mi.com/device_profile/v2/conversation?source=dialogu&hardware={hardware}&timestamp={timestamp}&limit=2"
COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={user_id}"
PLAY_TYPE_ONE = 0 # 单曲循环
PLAY_TYPE_ALL = 1 # 全部循环
PLAY_TYPE_RND = 2 # 随机播放
PLAY_TYPE_SIN = 3 # 单曲播放
PLAY_TYPE_SEQ = 4 # 顺序播放
# 需要采用 mina 获取对话记录的设备型号
GET_ASK_BY_MINA = {
"M01",
}
# 需要使用 play_musci 接口的设备型号
NEED_USE_PLAY_MUSIC_API = {
"X08C",
"X08E",
"X8F",
}

113
xiaomusic/crontab.py Normal file
View File

@@ -0,0 +1,113 @@
import json
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
class Crontab:
def __init__(self, log):
self.log = log
self.scheduler = AsyncIOScheduler()
def start(self):
self.scheduler.start()
def add_job(self, expression, job):
try:
trigger = CronTrigger.from_crontab(expression)
self.scheduler.add_job(job, trigger)
except ValueError as e:
self.log.error(f"Invalid crontab expression {e}")
except Exception as e:
self.log.exception(f"Execption {e}")
# 添加关机任务
def add_job_stop(self, expression, xiaomusic, did, **kwargs):
async def job():
await xiaomusic.stop(did, "notts")
self.add_job(expression, job)
# 添加播放任务
def add_job_play(self, expression, xiaomusic, did, arg1, **kwargs):
async def job():
await xiaomusic.play(did, arg1)
self.add_job(expression, job)
# 添加播放列表任务
def add_job_play_music_list(self, expression, xiaomusic, did, arg1, **kwargs):
async def job():
await xiaomusic.play_music_list(did, arg1)
self.add_job(expression, job)
# 添加语音播放任务
def add_job_tts(self, expression, xiaomusic, did, arg1, **kwargs):
async def job():
await xiaomusic.do_tts(did, arg1)
self.add_job(expression, job)
# 刷新播放列表任务
def add_job_refresh_music_list(self, expression, xiaomusic, **kwargs):
async def job():
await xiaomusic.gen_music_list()
self.add_job(expression, job)
# 设置音量任务
def add_job_set_volume(self, expression, xiaomusic, did, arg1, **kwargs):
async def job():
await xiaomusic.set_volume(did, arg1)
self.add_job(expression, job)
# 设置播放类型任务
def add_job_set_play_type(self, expression, xiaomusic, did, arg1, **kwargs):
async def job():
play_type = int(arg1)
await xiaomusic.set_play_type(did, play_type, False)
self.add_job(expression, job)
def add_job_cron(self, xiaomusic, cron):
expression = cron["expression"] # cron 计划格式
name = cron["name"] # stop, play, play_music_list, tts
did = cron.get("did", "")
arg1 = cron.get("arg1", "")
jobname = f"add_job_{name}"
func = getattr(self, jobname, None)
if callable(func):
func(expression, xiaomusic, did=did, arg1=arg1)
self.log.info(
f"crontab add_job_cron ok. did:{did}, name:{name}, arg1:{arg1}"
)
else:
self.log.error(
f"'{self.__class__.__name__}' object has no attribute '{jobname}'"
)
# 清空任务
def clear_jobs(self):
for job in self.scheduler.get_jobs():
try:
job.remove()
except Exception as e:
self.log.exception(f"Execption {e}")
# 重新加载计划任务
def reload_config(self, xiaomusic):
self.clear_jobs()
crontab_json = xiaomusic.config.crontab_json
if not crontab_json:
return
try:
cron_list = json.loads(crontab_json)
for cron in cron_list:
self.add_job_cron(xiaomusic, cron)
self.log.info("crontab reload_config ok")
except Exception as e:
self.log.exception(f"Execption {e}")

View File

@@ -1,74 +1,700 @@
#!/usr/bin/env python3
import asyncio
import hashlib
import json
import os
import traceback
import re
import secrets
import shutil
import tempfile
import urllib.parse
from contextlib import asynccontextmanager
from dataclasses import asdict
from typing import Annotated
from flask import Flask, request, send_from_directory
from threading import Thread
import aiofiles
from fastapi import (
Depends,
FastAPI,
File,
HTTPException,
Query,
Request,
UploadFile,
status,
)
from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
from fastapi.responses import RedirectResponse
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from starlette.background import BackgroundTask
from starlette.responses import FileResponse, Response
from xiaomusic.config import (
KEY_WORD_DICT,
from xiaomusic import __version__
from xiaomusic.utils import (
convert_file_to_mp3,
deepcopy_data_no_sensitive_info,
download_one_music,
download_playlist,
downloadfile,
get_latest_version,
is_mp3,
remove_common_prefix,
remove_id3_tags,
try_add_access_control_param,
)
app = Flask(__name__)
host = "0.0.0.0"
port = 8090
static_path = "music"
xiaomusic = None
config = None
log = None
@app.route("/allcmds")
def allcmds():
return KEY_WORD_DICT
@asynccontextmanager
async def app_lifespan(app):
if xiaomusic is not None:
asyncio.create_task(xiaomusic.run_forever())
try:
yield
except Exception as e:
log.exception(f"Execption {e}")
@app.route("/getvolume")
def getvolume():
security = HTTPBasic()
def verification(
credentials: Annotated[HTTPBasicCredentials, Depends(security)],
):
current_username_bytes = credentials.username.encode("utf8")
correct_username_bytes = config.httpauth_username.encode("utf8")
is_correct_username = secrets.compare_digest(
current_username_bytes, correct_username_bytes
)
current_password_bytes = credentials.password.encode("utf8")
correct_password_bytes = config.httpauth_password.encode("utf8")
is_correct_password = secrets.compare_digest(
current_password_bytes, correct_password_bytes
)
if not (is_correct_username and is_correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
return True
def no_verification():
return True
app = FastAPI(
lifespan=app_lifespan,
version=__version__,
docs_url=None,
redoc_url=None,
openapi_url=None,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许访问的源
allow_credentials=False, # 支持 cookie
allow_methods=["*"], # 允许使用的请求方法
allow_headers=["*"], # 允许携带的 Headers
)
def reset_http_server():
log.info(f"disable_httpauth:{config.disable_httpauth}")
if config.disable_httpauth:
app.dependency_overrides[verification] = no_verification
else:
app.dependency_overrides = {}
class AuthStaticFiles(StaticFiles):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
async def __call__(self, scope, receive, send) -> None:
request = Request(scope, receive)
if not config.disable_httpauth:
assert verification(await security(request))
await super().__call__(scope, receive, send)
def HttpInit(_xiaomusic):
global xiaomusic, config, log
xiaomusic = _xiaomusic
config = xiaomusic.config
log = xiaomusic.log
folder = os.path.dirname(__file__)
app.mount("/static", AuthStaticFiles(directory=f"{folder}/static"), name="static")
reset_http_server()
@app.get("/")
async def read_index(Verifcation=Depends(verification)):
folder = os.path.dirname(__file__)
return FileResponse(f"{folder}/static/index.html")
@app.get("/getversion")
def getversion(Verifcation=Depends(verification)):
log.debug("getversion %s", __version__)
return {"version": __version__}
@app.get("/getvolume")
async def getvolume(did: str = "", Verifcation=Depends(verification)):
if not xiaomusic.did_exist(did):
return {"volume": 0}
volume = await xiaomusic.get_volume(did=did)
return {"volume": volume}
class DidVolume(BaseModel):
did: str
volume: int = 0
@app.post("/setvolume")
async def setvolume(data: DidVolume, Verifcation=Depends(verification)):
did = data.did
volume = data.volume
if not xiaomusic.did_exist(did):
return {"ret": "Did not exist"}
log.info(f"set_volume {did} {volume}")
await xiaomusic.set_volume(did=did, arg1=volume)
return {"ret": "OK", "volume": volume}
@app.get("/searchmusic")
def searchmusic(name: str = "", Verifcation=Depends(verification)):
return xiaomusic.searchmusic(name)
@app.get("/playingmusic")
def playingmusic(did: str = "", Verifcation=Depends(verification)):
if not xiaomusic.did_exist(did):
return {"ret": "Did not exist"}
is_playing = xiaomusic.isplaying(did)
cur_music = xiaomusic.playingmusic(did)
cur_playlist = xiaomusic.get_cur_play_list(did)
# 播放进度
offset, duration = xiaomusic.get_offset_duration(did)
return {
"volume": xiaomusic.get_volume(),
"ret": "OK",
"is_playing": is_playing,
"cur_music": cur_music,
"cur_playlist": cur_playlist,
"offset": offset,
"duration": duration,
}
@app.route("/", methods=["GET"])
def redirect_to_index():
return send_from_directory("static", "index.html")
class DidCmd(BaseModel):
did: str
cmd: str
@app.route("/cmd", methods=["POST"])
async def do_cmd():
data = request.get_json()
cmd = data.get("cmd")
@app.post("/cmd")
async def do_cmd(data: DidCmd, Verifcation=Depends(verification)):
did = data.did
cmd = data.cmd
log.info(f"docmd. did:{did} cmd:{cmd}")
if not xiaomusic.did_exist(did):
return {"ret": "Did not exist"}
if len(cmd) > 0:
log.debug("docmd. cmd:%s", cmd)
xiaomusic.set_last_record(cmd)
try:
await xiaomusic.cancel_all_tasks()
task = asyncio.create_task(xiaomusic.do_check_cmd(did=did, query=cmd))
xiaomusic.append_running_task(task)
except Exception as e:
log.warning(f"Execption {e}")
return {"ret": "OK"}
return {"ret": "Unknow cmd"}
def static_path_handler(filename):
log.debug(filename)
log.debug(static_path)
absolute_path = os.path.abspath(static_path)
log.debug(absolute_path)
return send_from_directory(absolute_path, filename)
@app.get("/cmdstatus")
async def cmd_status(Verifcation=Depends(verification)):
finish = await xiaomusic.is_task_finish()
if finish:
return {"ret": "OK", "status": "finish"}
return {"ret": "OK", "status": "running"}
def run_app():
app.run(host=host, port=port)
@app.get("/getsetting")
async def getsetting(need_device_list: bool = False, Verifcation=Depends(verification)):
config = xiaomusic.getconfig()
data = asdict(config)
data["password"] = "******"
data["httpauth_password"] = "******"
if need_device_list:
device_list = await xiaomusic.getalldevices()
log.info(f"getsetting device_list: {device_list}")
data["device_list"] = device_list
return data
def StartHTTPServer(_port, _static_path, _xiaomusic):
global port, static_path, xiaomusic, log
port = _port
static_path = _static_path
xiaomusic = _xiaomusic
log = xiaomusic.log
@app.post("/savesetting")
async def savesetting(request: Request, Verifcation=Depends(verification)):
try:
data_json = await request.body()
data = json.loads(data_json.decode("utf-8"))
debug_data = deepcopy_data_no_sensitive_info(data)
log.info(f"saveconfig: {debug_data}")
config = xiaomusic.getconfig()
if data["password"] == "******" or data["password"] == "":
data["password"] = config.password
if data["httpauth_password"] == "******" or data["httpauth_password"] == "":
data["httpauth_password"] = config.httpauth_password
await xiaomusic.saveconfig(data)
reset_http_server()
return "save success"
except json.JSONDecodeError as err:
raise HTTPException(status_code=400, detail="Invalid JSON") from err
app.add_url_rule(
f"/{static_path}/<path:filename>", "static_path_handler", static_path_handler
)
server_thread = Thread(target=run_app)
server_thread.daemon = True
server_thread.start()
xiaomusic.log.info(f"Serving on {host}:{port}")
@app.get("/musiclist")
async def musiclist(Verifcation=Depends(verification)):
return xiaomusic.get_music_list()
@app.get("/musicinfo")
async def musicinfo(
name: str, musictag: bool = False, Verifcation=Depends(verification)
):
url = xiaomusic.get_music_url(name)
info = {
"ret": "OK",
"name": name,
"url": url,
}
if musictag:
info["tags"] = xiaomusic.get_music_tags(name)
return info
@app.get("/musicinfos")
async def musicinfos(
name: list[str] = Query(None),
musictag: bool = False,
Verifcation=Depends(verification),
):
ret = []
for music_name in name:
url = xiaomusic.get_music_url(music_name)
info = {
"name": music_name,
"url": url,
}
if musictag:
info["tags"] = xiaomusic.get_music_tags(music_name)
ret.append(info)
return ret
class MusicInfoObj(BaseModel):
musicname: str
title: str = ""
artist: str = ""
album: str = ""
year: str = ""
genre: str = ""
lyrics: str = ""
picture: str = "" # base64
@app.post("/setmusictag")
async def setmusictag(info: MusicInfoObj, Verifcation=Depends(verification)):
ret = xiaomusic.set_music_tag(info.musicname, info)
return {"ret": ret}
@app.get("/curplaylist")
async def curplaylist(did: str = "", Verifcation=Depends(verification)):
if not xiaomusic.did_exist(did):
return ""
return xiaomusic.get_cur_play_list(did)
class MusicItem(BaseModel):
name: str
@app.post("/delmusic")
async def delmusic(data: MusicItem, Verifcation=Depends(verification)):
log.info(data)
await xiaomusic.del_music(data.name)
return "success"
class UrlInfo(BaseModel):
url: str
class DidPlayMusic(BaseModel):
did: str
musicname: str = ""
searchkey: str = ""
@app.post("/playmusic")
async def playmusic(data: DidPlayMusic, Verifcation=Depends(verification)):
did = data.did
musicname = data.musicname
searchkey = data.searchkey
if not xiaomusic.did_exist(did):
return {"ret": "Did not exist"}
log.info(f"playmusic {did} musicname:{musicname} searchkey:{searchkey}")
await xiaomusic.do_play(did, musicname, searchkey)
return {"ret": "OK"}
class DidPlayMusicList(BaseModel):
did: str
listname: str = ""
musicname: str = ""
@app.post("/playmusiclist")
async def playmusiclist(data: DidPlayMusicList, Verifcation=Depends(verification)):
did = data.did
listname = data.listname
musicname = data.musicname
if not xiaomusic.did_exist(did):
return {"ret": "Did not exist"}
log.info(f"playmusiclist {did} listname:{listname} musicname:{musicname}")
await xiaomusic.do_play_music_list(did, listname, musicname)
return {"ret": "OK"}
@app.post("/downloadjson")
async def downloadjson(data: UrlInfo, Verifcation=Depends(verification)):
log.info(data)
url = data.url
content = ""
try:
ret = "OK"
content = await downloadfile(url)
except Exception as e:
log.exception(f"Execption {e}")
ret = "Download JSON file failed."
return {
"ret": ret,
"content": content,
}
@app.get("/downloadlog")
def downloadlog(Verifcation=Depends(verification)):
file_path = xiaomusic.config.log_file
if os.path.exists(file_path):
# 创建一个临时文件来保存日志的快照
temp_file = tempfile.NamedTemporaryFile(delete=False)
try:
with open(file_path, "rb") as f:
shutil.copyfileobj(f, temp_file)
temp_file.close()
# 使用BackgroundTask在响应发送完毕后删除临时文件
def cleanup_temp_file(tmp_file_path):
os.remove(tmp_file_path)
background_task = BackgroundTask(cleanup_temp_file, temp_file.name)
return FileResponse(
temp_file.name,
media_type="text/plain",
filename="xiaomusic.txt",
background=background_task,
)
except Exception as e:
os.remove(temp_file.name)
raise HTTPException(
status_code=500, detail="Error capturing log file"
) from e
else:
return {"message": "File not found."}
@app.get("/playurl")
async def playurl(did: str, url: str, Verifcation=Depends(verification)):
if not xiaomusic.did_exist(did):
return {"ret": "Did not exist"}
decoded_url = urllib.parse.unquote(url)
log.info(f"playurl did: {did} url: {decoded_url}")
return await xiaomusic.play_url(did=did, arg1=decoded_url)
@app.post("/refreshmusictag")
async def refreshmusictag(Verifcation=Depends(verification)):
xiaomusic.refresh_music_tag()
return {
"ret": "OK",
}
@app.post("/debug_play_by_music_url")
async def debug_play_by_music_url(request: Request, Verifcation=Depends(verification)):
try:
data = await request.body()
data_dict = json.loads(data.decode("utf-8"))
log.info(f"data:{data_dict}")
return await xiaomusic.debug_play_by_music_url(arg1=data_dict)
except json.JSONDecodeError as err:
raise HTTPException(status_code=400, detail="Invalid JSON") from err
@app.get("/latestversion")
async def latest_version(Verifcation=Depends(verification)):
version = await get_latest_version("xiaomusic")
if version:
return {"ret": "OK", "version": version}
else:
return {"ret": "Fetch version failed"}
class DownloadPlayList(BaseModel):
dirname: str
url: str
# 下载歌单
@app.post("/downloadplaylist")
async def downloadplaylist(data: DownloadPlayList, Verifcation=Depends(verification)):
try:
download_proc = await download_playlist(config, data.url, data.dirname)
async def check_download_proc():
# 等待子进程完成
exit_code = await download_proc.wait()
log.info(f"Download completed with exit code {exit_code}")
dir_path = os.path.join(config.download_path, data.dirname)
log.debug(f"Download dir_path: {dir_path}")
# 可能只是部分失败,都需要整理下载目录
remove_common_prefix(dir_path)
asyncio.create_task(check_download_proc())
return {"ret": "OK"}
except Exception as e:
log.exception(f"Execption {e}")
return {"ret": "Failed download"}
class DownloadOneMusic(BaseModel):
name: str = ""
url: str
# 下载单首歌曲
@app.post("/downloadonemusic")
async def downloadonemusic(data: DownloadOneMusic, Verifcation=Depends(verification)):
try:
await download_one_music(config, data.url, data.name)
return {"ret": "OK"}
except Exception as e:
log.exception(f"Execption {e}")
return {"ret": "Failed download"}
# 上传 yt-dlp cookies
@app.post("/uploadytdlpcookie")
async def upload_yt_dlp_cookie(file: UploadFile = File(...)):
with open(config.yt_dlp_cookies_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {
"ret": "OK",
"filename": file.filename,
"file_location": config.yt_dlp_cookies_path,
}
class PlayListObj(BaseModel):
name: str = "" # 歌单名
# 新增歌单
@app.post("/playlistadd")
async def playlistadd(data: PlayListObj, Verifcation=Depends(verification)):
ret = xiaomusic.play_list_add(data.name)
if ret:
return {"ret": "OK"}
return {"ret": "Add failed, may be already exist."}
# 移除歌单
@app.post("/playlistdel")
async def playlistdel(data: PlayListObj, Verifcation=Depends(verification)):
ret = xiaomusic.play_list_del(data.name)
if ret:
return {"ret": "OK"}
return {"ret": "Del failed, may be not exist."}
class PlayListMusicObj(BaseModel):
name: str = "" # 歌单名
music_list: list[str] # 歌曲名列表
# 歌单新增歌曲
@app.post("/playlistaddmusic")
async def playlistaddmusic(data: PlayListMusicObj, Verifcation=Depends(verification)):
ret = xiaomusic.play_list_add_music(data.name, data.music_list)
if ret:
return {"ret": "OK"}
return {"ret": "Add failed, may be playlist not exist."}
# 歌单移除歌曲
@app.post("/playlistdelmusic")
async def playlistdelmusic(data: PlayListMusicObj, Verifcation=Depends(verification)):
ret = xiaomusic.play_list_del_music(data.name, data.music_list)
if ret:
return {"ret": "OK"}
return {"ret": "Del failed, may be playlist not exist."}
async def file_iterator(file_path, start, end):
async with aiofiles.open(file_path, mode="rb") as file:
await file.seek(start)
chunk_size = 1024
while start <= end:
read_size = min(chunk_size, end - start + 1)
data = await file.read(read_size)
if not data:
break
start += len(data)
yield data
def access_key_verification(file_path, key, code):
if config.disable_httpauth:
return True
log.debug(f"访问限制接收端[{file_path}, {key}, {code}]")
if key is not None:
current_key_bytes = key.encode("utf8")
correct_key_bytes = (
config.httpauth_username + config.httpauth_password
).encode("utf8")
is_correct_key = secrets.compare_digest(correct_key_bytes, current_key_bytes)
if is_correct_key:
return True
if code is not None:
current_code_bytes = code.encode("utf8")
correct_code_bytes = (
hashlib.sha256(
(
file_path + config.httpauth_username + config.httpauth_password
).encode("utf-8")
)
.hexdigest()
.encode("utf-8")
)
is_correct_code = secrets.compare_digest(correct_code_bytes, current_code_bytes)
if is_correct_code:
return True
return False
range_pattern = re.compile(r"bytes=(\d+)-(\d*)")
def safe_redirect(url):
url = try_add_access_control_param(config, url)
url = url.replace("\\", "")
if not urllib.parse.urlparse(url).netloc and not urllib.parse.urlparse(url).scheme:
log.debug(f"redirect to {url}")
return RedirectResponse(url=url)
return None
@app.get("/music/{file_path:path}")
async def music_file(request: Request, file_path: str, key: str = "", code: str = ""):
if not access_key_verification(f"/music/{file_path}", key, code):
raise HTTPException(status_code=404, detail="File not found")
absolute_path = os.path.abspath(config.music_path)
absolute_file_path = os.path.normpath(os.path.join(absolute_path, file_path))
if not absolute_file_path.startswith(absolute_path):
raise HTTPException(status_code=404, detail="File not found")
if not os.path.exists(absolute_file_path):
raise HTTPException(status_code=404, detail="File not found")
# 移除MP3 ID3 v2标签和填充
if config.remove_id3tag and is_mp3(file_path):
log.info(f"remove_id3tag:{config.remove_id3tag}, is_mp3:True ")
temp_mp3_file = remove_id3_tags(absolute_file_path, config)
if temp_mp3_file:
log.info(f"ID3 tag removed {absolute_file_path} to {temp_mp3_file}")
redirect = safe_redirect(f"/music/{temp_mp3_file}")
if redirect:
return redirect
else:
log.info(f"No ID3 tag remove needed: {absolute_file_path}")
if config.convert_to_mp3 and not is_mp3(file_path):
temp_mp3_file = convert_file_to_mp3(absolute_file_path, config)
if temp_mp3_file:
log.info(f"Converted file: {absolute_file_path} to {temp_mp3_file}")
redirect = safe_redirect(f"/music/{temp_mp3_file}")
if redirect:
return redirect
else:
log.warning(f"Failed to convert file to MP3 format: {absolute_file_path}")
return FileResponse(absolute_file_path)
@app.options("/music/{file_path:path}")
async def music_options():
headers = {
"Accept-Ranges": "bytes",
}
return Response(headers=headers)
@app.get("/picture/{file_path:path}")
async def get_picture(request: Request, file_path: str, key: str = "", code: str = ""):
if not access_key_verification(f"/picture/{file_path}", key, code):
raise HTTPException(status_code=404, detail="File not found")
absolute_path = os.path.abspath(config.picture_cache_path)
absolute_file_path = os.path.normpath(os.path.join(absolute_path, file_path))
if not absolute_file_path.startswith(absolute_path):
raise HTTPException(status_code=404, detail="File not found")
if not os.path.exists(absolute_file_path):
raise HTTPException(status_code=404, detail="File not found")
return FileResponse(absolute_file_path)
@app.get("/docs", include_in_schema=False)
async def get_swagger_documentation(Verifcation=Depends(verification)):
return get_swagger_ui_html(openapi_url="/openapi.json", title="docs")
@app.get("/redoc", include_in_schema=False)
async def get_redoc_documentation(Verifcation=Depends(verification)):
return get_redoc_html(openapi_url="/openapi.json", title="docs")
@app.get("/openapi.json", include_in_schema=False)
async def openapi(Verifcation=Depends(verification)):
return get_openapi(title=app.title, version=app.version, routes=app.routes)

69
xiaomusic/plugin.py Normal file
View File

@@ -0,0 +1,69 @@
import importlib
import inspect
import pkgutil
class PluginManager:
def __init__(self, xiaomusic, plugin_dir="plugins"):
self.xiaomusic = xiaomusic
self.log = xiaomusic.log
self._funcs = {}
self._load_plugins(plugin_dir)
def _load_plugins(self, plugin_dir):
# 假设 plugins 已经在搜索路径上
package_name = plugin_dir
package = importlib.import_module(package_name)
# 遍历 package 中所有模块并动态导入它们
for _, modname, _ in pkgutil.iter_modules(package.__path__, package_name + "."):
# 跳过__init__文件
if modname.endswith("__init__"):
continue
module = importlib.import_module(modname)
# 将 log 和 xiaomusic 注入模块的命名空间
module.log = self.log
module.xiaomusic = self.xiaomusic
# 动态获取模块中与文件名同名的函数
function_name = modname.split(".")[-1] # 从模块全名提取函数名
if hasattr(module, function_name):
self._funcs[function_name] = getattr(module, function_name)
else:
self.log.error(
f"No function named '{function_name}' found in module {modname}"
)
def get_func(self, plugin_name):
"""根据插件名获取插件函数"""
return self._funcs.get(plugin_name)
def get_local_namespace(self):
"""返回包含所有插件函数的字典,可以用作 exec 要执行的代码的命名空间"""
return self._funcs.copy()
async def execute_plugin(self, code):
"""
执行指定的插件代码。插件函数可以是同步或异步。
:param code: 需要执行的插件函数代码(例如 'plugin1("hello")'
"""
# 分解代码字符串以获取函数名
func_name = code.split("(")[0]
# 根据解析出的函数名从插件字典中获取函数
plugin_func = self.get_func(func_name)
if not plugin_func:
raise ValueError(f"No plugin function named '{func_name}' found.")
# 检查函数是否是异步函数
global_namespace = globals().copy()
local_namespace = self.get_local_namespace()
if inspect.iscoroutinefunction(plugin_func):
# 如果是异步函数,构建执行用的协程对象
coroutine = eval(code, global_namespace, local_namespace)
# 等待协程执行
await coroutine
else:
# 如果是普通函数,直接执行代码
eval(code, global_namespace, local_namespace)

View File

@@ -1,67 +0,0 @@
$(function(){
$container=$("#cmds");
append_op_button_name("下一首");
append_op_button_name("全部循环");
append_op_button_name("关机");
append_op_button_name("单曲循环");
append_op_button_name("播放歌曲");
append_op_button_name("随机播放");
$container.append($("<hr>"));
append_op_button_name("10分钟后关机");
append_op_button_name("30分钟后关机");
append_op_button_name("60分钟后关机");
// 拉取声音
$.get("/getvolume", function(data, status) {
console.log(data, status, data["volume"]);
$("#volume").val(data.volume);
});
function append_op_button_name(name) {
append_op_button(name, name);
}
function append_op_button(name, cmd) {
// 创建按钮
const $button = $("<button>");
$button.text(name);
$button.attr("type", "button");
// 设置按钮点击事件
$button.on("click", () => {
sendcmd(cmd);
});
// 添加按钮到容器
$container.append($button);
}
$("#play").on("click", () => {
var search_key = $("#music-name").val();
var filename=$("#music-filename").val();
let cmd = "播放歌曲"+search_key+"|"+filename;
sendcmd(cmd);
});
$("#volume").on('input', function () {
var value = $(this).val();
sendcmd("set_volume#"+value);
});
function sendcmd(cmd) {
$.ajax({
type: "POST",
url: "/cmd",
contentType: "application/json",
data: JSON.stringify({cmd: cmd}),
success: () => {
// 请求成功时执行的操作
},
error: () => {
// 请求失败时执行的操作
}
});
}
});

View File

@@ -0,0 +1,520 @@
$(function(){
$container=$("#cmds");
append_op_button_name("加入收藏");
append_op_button_name("取消收藏");
append_op_button_name("上一首");
append_op_button_name("关机");
append_op_button_name("下一首");
const PLAY_TYPE_ONE = 0; // 单曲循环
const PLAY_TYPE_ALL = 1; // 全部循环
const PLAY_TYPE_RND = 2; // 随机播放
const PLAY_TYPE_SIN = 3; // 单曲播放
const PLAY_TYPE_SEQ = 4; // 顺序播放
append_op_button("play_type_all", "全部循环", "全部循环");
append_op_button("play_type_one", "单曲循环", "单曲循环");
append_op_button("play_type_rnd", "随机播放", "随机播放");
append_op_button("play_type_sin", "单曲播放", "单曲播放");
append_op_button("play_type_seq", "顺序播放", "顺序播放");
append_op_button_name("刷新列表");
$container.append($("<hr>"));
append_op_button_name("10分钟后关机");
append_op_button_name("30分钟后关机");
append_op_button_name("60分钟后关机");
var offset = 0;
var duration = 0;
let no_warning = localStorage.getItem('no-warning');
// 拉取现有配置
$.get("/getsetting", function(data, status) {
console.log(data, status);
localStorage.setItem('mi_did', data.mi_did);
var did = localStorage.getItem('cur_did');
var dids = [];
if (data.mi_did != null) {
dids = data.mi_did.split(',');
}
console.log('cur_did', did);
console.log('dids', dids);
if ((dids.length > 0) && (did == null || did == "" || !dids.includes(did))) {
did = dids[0];
localStorage.setItem('cur_did', did);
}
window.did = did;
$.get(`/getvolume?did=${did}`, function(data, status) {
console.log(data, status, data["volume"]);
$("#volume").val(data.volume);
});
refresh_music_list();
$("#did").empty();
var dids = data.mi_did.split(',');
$.each(dids, function(index, value) {
var cur_device = Object.values(data.devices).find(device => device.did === value);
if (cur_device) {
var option = $('<option></option>')
.val(value)
.text(cur_device.name)
.prop('selected', value === did);
$("#did").append(option);
if (value === did) {
if (cur_device.play_type == PLAY_TYPE_ALL) {
$("#play_type_all").css('background-color', '#b1a8f3');
$("#play_type_all").text('✔️ 全部循环');
} else if (cur_device.play_type == PLAY_TYPE_ONE) {
$("#play_type_one").css('background-color', '#b1a8f3');
$("#play_type_one").text('✔️ 单曲循环');
} else if (cur_device.play_type == PLAY_TYPE_RND) {
$("#play_type_rnd").css('background-color', '#b1a8f3');
$("#play_type_rnd").text('✔️ 随机播放');
} else if (cur_device.play_type == PLAY_TYPE_SIN) {
$("#play_type_sin").css('background-color', '#b1a8f3');
$("#play_type_sin").text('✔️ 单曲播放');
} else if (cur_device.play_type == PLAY_TYPE_SEQ) {
$("#play_type_seq").css('background-color', '#b1a8f3');
$("#play_type_seq").text('✔️ 顺序播放');
}
}
}
});
console.log('cur_did', did);
$('#did').change(function() {
did = $(this).val();
localStorage.setItem('cur_did', did);
window.did = did;
console.log('cur_did', did);
location.reload();
})
});
function compareVersion(version1, version2) {
const v1 = version1.split('.').map(Number);
const v2 = version2.split('.').map(Number);
const len = Math.max(v1.length, v2.length);
for (let i = 0; i < len; i++) {
const num1 = v1[i] || 0;
const num2 = v2[i] || 0;
if (num1 > num2) return 1;
if (num1 < num2) return -1;
}
return 0;
}
// 拉取版本
$.get("/getversion", function(data, status) {
console.log(data, status, data["version"]);
$("#version").text(`${data.version}`);
$.get("/latestversion", function(ret, status) {
console.log(ret, status);
if (ret.ret == "OK") {
const result = compareVersion(ret.version, data.version);
if (result > 0) {
console.log(`${ret.version} is greater than ${data.version}`);
$("#versionnew").text("🆕");
}
}
});
});
function _refresh_music_list(callback) {
$('#music_list').empty();
$.get("/musiclist", function(data, status) {
console.log(data, status);
$.each(data, function(key, value) {
let cnt = value.length;
$('#music_list').append($('<option></option>').val(key).text(`${key} (${cnt})`));
});
$('#music_list').change(function() {
const selectedValue = $(this).val();
localStorage.setItem('cur_playlist', selectedValue);
$('#music_name').empty();
$.each(data[selectedValue], function(index, item) {
$('#music_name').append($('<option></option>').val(item).text(item));
});
});
$('#music_list').trigger('change');
// 获取当前播放列表
$.get(`/curplaylist?did=${did}`, function(playlist, status) {
if (playlist != "") {
$('#music_list').val(playlist);
$('#music_list').trigger('change');
} else {
// 使用本地记录的
playlist = localStorage.getItem('cur_playlist');
if (data.includes(playlist)) {
$('#music_list').val(playlist);
$('#music_list').trigger('change');
}
}
})
callback();
})
}
// 拉取播放列表
function refresh_music_list() {
// 刷新列表时清空并临时禁用搜索框
const searchInput = document.getElementById('search');
const oriPlaceHolder = searchInput.placeholder
const oriValue = searchInput.value
const inputEvent = new Event('input', { bubbles: true });
searchInput.value = '';
// 分发事件,让其他控件改变状态
searchInput.dispatchEvent(inputEvent);
searchInput.disabled = true;
searchInput.placeholder = '请等待...';
_refresh_music_list(() => {
// 刷新完成再启用
searchInput.disabled = false;
searchInput.value = oriValue
searchInput.dispatchEvent(inputEvent);
searchInput.placeholder = oriPlaceHolder;
// 每3秒获取下正在播放的音乐
get_playing_music();
setInterval(() => {
get_playing_music();
}, 3000);
});
}
function do_play_music_list(listname, musicname) {
$.ajax({
type: "POST",
url: "/playmusiclist",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({did: did, listname: listname, musicname: musicname}),
success: () => {
console.log("do_play_music_list succ", listname, musicname);
},
error: () => {
console.log("do_play_music_list failed", listname, musicname);
}
});
}
$("#play_music_list").on("click", () => {
var music_list = $("#music_list").val();
var music_name = $("#music_name").val();
if (no_warning) {
do_play_music_list(music_list, music_name);
return;
}
$.get(`/musicinfo?name=${music_name}`, function(data, status) {
console.log(data);
if (data.ret == "OK") {
validHost(data.url) && do_play_music_list(music_list, music_name);
}
});
});
$("#web_play").on("click", () => {
const music_name = $("#music_name").val();
$.get(`/musicinfo?name=${music_name}`, function(data, status) {
console.log(data);
if (data.ret == "OK") {
validHost(data.url) && $('audio').attr('src',data.url);
}
});
});
$("#del_music").on("click", () => {
var del_music_name = $("#music_name").val();
if (confirm(`确定删除歌曲 ${del_music_name} 吗?`)) {
console.log(`删除歌曲 ${del_music_name}`);
$.ajax({
type: 'POST',
url: '/delmusic',
data: JSON.stringify({"name": del_music_name}),
contentType: "application/json; charset=utf-8",
success: () => {
alert(`删除 ${del_music_name} 成功`);
refresh_music_list();
},
error: () => {
alert(`删除 ${del_music_name} 失败`);
}
});
}
});
$("#playurl").on("click", () => {
var url = $("#music-url").val();
const encoded_url = encodeURIComponent(url);
$.get(`/playurl?url=${encoded_url}&did=${did}`, function(data, status) {
console.log(data);
});
});
function append_op_button_name(name) {
append_op_button(null, name, name);
}
function append_op_button(id, name, cmd) {
// 创建按钮
const $button = $("<button>");
$button.text(name);
$button.attr("type", "button");
if (id !== null) {
$button.attr("id", id);
}
// 设置按钮点击事件
$button.on("click", () => {
sendcmd(cmd);
});
// 添加按钮到容器
$container.append($button);
}
function do_play_music(musicname, searchkey) {
$.ajax({
type: "POST",
url: "/playmusic",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({did: did, musicname: musicname, searchkey: searchkey}),
success: () => {
console.log("do_play_music succ", musicname, searchkey);
},
error: () => {
console.log("do_play_music failed", musicname, searchkey);
}
});
}
$("#play").on("click", () => {
var search_key = $("#music-name").val();
if (search_key == null) {
search_key = "";
}
var filename = $("#music-filename").val();
if (filename == null || filename == "") {
filename = search_key;
}
do_play_music(filename, search_key);
});
$("#volume").on('change', function () {
var value = $(this).val();
$.ajax({
type: "POST",
url: "/setvolume",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({did: did, volume: value}),
success: () => {
},
error: () => {
}
});
});
function check_status_refresh_music_list(retries) {
$.get("/cmdstatus", function(data) {
if (data.status === "finish") {
refresh_music_list();
} else if (retries > 0) {
setTimeout(function() {
check_status_refresh_music_list(retries - 1);
}, 1000); // 等待1秒后重试
}
});
}
function sendcmd(cmd) {
$.ajax({
type: "POST",
url: "/cmd",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({did: did, cmd: cmd}),
success: () => {
if (cmd == "刷新列表") {
check_status_refresh_music_list(3); // 最多重试3次
}
if (["全部循环", "单曲循环", "随机播放", "单曲播放", "顺序播放"].includes(cmd)) {
location.reload();
}
},
error: () => {
// 请求失败时执行的操作
}
});
}
// 监听输入框的输入事件
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), delay);
};
}
const searchInput = document.getElementById('search');
const musicSelect = document.getElementById('music-name');
const musicSelectLabel = document.getElementById('music-name-label');
searchInput.addEventListener('input', debounce(function() {
const query = searchInput.value.trim();
if (query.length === 0) {
musicSelect.innerHTML = '';
musicSelect.style.display = 'none'
musicSelectLabel.style.display = 'none'
return;
}
musicSelect.style.display = 'block'
musicSelectLabel.style.display = 'block'
fetch(`/searchmusic?name=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
musicSelect.innerHTML = ''; // 清空现有选项
// 找到的优先显示
if (data.length > 0) {
data.forEach(song => {
const option = document.createElement('option');
option.value = song
option.textContent = song
musicSelect.appendChild(option);
});
}
// 添加用户输入作为一个选项
const userOption = document.createElement('option');
userOption.value = query;
userOption.textContent = `使用关键词播放: ${query}`;
musicSelect.appendChild(userOption);
// 提示没找到
if (data.length === 0) {
const option = document.createElement('option');
option.textContent = '没有匹配的结果';
option.disabled = true;
musicSelect.appendChild(option);
}
})
.catch(error => {
console.error('Error fetching data:', error);
});
}, 500));
// 动态显示保存文件名输入框
const musicNameSelect = document.getElementById('music-name');
const musicFilenameInput = document.getElementById('music-filename');
function updateInputVisibility() {
const selectedOption = musicNameSelect.options[musicNameSelect.selectedIndex];
var startsWithKeyword;
if (musicNameSelect.options.length === 0) {
startsWithKeyword = false;
} else {
startsWithKeyword = selectedOption.text.startsWith('使用关键词联网搜索:');
}
if (startsWithKeyword) {
musicFilenameInput.style.display = 'block';
musicFilenameInput.placeholder = '请输入保存为的文件名称(默认:' + selectedOption.value + ')';
} else {
musicFilenameInput.style.display = 'none';
}
}
// 观察元素修改
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
updateInputVisibility()
}
}
});
observer.observe(musicNameSelect, { childList: true });
// 监听用户输入
musicNameSelect.addEventListener('change', updateInputVisibility);
function get_playing_music() {
$.get(`/playingmusic?did=${did}`, function(data, status) {
console.log(data);
if (data.ret == "OK") {
if (data.is_playing) {
$("#playering-music").text(`【播放中】 ${data.cur_music}`);
} else {
$("#playering-music").text(`【空闲中】 ${data.cur_music}`);
}
offset = data.offset;
duration = data.duration;
}
});
}
setInterval(()=>{
if (duration > 0) {
offset++;
$("#progress").val(offset / duration * 100);
$("#play-time").text(`${formatTime(offset)}/${formatTime(duration)}`)
}else{
$("#play-time").text(`${formatTime(0)}/${formatTime(0)}`)
}
},1000)
function formatTime(seconds) {
var minutes = Math.floor(seconds / 60);
var remainingSeconds =Math.floor(seconds % 60);
return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
}
$("audio").on("error", (e) => {
//如果audio标签的src为空则不做任何操作兼容安卓端的低版本webview
if ($("audio").attr("src") === "") {
return;
}
console.log('%c网页播放出现错误: ', 'color: #007acc;', e.currentTarget.error.code,e.currentTarget.error.message);
alert(e.currentTarget.error.code==4 ? "无法打开媒体文件XIAOMUSIC_HOSTNAME或端口地址错误请重新设置" : "在线播放失败,请截图反馈: "+e.currentTarget.error.message);
});
function validHost(url) {
//如果 localStorage 中有 no-warning 则直接返回true
if (no_warning) {
return true;
}
const local = location.host;
const host = new URL(url).host;
// 如果当前页面的Host与设置中的XIAOMUSIC_HOSTNAME、PORT一致, 不再提醒
if (local === host) {
localStorage.setItem('no-warning', 'true');
// 设置全局变量
no_warning = true;
return true;
}
// 如果当前页面的Host与设置中的XIAOMUSIC_HOSTNAME、PORT不一致
const validHost = document.getElementById('valid-host');
let validFlag = false;
$('#local-host').text(local);
$('#setting-host').text(host);
validHost.showModal();
//监听validHost的close事件
function _handleClose() {
console.log('%c提醒HOST不一致弹窗,用户已选择: ', 'color: #007acc;', validHost.returnValue);
if (validHost.returnValue == "no-warning") {
localStorage.setItem('no-warning', 'true');
no_warning = true;
validFlag = true;
}
validHost.removeEventListener('close', _handleClose)
}
validHost.addEventListener('close', _handleClose)
return validFlag;
}
});

View File

@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width">
<title>Debug For XiaoMusic</title>
<link rel="stylesheet" type="text/css" href="./style.css?version=1733278946">
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script src="./jquery-3.7.1.min.js?version=1733278946"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', 'G-Z09NC1K7ZW');
</script>
<script>
var vConsole = new window.VConsole();
function postJSON() {
var data = $('#post-input').val();
$.ajax({
type: 'POST',
url: '/debug_play_by_music_url',
data: data,
contentType: "application/json; charset=utf-8",
success: (err) => {
console.log("succ", res);
},
error: (res) => {
console.log("error", res);
}
});
}
function sendDebugCmd() {
var cmd = $("#cmd").val();
var did = localStorage.getItem('cur_did');
$.ajax({
type: "POST",
url: "/cmd",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({did: did, cmd: cmd}),
success: () => {
},
error: () => {
// 请求失败时执行的操作
}
});
}
</script>
</head>
<body>
<h1>Debug For XiaoMusic</h1>
<textarea id="post-input" rows="10" cols="50" placeholder="粘贴json数据..."></textarea><br>
<button onclick="postJSON()">提交</button><br>
<hr>
<input id="cmd" type="text"></input>
<button onclick="sendDebugCmd()">测试自定义口令</button><br>
</body>
<footer>
<p>Powered by <a href="https://github.com/hanxi/xiaomusic" target="_blank">xiaomusic</a></p>
</footer>
</html>

View File

@@ -0,0 +1,113 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<title>歌曲下载工具</title>
<link rel="stylesheet" type="text/css" href="./style.css?version=1733278946">
<script src="./jquery-3.7.1.min.js?version=1733278946"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', 'G-Z09NC1K7ZW');
</script>
</head>
<body>
<h1>歌曲下载工具</h1>
<div class="rows">
<!-- 歌单的输入 -->
<label for="playlistUrl">输入歌单 URL:</label>
<input type="text" id="playlistUrl" value="https://m.bilibili.com/video/BV1WUsDezE88">
<label for="dirname">输入歌单名字:</label>
<input type="text" id="dirname" placeholder="流行歌曲">
<button id="downloadPlaylistBtn">下载歌单</button>
</div>
<hr>
<div class="rows">
<!-- 单曲的输入 -->
<label for="songUrl">输入歌曲 URL:</label>
<input type="text" id="songUrl" value="https://m.bilibili.com/video/BV1qD4y1U7fs">
<label for="songName">输入歌曲名字:</label>
<input type="text" id="songName" placeholder="歌曲名">
<button id="downloadSongBtn">下载单曲</button>
</div>
<script>
// 下载歌单
$('#downloadPlaylistBtn').click(function() {
var playlistUrl = $('#playlistUrl').val();
var dirname = $('#dirname').val();
if (!playlistUrl || !dirname) {
alert('请填写完整的歌单 URL 和歌单名字');
return;
}
var data = {
dirname: dirname,
url: playlistUrl
};
$.ajax({
type: "POST",
url: "/downloadplaylist",
contentType: "application/json",
data: JSON.stringify(data),
success: (msg) => {
alert('歌单下载请求已发送!');
console.log(response);
},
error: (msg) => {
alert('歌单下载请求失败,请重试。');
}
});
});
// 下载单曲
$('#downloadSongBtn').click(function() {
var songName = $('#songName').val();
var songUrl = $('#songUrl').val();
if (!songUrl || !songName) {
alert('请填写完整的歌曲 URL 和歌曲名字');
return;
}
var data = {
name: songName,
url: songUrl
};
$.ajax({
type: "POST",
url: "/downloadonemusic",
contentType: "application/json",
data: JSON.stringify(data),
success: (msg) => {
alert('单曲下载请求已发送!');
console.log(response);
},
error: (msg) => {
alert('单曲下载请求失败,请重试。');
}
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width">
<title>小爱音箱操控面板</title>
<script src="./jquery-3.7.1.min.js?version=1733278946"></script>
<script src="./app.js?version=1733278946"></script>
<link rel="stylesheet" type="text/css" href="./style.css?version=1733278946">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', 'G-Z09NC1K7ZW');
</script>
<!--
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
var vConsole = new window.VConsole();
</script>
-->
</head>
<body>
<h2>小爱音箱操控面板
(<a id="version" href="https://github.com/hanxi/xiaomusic/blob/main/CHANGELOG.md">版本未知</a>)
<span id="versionnew" class="blink"></span>
</h2>
<hr>
<div class="rows">
<select id="did">
</select>
</div>
<div id="cmds">
<a class="button" href="./setting.html">设置</a>
</div>
<hr>
<div style="margin: 20px;">
<div style="display: flex; align-items: center;">
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="#8e43e7" style="height: 48px; width: 48px;"><path d="M550.826667 154.666667a47.786667 47.786667 0 0 0-19.84 4.48L298.666667 298.666667H186.453333A80 80 0 0 0 106.666667 378.453333v267.093334A80 80 0 0 0 186.453333 725.333333H298.666667l232.32 139.52a47.786667 47.786667 0 0 0 19.84 4.48A46.506667 46.506667 0 0 0 597.333333 822.826667V201.173333a46.506667 46.506667 0 0 0-46.506666-46.506666zM554.666667 822.826667c0 3.413333-3.84 3.84-3.84 3.84L320 688.853333l-9.6-6.186666H186.453333A37.12 37.12 0 0 1 149.333333 645.546667V378.453333A37.12 37.12 0 0 1 186.453333 341.333333h123.946667l10.24-6.186666 229.546667-137.6s3.84 0 3.84 3.84zM667.52 346.026667a21.333333 21.333333 0 0 0 0 30.293333 192 192 0 0 1 0 271.36 21.333333 21.333333 0 0 0 0 30.293333 21.333333 21.333333 0 0 0 30.293333 0 234.666667 234.666667 0 0 0 0-331.946666 21.333333 21.333333 0 0 0-30.293333 0z"></path><path d="M804.48 219.52a21.333333 21.333333 0 0 0-30.293333 30.293333 370.986667 370.986667 0 0 1 0 524.373334 21.333333 21.333333 0 0 0 0 30.293333 21.333333 21.333333 0 0 0 30.293333 0 414.08 414.08 0 0 0 0-584.96z"></path></svg>
<input id="volume" type="range"></input>
</div>
</div>
<hr>
<div class="rows">
<label for="search">搜索歌曲:</label>
<input type="text" id="search" placeholder="请输入搜索关键词(如:MV高清版 周杰伦 七里香)">
<label for="music-name" id="music-name-label" style="display: none;">确认选择:</label>
<select id="music-name" style="display: none;">
<!-- 动态生成选项 -->
</select>
<input id="music-filename" type="text" placeholder="请输入保存为的文件名称(如:周杰伦七里香)" style="display: none;"></input>
<div style="display: flex; align-items: center">
<progress id="progress" value="0" max="100" style="width: 270px"></progress>
<div id="play-time" style="margin-left: 10px">00:00/00:00</div>
</div>
<div>
<button id="play">播放</button>
<div id="playering-music" class="text"></div>
</div>
</div>
<hr>
<div class="rows">
<label for="music_list">播放列表:</label>
<select id="music_list"></select>
<label for="music_name">歌曲:</label>
<select id="music_name"></select>
<div>
<button id="play_music_list">播放选中歌曲</button>
<button id="del_music">删除选中歌曲</button>
<button id="web_play">网页播放</button>
</div>
<div class="play_pannel">
<audio autoplay controls src=""></audio>
</div>
</div>
<hr>
<div class="rows">
<input id="music-url" type="text" value="https://lhttp.qtfm.cn/live/4915/64k.mp3"></input>
<button id="playurl">播放链接</button>
</div>
<footer>
<p>Powered by <a href="https://github.com/hanxi/xiaomusic" target="_blank">xiaomusic</a></p>
</footer>
<dialog id="valid-host">
<form method="dialog">
<p>当前页面的HOST与设置中的HOST不一致请检查是否设置错误</p>
<p>当前HOST: <span id="local-host"></span></p>
<p>设置中的HOST: <span id="setting-host"></span></p>
<div class="btn-list">
<a href="./setting.html" target="_blank">立即修改</a>
<button value="no-warning" type="submit">继续并不再显示</button>
<button value="cancle" type="submit">取消</button>
</div>
</form>
</dialog>
</body>
</html>

View File

@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width">
<title>M3U to JSON Converter</title>
<link rel="stylesheet" type="text/css" href="./style.css?version=1733278946">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', 'G-Z09NC1K7ZW');
</script>
<!--
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
// VConsole 默认会挂载到 `window.VConsole` 上
var vConsole = new window.VConsole();
</script>
-->
<script>
function handleFileSelect(evt) {
var file = evt.target.files[0];
if (file) {
var reader = new FileReader();
reader.onload = function(e) {
document.getElementById('m3u-input').value = e.target.result;
};
reader.readAsText(file);
} else {
alert('无法加载文件');
}
}
function convertToJSON() {
var m3uContent = document.getElementById('m3u-input').value;
var lines = m3uContent.split('\n');
console.log(lines);
var musicsArray = [];
var currentName = '';
lines.forEach(function(line) {
line = line.trim();
if (line.startsWith('#EXTINF:')) {
currentName = line.replace(/.*,/g, '');
} else if (line.startsWith('http') && currentName !== '') {
musicsArray.push({"name": currentName, "type": "radio", "url": line});
currentName = ''; // Reset the name for the next entry
}
});
var output = [{
"name": "m3u电台",
"musics": musicsArray
}];
document.getElementById('json-output').value = JSON.stringify(output, null, 2);
}
</script>
</head>
<body>
<h1>M3U to JSON Converter</h1>
<input type="file" id="file-input" accept=".m3u" onchange="handleFileSelect(event)"/><br>
<textarea id="m3u-input" rows="10" cols="50" placeholder="粘贴m3u内容或上传文件..."></textarea><br>
<button onclick="convertToJSON()">转换</button><br>
<textarea id="json-output" rows="10" cols="50" placeholder="转换后的JSON..."></textarea>
</body>
<footer>
<p>Powered by <a href="https://github.com/hanxi/xiaomusic" target="_blank">xiaomusic</a></p>
</footer>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,256 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width">
<title>小爱音箱操控面板</title>
<script src="./jquery-3.7.1.min.js?version=1733278946"></script>
<script src="./setting.js?version=1733278946"></script>
<link rel="stylesheet" type="text/css" href="./style.css?version=1733278946">
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', 'G-Z09NC1K7ZW');
</script>
<!--
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
var vConsole = new window.VConsole();
</script>
-->
</head>
<body>
<h2>小爱音箱设置面板
(<a id="version" href="https://github.com/hanxi/xiaomusic/blob/main/CHANGELOG.md">
版本未知
</a>)
</h2>
<hr>
<div class="rows">
<label for="mi_did">*勾选设备(至少勾选1个):</label>
<div id="mi_did">
</div>
</div>
<br>
<div id="setting">
<div class="rows">
<label for="account">*小米账号:</label>
<input id="account" type="text" placeholder="填写小米登录账号" />
<label for="password">*小米密码:</label>
<input id="password" type="password" placeholder="填写小米登录密码" />
<label for="hostname">*XIAOMUSIC_HOSTNAME(IP或域名):</label>
<input id="hostname" type="text" />
</div>
<hr>
<div class="rows">
<label for="verbose">是否开启调试日志:</label>
<select id="verbose">
<option value="true" selected>true</option>
<option value="false">false</option>
</select>
<label for="port">监听端口(修改后需要重启):</label>
<input id="port" type="number" value="8090" />
<label for="public_port">外网访问端口(0表示跟监听端口一致):</label>
<input id="public_port" type="number" value="0" />
<label for="group_list">设备分组配置:<a href="https://github.com/hanxi/xiaomusic/issues/65#issuecomment-2215736529" target="_blank">文档</a></label>
<input id="group_list" type="text" placeholder="did1:组名1,did2:组名1,did3:组名2" />
<label for="music_path">音乐目录:</label>
<input id="music_path" type="text" value="music" />
<label for="download_path">音乐下载目录(必须是music的子目录):</label>
<input id="download_path" type="text" value='music/download' />
<label for="conf_path">配置文件目录:</label>
<input id="conf_path" type="text" />
<label for="cache_dir">缓存文件目录:</label>
<input id="cache_dir" type="text" />
<label for="temp_path">临时文件目录:</label>
<input id="temp_path" type="text" value="music/tmp" />
<label for="ffmpeg_location">ffmpeg路径:</label>
<input id="ffmpeg_location" type="text" value="./ffmpeg/bin" />
<label for="log_file">日志路径:</label>
<input id="log_file" type="text" value="xiaomusic.log.txt" />
<label for="active_cmd">允许唤醒的命令:</label>
<input id="active_cmd" type="text" value="play,random_play,playlocal,play_music_list,stop" />
<label for="exclude_dirs">忽略目录(逗号分割):</label>
<input id="exclude_dirs" type="text" value="@eaDir,tmp" />
<label for="music_path_depth">目录深度:</label>
<input id="music_path_depth" type="number" value="10" />
<label for="search_prefix">XIAOMUSIC_SEARCH(歌曲下载方式):</label>
<select id="search_prefix">
<option value="bilisearch:">bilisearch:</option>
<option value="ytsearch:">ytsearch:</option>
</select>
<label for="proxy">XIAOMUSIC_PROXY(ytsearch需要):</label>
<input id="proxy" type="text" placeholder="http://192.168.2.5:8080" />
<label for="remove_id3tag">去除MP3 ID3v2和填充:</label>
<select id="remove_id3tag">
<option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="convert_to_mp3">转换为MP3:</label>
<select id="convert_to_mp3">
<option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="miio_tts_command">MiIO tts 指令(解决部分型号没有提示音的问题):</label>
<input id="miio_tts_command" type="text" placeholder="如5 或者 5-3" />
<label for="disable_httpauth">关闭控制台密码验证:</label>
<select id="disable_httpauth">
<option value="true" selected>true</option>
<option value="false">false</option>
</select>
<label for="httpauth_username">控制台账户:</label>
<input id="httpauth_username" type="text" value="" />
<label for="httpauth_password">控制台密码:</label>
<input id="httpauth_password" type="password" value="" />
<label for="disable_download">关闭下载功能:</label>
<select id="disable_download">
<option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="use_music_audio_id">触屏版显示歌曲ID:</label>
<input id="use_music_audio_id" type="text" value="1582971365183456177" />
<label for="use_music_id">触屏版显示歌曲分段ID:</label>
<input id="use_music_id" type="text" value="355454500" />
<label for="fuzzy_match_cutoff">模糊匹配阈值(0.1~0.9):</label>
<input id="fuzzy_match_cutoff" type="number" value="0.6" />
<label for="enable_fuzzy_match">开启模糊搜索:</label>
<select id="enable_fuzzy_match">
<option value="true" selected>true</option>
<option value="false">false</option>
</select>
<label for="use_music_api">型号兼容模式:</label>
<select id="use_music_api">
<option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="continue_play">启用继续播放(可能导致兼容性问题):</label>
<select id="continue_play">
<option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="pull_ask_sec">获取对话记录间隔(秒):</label>
<input id="pull_ask_sec" type="number" value="1" />
<label for="delay_sec">下一首歌延迟播放秒数:</label>
<input id="delay_sec" type="number" value="3" />
<label for="stop_tts_msg">停止提示音:</label>
<input id="stop_tts_msg" type="text" value="收到,再见" />
<label for="play_type_one_tts_msg">单曲循环提示音:</label>
<input id="play_type_one_tts_msg" type="text" value="已经设置为单曲循环" />
<label for="play_type_all_tts_msg">全部循环提示音:</label>
<input id="play_type_all_tts_msg" type="text" value="已经设置为全部循环" />
<label for="play_type_rnd_tts_msg">随机播放提示音:</label>
<input id="play_type_rnd_tts_msg" type="text" value="已经设置为随机播放" />
<label for="play_type_sin_tts_msg">单曲播放提示音:</label>
<input id="play_type_sin_tts_msg" type="text" value="已经设置为单曲播放" />
<label for="play_type_seq_tts_msg">顺序播放提示音:</label>
<input id="play_type_seq_tts_msg" type="text" value="已经设置为顺序播放" />
<label for="keywords_playlocal">播放本地歌曲口令:</label>
<input id="keywords_playlocal" type="text" value="播放本地歌曲,本地播放歌曲" />
<label for="keywords_play">播放歌曲口令:</label>
<input id="keywords_play" type="text" value="播放歌曲,放歌曲" />
<label for="keywords_playlist">播放列表口令:</label>
<input id="keywords_playlist" type="text" value="播放列表,播放歌单" />
<label for="keywords_stop">停止口令:</label>
<input id="keywords_stop" type="text" value="关机,暂停,停止,停止播放" />
<label for="enable_yt_dlp_cookies">启用yt-dlp-cookies(需要先上传yt-dlp-cookies.txt文件):</label>
<select id="enable_yt_dlp_cookies">
<option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="get_ask_by_mina">特殊型号获取对话记录:</label>
<select id="get_ask_by_mina">
<option value="true">true</option>
<option value="false" selected>false</option>
</select>
<label for="recently_added_playlist_len">最近新增的歌曲数量:</label>
<input id="recently_added_playlist_len" type="number" value="50" />
<label for="music_list_url">歌单地址:</label>
<input id="music_list_url" type="text" value="https://gist.githubusercontent.com/hanxi/dda82d964a28f8110f8fba81c3ff8314/raw/example.json" />
<label for="music_list_json">歌单内容:<a href="https://github.com/hanxi/xiaomusic/issues/78" target="_blank">格式文档</a></label>
<textarea id="music_list_json" type="text"></textarea>
<label for="crontab_json">定时任务:<a href="https://github.com/hanxi/xiaomusic/issues/182" target="_blank">格式文档</a></label>
<textarea id="crontab_json" type="text"></textarea>
</div>
</div>
<hr>
<div class="rows">
<label for="yt_dlp_cookies_file">上传yt_dlp_cookies.txt文件:<a href="https://github.com/hanxi/xiaomusic/issues/210" target="_blank">文档</a></label>
<input id="yt_dlp_cookies_file" name="file" type="file">
<button id="upload_yt_dlp_cookie">上传</button>
</div>
<hr>
<button onclick="location.href='/static/default/index.html';">返回首页</button>
<button id="get_music_list">获取歌单</button>
<button class="save-button">保存</button>
<hr>
<button id="refresh_music_tag">刷新tag</button>
<button id="clear_cache">清空缓存</button>
<a class="button" href="/downloadlog" download="xiaomusic.txt">下载日志文件</a>
<hr>
<button onclick="location.href='/docs';">查看接口文档</button>
<a class="button" href="./m3u.html" target="_blank">m3u文件转换</a>
<a class="button" href="./downloadtool.html" target="_blank">歌曲下载工具</a>
<hr>
<a class="button" href="./debug.html" target="_blank">调试工具</a>
<a class="button" href="https://afdian.com/a/imhanxi" target="_blank">💰 爱发电</a>
<a class="button" href="https://github.com/hanxi/xiaomusic" target="_blank">点个 Star ⭐</a>
<div class="rows">
<img class="qrcode" src="./qrcode.png" alt="请涵曦喝奶茶🧋">
</div>
<footer>
<p>Powered by <a href="https://github.com/hanxi/xiaomusic" target="_blank">xiaomusic</a></p>
</footer>
</body>
</html>

View File

@@ -0,0 +1,194 @@
$(function(){
// 拉取版本
$.get("/getversion", function(data, status) {
console.log(data, status, data["version"]);
$("#version").text(`${data.version}`);
});
// 遍历所有的select元素默认选中只有1个选项的
const autoSelectOne = () => {
$('select').each(function() {
// 如果select元素仅有一个option子元素
if ($(this).children('option').length === 1) {
// 选中这个option
$(this).find('option').prop('selected', true);
}
});
};
function updateCheckbox(selector, mi_did, device_list,accountPassValid) {
// 清除现有的内容
$(selector).empty();
// 将 mi_did 字符串通过逗号分割转换为数组,以便于判断默认选中项
var selected_dids = mi_did.split(',');
//如果device_list为空则可能是未设置小米账号密码或者已设置密码但是没有过小米验证此处需要提示用户
if (device_list.length == 0) {
const loginTips = accountPassValid ? `<div class="login-tips">未发现可用的小爱设备,请检查账号密码是否输错,并关闭加速代理或在<a href="https://www.mi.com">小米官网</a>登陆过人脸或滑块验证。如仍未解决。请根据<a href="https://github.com/hanxi/xiaomusic/issues/99">FAQ</a>的内容解决问题。</div>` : `<div class="login-tips">未发现可用的小爱设备,请先在下面的输入框中设置小米的<b>账号、密码</b></div>`;
$(selector).append(loginTips);
return;
}
$.each(device_list, function(index, device) {
var did = device.miotDID;
var hardware = device.hardware;
var name = device.name;
// 创建复选框元素
var checkbox = $('<input>', {
type: 'checkbox',
id: did,
value: `${did}`,
class: 'custom-checkbox', // 添加样式类
// 如果mi_did中包含了该did则默认选中
checked: selected_dids.indexOf(did) !== -1
});
// 创建标签元素
var label = $('<label>', {
for: did,
class: 'checkbox-label', // 添加样式类
text: `${hardware} ${did}${name}` // 设定标签内容
});
// 将复选框和标签添加到目标选择器元素中
$(selector).append(checkbox).append(label);
});
}
function getSelectedDids(containerSelector) {
var selectedDids = [];
// 仅选择给定容器中选中的复选框
$(containerSelector + ' .custom-checkbox:checked').each(function() {
var did = this.value;
selectedDids.push(did);
});
return selectedDids.join(',');
}
// 拉取现有配置
$.get("/getsetting?need_device_list=true", function(data, status) {
console.log(data, status);
const accountPassValid = data.account && data.password;
updateCheckbox("#mi_did", data.mi_did, data.device_list, accountPassValid);
// 初始化显示
for (const key in data) {
const $element = $("#" + key);
if ($element.length) {
if (data[key] === true) {
$element.val('true');
} else if (data[key] === false) {
$element.val('false');
} else {
$element.val(data[key]);
}
}
}
autoSelectOne();
});
$(".save-button").on("click", () => {
var setting = $('#setting');
var inputs = setting.find('input, select, textarea');
var data = {};
inputs.each(function() {
var id = this.id;
if (id) {
data[id] = $(this).val();
}
});
var did_list = getSelectedDids("#mi_did");
data["mi_did"] = did_list;
console.log(data)
$.ajax({
type: "POST",
url: "/savesetting",
contentType: "application/json",
data: JSON.stringify(data),
success: (msg) => {
alert(msg);
location.reload();
},
error: (msg) => {
alert(msg);
}
});
});
$("#get_music_list").on("click", () => {
var music_list_url = $("#music_list_url").val();
console.log("music_list_url", music_list_url);
var data = {
url: music_list_url,
};
$.ajax({
type: "POST",
url: "/downloadjson",
contentType: "application/json",
data: JSON.stringify(data),
success: (res) => {
if (res.ret == "OK") {
$("#music_list_json").val(res.content);
} else {
console.log(res);
alert(res.ret);
}
},
error: (res) => {
console.log(res);
alert(res);
}
});
});
$("#refresh_music_tag").on("click", () => {
$.ajax({
type: "POST",
url: "/refreshmusictag",
contentType: "application/json",
success: (res) => {
console.log(res);
alert(res.ret);
},
error: (res) => {
console.log(res);
alert(res);
}
});
});
$("#upload_yt_dlp_cookie").on("click", () => {
var fileInput = document.getElementById('yt_dlp_cookies_file');
var file = fileInput.files[0]; // 获取文件对象
if (file) {
var formData = new FormData();
formData.append("file", file);
$.ajax({
url: "/uploadytdlpcookie",
type: "POST",
data: formData,
processData: false,
contentType: false,
success: function(res) {
console.log(res);
alert("上传成功");
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(res);
alert("上传失败");
}
});
} else {
alert("请选择一个文件");
}
});
$("#clear_cache").on("click", () => {
localStorage.clear();
});
});

View File

@@ -0,0 +1,165 @@
.button {
line-height: 50px;
font-size: 14px;
}
button, .button {
margin: 10px;
width: 100px;
height: 50px;
border: none;
color: white;
text-align: center;
text-decoration: none;
display: inline-block;
border-radius: 10px;
background-color: #008CBA;
}
button:active, .button:active {
font-weight:bold;
background-color: #007CBA;
transform: translateY(2px);
}
label {
margin-left: 10px;
}
input,select {
margin-left: 5%;
margin-right: 5%;
margin-top: 10px;
margin-bottom: 10px;
width: 90%;
max-width: 400px;
height: 40px;
}
.rows {
display: flex;
flex-direction: column;
justify-content: center;
}
footer {
bottom: 0;
width: 100%;
text-align: center;
padding: 10px 0;
}
textarea{
margin-left: 5%;
margin-right: 5%;
margin-top: 10px;
margin-bottom: 10px;
width: 90%;
max-width: 400px;
height: 200px;
}
.custom-checkbox {
display: inline-block;
margin: 10px;
width: 20px;
height: 20px;
vertical-align: middle; /* 确保与标签垂直居中对齐 */
}
.checkbox-label {
display: inline-block;
width: 260px;
background-color: #fff;
border: 0px solid #ccc;
border-radius: 3px;
position: relative;
cursor: pointer;
vertical-align: middle; /* 确保与复选框垂直居中对齐 */
margin-left: 1px; /* 给复选框和标签之间一些距离,如果需要的话 */
}
.text {
margin: 10px;
width: 150px;
height: 50px;
text-align: center;
text-decoration: none;
display: inline-block;
}
.qrcode {
width: 100%;
max-width: 480px;
height: auto;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.blink {
animation: blink 1s infinite;
}
.login-tips {
color: red;
font-size: 12px;
margin-left: 10px;
}
.login-tips a {
color: rgb(9, 105, 218);
text-decoration: underline;
}
#valid-host {
padding: 20px;
border: none;
border-radius: 10px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
#valid-host::backdrop {
background-color: rgba(0, 0, 0, 0.5);
}
#valid-host form input {
width: fit-content;
margin: 0;
height: fit-content;
}
#valid-host p {
word-break: break-all;
}
#valid-host p span {
color: red;
}
#valid-host a, #valid-host a:visited {
color: rgb(9, 105, 218);;
text-decoration: underline;
display: flex;
align-items: center;
}
#valid-host a:hover {
color: rgb(9, 95, 198);
}
#valid-host .btn-list {
display: flex;
justify-content: center;
margin-top: 20px;
}
#valid-host .btn-list button {
width: fit-content;
min-width: 60px;
height: 40px;
border: none;
color: white;
text-align: center;
text-decoration: none;
display: inline-block;
border-radius: 10px;
background-color: #008CBA;
}
#valid-host .btn-list button:hover {
font-weight:bold;
background-color: #007CBA;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

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