Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
790a4cd808 | ||
|
|
70d029f4d1 | ||
|
|
0472ad3188 | ||
|
|
733c44d12f | ||
|
|
8c92afd09b | ||
|
|
742929b9d5 | ||
|
|
4766e08ff4 | ||
|
|
99c4296c7a | ||
|
|
0ae24f4810 | ||
|
|
77cca0d18c | ||
|
|
ddc24595df | ||
|
|
ce1bfb164c | ||
|
|
78dbba6c1f | ||
|
|
7eb0ab1e46 | ||
|
|
0a2e3cc579 | ||
|
|
75ec336285 | ||
|
|
a19d53f000 | ||
|
|
cdb6190e7a | ||
|
|
1aa9b58561 | ||
|
|
6a990f48d0 | ||
|
|
82e92a0380 | ||
|
|
ecce5c8848 | ||
|
|
87fb34e5c9 | ||
|
|
df3c4b7fa9 | ||
|
|
cb2af0ee9f | ||
|
|
8f9bba0ca3 | ||
|
|
b86e8d3196 | ||
|
|
65067346f3 | ||
|
|
865b412fb7 | ||
|
|
441fffd59e | ||
|
|
2ee7b956cf | ||
|
|
126bfa43a2 | ||
|
|
3a4d702070 | ||
|
|
2c7a09b9d4 | ||
|
|
74837baaef | ||
|
|
def63b2407 | ||
|
|
93d2047c7a | ||
|
|
9a1b9d1949 | ||
|
|
88fa4318dc | ||
|
|
0bc4bc8b13 | ||
|
|
ca2ddcd89c | ||
|
|
78acc35a62 | ||
|
|
323a832a9e | ||
|
|
0c99f4d537 | ||
|
|
b2737f745d | ||
|
|
464a1e1bd1 | ||
|
|
7bd613f74c | ||
|
|
3fb0a3bae9 | ||
|
|
b4f0e7b349 | ||
|
|
7c1ce81b5e | ||
|
|
052027fb50 | ||
|
|
b4d8434507 |
1
.gitignore
vendored
@@ -169,3 +169,4 @@ setting.json
|
||||
.DS_Store
|
||||
cache
|
||||
tmp/
|
||||
xiaomusic.log.txt
|
||||
|
||||
86
CHANGELOG.md
@@ -1,3 +1,89 @@
|
||||
## 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
|
||||
|
||||
31
README.md
@@ -6,7 +6,8 @@
|
||||
[](https://pypi.org/project/xiaomusic/)
|
||||
[](https://pypi.org/project/xiaomusic/)
|
||||
[](https://github.com/hanxi/xiaomusic/releases)
|
||||
|
||||
[](https://visitorbadge.io/status?path=hanxi%2Fxiaomusic)
|
||||
[](https://visitorbadge.io/status?path=hanxi%2Fxiaomusic)
|
||||
|
||||
|
||||
使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。
|
||||
@@ -72,6 +73,29 @@ docker 和 docker compose 二选一即可,启动成功后,在 web 页面可
|
||||
|
||||
### 🔥 修改默认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
|
||||
@@ -94,7 +118,7 @@ services:
|
||||
遇到问题可以去 web 设置页面底部点击【下载日志文件】按钮,然后搜索一下日志文件内容确保里面没有账号密码信息后(有就删除这些敏感信息),然后在提 issues 反馈问题时把下载的日志文件带上。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> XIAOMUSIC_PORT 也可以在后台配置,对应的是监听端口。
|
||||
> XIAOMUSIC_PORT 也可以在后台配置,对应的是监听端口,修改后记得重启。
|
||||
|
||||
|
||||
### 🤐 支持语音口令
|
||||
@@ -201,7 +225,8 @@ docker build -t xiaomusic .
|
||||
| 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 | 已经支持的触屏版 |
|
||||
| M01/XMYX01JY | 小米小爱音箱HD (获取对话记录的接口比较特殊) |
|
||||
| X08C X08E X8F | 需要设置【型号兼容模式】选项为 true |
|
||||
| M01/XMYX01JY | 小米小爱音箱HD 需要设置【特殊型号获取对话记录】选项为 true 才能语音播放|
|
||||
|
||||
型号与产品名称对照可以在这里查询 <https://home.miot-spec.com/s/xiaomi.wifispeaker>
|
||||
|
||||
|
||||
160
pdm.lock
generated
@@ -5,7 +5,7 @@
|
||||
groups = ["default", "dev", "lint"]
|
||||
strategy = ["inherit_metadata"]
|
||||
lock_version = "4.5.0"
|
||||
content_hash = "sha256:10d18ad4e1cc30b6683ac4c5fcdf73ab7aa3e6a5910b94e5ded3183e108bbbe1"
|
||||
content_hash = "sha256:d22d4b03450cd369683d3ddf827359cd96541ee09e29bfa1fa666d72aa7d3ab0"
|
||||
|
||||
[[metadata.targets]]
|
||||
requires_python = "==3.10.12"
|
||||
@@ -39,23 +39,24 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.10.10"
|
||||
requires_python = ">=3.8"
|
||||
version = "3.11.9"
|
||||
requires_python = ">=3.9"
|
||||
summary = "Async http client/server framework (asyncio)"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
dependencies = [
|
||||
"aiohappyeyeballs>=2.3.0",
|
||||
"aiosignal>=1.1.2",
|
||||
"async-timeout<5.0,>=4.0; python_version < \"3.11\"",
|
||||
"async-timeout<6.0,>=4.0; python_version < \"3.11\"",
|
||||
"attrs>=17.3.0",
|
||||
"frozenlist>=1.1.1",
|
||||
"multidict<7.0,>=4.5",
|
||||
"yarl<2.0,>=1.12.0",
|
||||
"propcache>=0.2.0",
|
||||
"yarl<2.0,>=1.17.0",
|
||||
]
|
||||
files = [
|
||||
{file = "aiohttp-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab5a5a0c7a7991d90446a198689c0535be89bbd6b410a1f9a66688f0880ec026"},
|
||||
{file = "aiohttp-3.10.10.tar.gz", hash = "sha256:0631dd7c9f0822cc61c88586ca76d5b5ada26538097d0f1df510b082bad3411a"},
|
||||
{file = "aiohttp-3.11.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39625703540feb50b6b7f938b3856d1f4886d2e585d88274e62b1bd273fae09b"},
|
||||
{file = "aiohttp-3.11.9.tar.gz", hash = "sha256:a9266644064779840feec0e34f10a89b3ff1d2d6b751fe90017abcad1864fa7c"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -108,20 +109,18 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "apscheduler"
|
||||
version = "3.10.4"
|
||||
requires_python = ">=3.6"
|
||||
version = "3.11.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "In-process task scheduler with Cron-like capabilities"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
dependencies = [
|
||||
"importlib-metadata>=3.6.0; python_version < \"3.8\"",
|
||||
"pytz",
|
||||
"six>=1.4.0",
|
||||
"tzlocal!=3.*,>=2.0",
|
||||
"backports-zoneinfo; python_version < \"3.9\"",
|
||||
"tzlocal>=3.0",
|
||||
]
|
||||
files = [
|
||||
{file = "APScheduler-3.10.4-py3-none-any.whl", hash = "sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661"},
|
||||
{file = "APScheduler-3.10.4.tar.gz", hash = "sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a"},
|
||||
{file = "APScheduler-3.11.0-py3-none-any.whl", hash = "sha256:fc134ca32e50f5eadcc4938e3a4545ab19131435e851abb40b34d63d5141c6da"},
|
||||
{file = "apscheduler-3.11.0.tar.gz", hash = "sha256:4c622d250b0955a65d5d0eb91c33e6d43fd879834bf541e0a18661ae60460133"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -166,6 +165,17 @@ files = [
|
||||
{file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "brotli"
|
||||
version = "1.1.0"
|
||||
summary = "Python bindings for the Brotli compression library"
|
||||
groups = ["default"]
|
||||
marker = "implementation_name == \"cpython\" and python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"},
|
||||
{file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2024.8.30"
|
||||
@@ -265,8 +275,8 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "commitizen"
|
||||
version = "3.30.0"
|
||||
requires_python = ">=3.8"
|
||||
version = "4.0.0"
|
||||
requires_python = ">=3.9"
|
||||
summary = "Python commitizen client tool"
|
||||
groups = ["dev"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
@@ -282,11 +292,11 @@ dependencies = [
|
||||
"questionary<3.0,>=2.0",
|
||||
"termcolor<3,>=1.1",
|
||||
"tomlkit<1.0.0,>=0.5.3",
|
||||
"typing-extensions<5.0.0,>=4.0.1; python_version < \"3.8\"",
|
||||
"typing-extensions<5.0.0,>=4.0.1; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "commitizen-3.30.0-py3-none-any.whl", hash = "sha256:8dc226a136aee61207e396101fcd89e73de67a57c06e066db982310863caaf65"},
|
||||
{file = "commitizen-3.30.0.tar.gz", hash = "sha256:ae67a47c1a700b4f35ac12de0c35c7ba96f152b9377d22b6226bb87372c527b0"},
|
||||
{file = "commitizen-4.0.0-py3-none-any.whl", hash = "sha256:52873ee589a64cf77fc55570dbd3f987c6ffcd33132d179eb625c4d06ae935f7"},
|
||||
{file = "commitizen-4.0.0.tar.gz", hash = "sha256:16aff27e01b43015eab1c74eabbca3e284b4988dd1b146a0963282db241dc2c0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -315,7 +325,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "fastapi"
|
||||
version = "0.115.4"
|
||||
version = "0.115.5"
|
||||
requires_python = ">=3.8"
|
||||
summary = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
groups = ["default"]
|
||||
@@ -326,8 +336,8 @@ dependencies = [
|
||||
"typing-extensions>=4.8.0",
|
||||
]
|
||||
files = [
|
||||
{file = "fastapi-0.115.4-py3-none-any.whl", hash = "sha256:0b504a063ffb3cf96a5e27dc1bc32c80ca743a2528574f9cdc77daa2d31b4742"},
|
||||
{file = "fastapi-0.115.4.tar.gz", hash = "sha256:db653475586b091cb8b2fec2ac54a680ac6a158e07406e1abae31679e8826349"},
|
||||
{file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"},
|
||||
{file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -659,6 +669,18 @@ files = [
|
||||
{file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycryptodomex"
|
||||
version = "3.21.0"
|
||||
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
||||
summary = "Cryptographic library for Python"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "pycryptodomex-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9aa0cf13a1a1128b3e964dc667e5fe5c6235f7d7cfb0277213f0e2a783837cc2"},
|
||||
{file = "pycryptodomex-3.21.0.tar.gz", hash = "sha256:222d0bd05381dd25c32dd6065c071ebf084212ab79bab4599ba9e6a3e0009e6c"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.8.2"
|
||||
@@ -761,25 +783,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "python-multipart"
|
||||
version = "0.0.17"
|
||||
version = "0.0.19"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A streaming multipart parser for Python"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "python_multipart-0.0.17-py3-none-any.whl", hash = "sha256:15dc4f487e0a9476cc1201261188ee0940165cffc94429b6fc565c4d3045cb5d"},
|
||||
{file = "python_multipart-0.0.17.tar.gz", hash = "sha256:41330d831cae6e2f22902704ead2826ea038d0419530eadff3ea80175aec5538"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2024.2"
|
||||
summary = "World timezone definitions, modern and historical"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
|
||||
{file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
|
||||
{file = "python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d"},
|
||||
{file = "python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -868,26 +879,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.7.2"
|
||||
version = "0.8.1"
|
||||
requires_python = ">=3.7"
|
||||
summary = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
groups = ["lint"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "ruff-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dba53ed84ac19ae4bfb4ea4bf0172550a2285fa27fbb13e3746f04c80f7fa088"},
|
||||
{file = "ruff-0.7.2.tar.gz", hash = "sha256:2b14e77293380e475b4e3a7a368e14549288ed2931fce259a6f99978669e844f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
summary = "Python 2 and 3 compatibility utilities"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||
{file = "ruff-0.8.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22346f845fec132aa39cd29acb94451d030c10874408dbf776af3aaeb53284c"},
|
||||
{file = "ruff-0.8.1.tar.gz", hash = "sha256:3583db9a6450364ed5ca3f3b4225958b24f78178908d5c4bc0f46251ccca898f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -904,7 +903,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "starlette"
|
||||
version = "0.41.2"
|
||||
version = "0.41.3"
|
||||
requires_python = ">=3.8"
|
||||
summary = "The little ASGI library that shines."
|
||||
groups = ["default"]
|
||||
@@ -914,8 +913,8 @@ dependencies = [
|
||||
"typing-extensions>=3.10.0; python_version < \"3.10\"",
|
||||
]
|
||||
files = [
|
||||
{file = "starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d"},
|
||||
{file = "starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62"},
|
||||
{file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"},
|
||||
{file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -947,7 +946,7 @@ name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
groups = ["default"]
|
||||
groups = ["default", "dev"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
|
||||
@@ -984,7 +983,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.32.0"
|
||||
version = "0.32.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "The lightning-fast ASGI server."
|
||||
groups = ["default"]
|
||||
@@ -995,8 +994,8 @@ dependencies = [
|
||||
"typing-extensions>=4.0; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"},
|
||||
{file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"},
|
||||
{file = "uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e"},
|
||||
{file = "uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1013,6 +1012,19 @@ files = [
|
||||
{file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "14.1"
|
||||
requires_python = ">=3.9"
|
||||
summary = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"},
|
||||
{file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"},
|
||||
{file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.17.1"
|
||||
@@ -1033,12 +1045,36 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "yt-dlp"
|
||||
version = "2024.11.4"
|
||||
version = "2024.12.1.232904.dev0"
|
||||
requires_python = ">=3.9"
|
||||
summary = "A feature-rich command-line audio/video downloader"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
files = [
|
||||
{file = "yt_dlp-2024.11.4-py3-none-any.whl", hash = "sha256:589d51ed9f154624a45c1f0ceb3d68d0d1e2031460e8dbc62139be631c20b388"},
|
||||
{file = "yt_dlp-2024.11.4.tar.gz", hash = "sha256:ed204c1b61bc563e134447766d1ab343173540799e13ebb953e887ce7dcf6865"},
|
||||
{file = "yt_dlp-2024.12.1.232904.dev0-py3-none-any.whl", hash = "sha256:67c1fa0986662f2f26ba70789c79c6d04fda45eb530944872f4a47ef4f94047e"},
|
||||
{file = "yt_dlp-2024.12.1.232904.dev0.tar.gz", hash = "sha256:4fcce6637e70bad7c63220e4dd69a3a6b8969cbc17c93bfe569940dd173c0548"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yt-dlp"
|
||||
version = "2024.12.1.232904.dev0"
|
||||
extras = ["default"]
|
||||
requires_python = ">=3.9"
|
||||
summary = "A feature-rich command-line audio/video downloader"
|
||||
groups = ["default"]
|
||||
marker = "python_full_version == \"3.10.12\""
|
||||
dependencies = [
|
||||
"brotli; implementation_name == \"cpython\"",
|
||||
"brotlicffi; implementation_name != \"cpython\"",
|
||||
"certifi",
|
||||
"mutagen",
|
||||
"pycryptodomex",
|
||||
"requests<3,>=2.32.2",
|
||||
"urllib3<3,>=1.26.17",
|
||||
"websockets>=13.0",
|
||||
"yt-dlp==2024.12.1.232904.dev0",
|
||||
]
|
||||
files = [
|
||||
{file = "yt_dlp-2024.12.1.232904.dev0-py3-none-any.whl", hash = "sha256:67c1fa0986662f2f26ba70789c79c6d04fda45eb530944872f4a47ef4f94047e"},
|
||||
{file = "yt_dlp-2024.12.1.232904.dev0.tar.gz", hash = "sha256:4fcce6637e70bad7c63220e4dd69a3a6b8969cbc17c93bfe569940dd173c0548"},
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "xiaomusic"
|
||||
version = "0.3.46"
|
||||
version = "0.3.54"
|
||||
description = "Play Music with xiaomi AI speaker"
|
||||
authors = [
|
||||
{name = "涵曦", email = "im.hanxi@gmail.com"},
|
||||
@@ -9,7 +9,7 @@ dependencies = [
|
||||
"aiohttp>=3.8.6",
|
||||
"miservice-fork>=2.7.0",
|
||||
"mutagen>=1.47.0",
|
||||
"yt-dlp>=2024.07.01",
|
||||
"yt-dlp[default]>=2024.12.1.232904.dev0",
|
||||
"uvicorn>=0.30.1",
|
||||
"fastapi>=0.115.4",
|
||||
"starlette>=0.37.2",
|
||||
|
||||
33
test/test_difflib.py
Normal 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)
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.3.46"
|
||||
__version__ = "0.3.54"
|
||||
|
||||
@@ -30,6 +30,7 @@ class Analytics:
|
||||
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
|
||||
@@ -66,17 +67,18 @@ class Analytics:
|
||||
self.gtag.send(events=event_list)
|
||||
self.current_date = current_date
|
||||
|
||||
async def send_play_event(self, name, sec):
|
||||
async def send_play_event(self, name, sec, hardware):
|
||||
try:
|
||||
await self.run_with_cancel(self._send_play_event, name, sec)
|
||||
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):
|
||||
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)
|
||||
|
||||
@@ -6,22 +6,27 @@ import os
|
||||
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
|
||||
|
||||
|
||||
# 默认口令
|
||||
def default_key_word_dict():
|
||||
return {
|
||||
"播放歌曲": "play",
|
||||
"播放本地歌曲": "playlocal",
|
||||
"关机": "stop",
|
||||
"下一首": "play_next",
|
||||
"上一首": "play_prev",
|
||||
"单曲循环": "set_play_type_one",
|
||||
"全部循环": "set_play_type_all",
|
||||
"随机播放": "set_random_play",
|
||||
"随机播放": "set_play_type_rnd",
|
||||
"单曲播放": "set_play_type_sin",
|
||||
"顺序播放": "set_play_type_seq",
|
||||
"分钟后关机": "stop_after_minute",
|
||||
"播放列表": "play_music_list",
|
||||
"刷新列表": "gen_music_list",
|
||||
"加入收藏": "add_to_favorites",
|
||||
"收藏歌曲": "add_to_favorites",
|
||||
@@ -47,12 +52,13 @@ KEY_WORD_ARG_BEFORE_DICT = {
|
||||
def default_key_match_order():
|
||||
return [
|
||||
"分钟后关机",
|
||||
"播放歌曲",
|
||||
"下一首",
|
||||
"上一首",
|
||||
"单曲循环",
|
||||
"全部循环",
|
||||
"随机播放",
|
||||
"单曲播放",
|
||||
"顺序播放",
|
||||
"关机",
|
||||
"刷新列表",
|
||||
"播放列表第",
|
||||
@@ -82,9 +88,8 @@ class Config:
|
||||
miio_tts_command: str = os.getenv("MIIO_TTS_CMD", "")
|
||||
cookie: str = ""
|
||||
verbose: bool = os.getenv("XIAOMUSIC_VERBOSE", "").lower() == "true"
|
||||
music_path: str = os.getenv(
|
||||
"XIAOMUSIC_MUSIC_PATH", "music"
|
||||
) # 只能是music目录下的子目录
|
||||
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")
|
||||
@@ -98,7 +103,7 @@ class Config:
|
||||
ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin")
|
||||
active_cmd: str = os.getenv(
|
||||
"XIAOMUSIC_ACTIVE_CMD",
|
||||
"play,set_random_play,playlocal,play_music_list,play_music_list_index,stop_after_minute,stop",
|
||||
"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"))
|
||||
@@ -122,7 +127,7 @@ class Config:
|
||||
"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", "/tmp/xiaomusic.txt")
|
||||
log_file: str = os.getenv("XIAOMUSIC_LOG_FILE", "xiaomusic.log.txt")
|
||||
# 模糊搜索匹配的最低相似度阈值
|
||||
fuzzy_match_cutoff: float = float(os.getenv("XIAOMUSIC_FUZZY_MATCH_CUTOFF", "0.6"))
|
||||
# 开启模糊搜索
|
||||
@@ -166,6 +171,24 @@ class Config:
|
||||
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(","):
|
||||
@@ -188,6 +211,9 @@ class Config:
|
||||
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:
|
||||
@@ -282,3 +308,22 @@ class Config:
|
||||
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 ""
|
||||
|
||||
@@ -13,14 +13,17 @@ COOKIE_TEMPLATE = "deviceId={device_id}; serviceToken={service_token}; userId={u
|
||||
PLAY_TYPE_ONE = 0 # 单曲循环
|
||||
PLAY_TYPE_ALL = 1 # 全部循环
|
||||
PLAY_TYPE_RND = 2 # 随机播放
|
||||
|
||||
PLAY_TYPE_TTS = {
|
||||
PLAY_TYPE_ONE: "已经设置为单曲循环",
|
||||
PLAY_TYPE_ALL: "已经设置为全部循环",
|
||||
PLAY_TYPE_RND: "已经设置为随机播放",
|
||||
}
|
||||
PLAY_TYPE_SIN = 3 # 单曲播放
|
||||
PLAY_TYPE_SEQ = 4 # 顺序播放
|
||||
|
||||
# 需要采用 mina 获取对话记录的设备型号
|
||||
GET_ASK_BY_MINA = {
|
||||
"M01",
|
||||
}
|
||||
|
||||
# 需要使用 play_musci 接口的设备型号
|
||||
NEED_USE_PLAY_MUSIC_API = {
|
||||
"X08C",
|
||||
"X08E",
|
||||
"X8F",
|
||||
}
|
||||
|
||||
@@ -63,6 +63,14 @@ class Crontab:
|
||||
|
||||
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
|
||||
|
||||
@@ -303,6 +303,23 @@ async def musicinfos(
|
||||
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):
|
||||
@@ -325,6 +342,44 @@ 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)
|
||||
@@ -468,6 +523,51 @@ async def upload_yt_dlp_cookie(file: UploadFile = File(...)):
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@@ -4,17 +4,21 @@ $(function(){
|
||||
append_op_button_name("加入收藏");
|
||||
append_op_button_name("取消收藏");
|
||||
|
||||
const PLAY_TYPE_ONE = 0; // 单曲循环
|
||||
const PLAY_TYPE_ALL = 1; // 全部循环
|
||||
const PLAY_TYPE_RND = 2; // 随机播放
|
||||
append_op_button("play_type_all", "全部循环", "全部循环");
|
||||
append_op_button("play_type_one", "单曲循环", "单曲循环");
|
||||
append_op_button("play_type_rnd", "随机播放", "随机播放");
|
||||
|
||||
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>"));
|
||||
@@ -25,7 +29,7 @@ $(function(){
|
||||
|
||||
var offset = 0;
|
||||
var duration = 0;
|
||||
|
||||
let no_warning = localStorage.getItem('no-warning');
|
||||
// 拉取现有配置
|
||||
$.get("/getsetting", function(data, status) {
|
||||
console.log(data, status);
|
||||
@@ -71,6 +75,12 @@ $(function(){
|
||||
} 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('✔️ 顺序播放');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,11 +192,34 @@ $(function(){
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
let cmd = "播放列表" + music_list + "|" + music_name;
|
||||
sendcmd(cmd);
|
||||
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", () => {
|
||||
@@ -194,7 +227,7 @@ $(function(){
|
||||
$.get(`/musicinfo?name=${music_name}`, function(data, status) {
|
||||
console.log(data);
|
||||
if (data.ret == "OK") {
|
||||
$('audio').attr('src',data.url);
|
||||
validHost(data.url) && $('audio').attr('src',data.url);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -249,17 +282,32 @@ $(function(){
|
||||
$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 = "";
|
||||
if (filename == null || filename == "") {
|
||||
filename = search_key;
|
||||
}
|
||||
let cmd = "播放歌曲" + search_key + "|" + filename;
|
||||
sendcmd(cmd);
|
||||
do_play_music(filename, search_key);
|
||||
});
|
||||
|
||||
$("#volume").on('change', function () {
|
||||
@@ -298,7 +346,7 @@ $(function(){
|
||||
if (cmd == "刷新列表") {
|
||||
check_status_refresh_music_list(3); // 最多重试3次
|
||||
}
|
||||
if (["全部循环", "单曲循环", "随机播放"].includes(cmd)) {
|
||||
if (["全部循环", "单曲循环", "随机播放", "单曲播放", "顺序播放"].includes(cmd)) {
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
@@ -365,7 +413,7 @@ $(function(){
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
});
|
||||
}, 300));
|
||||
}, 500));
|
||||
|
||||
// 动态显示保存文件名输入框
|
||||
const musicNameSelect = document.getElementById('music-name');
|
||||
@@ -425,5 +473,48 @@ $(function(){
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Debug For XiaoMusic</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1731079341">
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1733247688">
|
||||
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||
<script src="./jquery-3.7.1.min.js?version=1731079341"></script>
|
||||
<script src="./jquery-3.7.1.min.js?version=1733247688"></script>
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>歌曲下载工具</title>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1731079341">
|
||||
<script src="./jquery-3.7.1.min.js?version=1731079341"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1733247688">
|
||||
<script src="./jquery-3.7.1.min.js?version=1733247688"></script>
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>小爱音箱操控面板</title>
|
||||
<script src="./jquery-3.7.1.min.js?version=1731079341"></script>
|
||||
<script src="./app.js?version=1731079341"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1731079341">
|
||||
<script src="./jquery-3.7.1.min.js?version=1733247688"></script>
|
||||
<script src="./app.js?version=1733247688"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1733247688">
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
@@ -94,5 +94,17 @@ var vConsole = new window.VConsole();
|
||||
<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>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<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=1731079341">
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1733247688">
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>小爱音箱操控面板</title>
|
||||
<script src="./jquery-3.7.1.min.js?version=1731079341"></script>
|
||||
<script src="./setting.js?version=1731079341"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1731079341">
|
||||
<script src="./jquery-3.7.1.min.js?version=1733247688"></script>
|
||||
<script src="./setting.js?version=1733247688"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css?version=1733247688">
|
||||
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-Z09NC1K7ZW"></script>
|
||||
@@ -43,13 +43,13 @@ var vConsole = new window.VConsole();
|
||||
<div id="setting">
|
||||
<div class="rows">
|
||||
<label for="account">*小米账号:</label>
|
||||
<input id="account" type="text" placeholder="填写小米登录账号"></input>
|
||||
<input id="account" type="text" placeholder="填写小米登录账号" />
|
||||
|
||||
<label for="password">*小米密码:</label>
|
||||
<input id="password" type="password" placeholder="填写小米登录密码"></input>
|
||||
<input id="password" type="password" placeholder="填写小米登录密码" />
|
||||
|
||||
<label for="hostname">*XIAOMUSIC_HOSTNAME(IP或域名):</label>
|
||||
<input id="hostname" type="text"></input>
|
||||
<input id="hostname" type="text" />
|
||||
</div>
|
||||
<hr>
|
||||
<div class="rows">
|
||||
@@ -59,36 +59,44 @@ var vConsole = new window.VConsole();
|
||||
<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"></input>
|
||||
<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"></input>
|
||||
<input id="music_path" type="text" value="music" />
|
||||
|
||||
<label for="download_path">音乐下载目录(必须是music的子目录):</label>
|
||||
<input id="download_path" type="text" value='music/download'></input>
|
||||
<input id="download_path" type="text" value='music/download' />
|
||||
|
||||
<label for="conf_path">配置文件目录:</label>
|
||||
<input id="conf_path" type="text"></input>
|
||||
|
||||
<input id="conf_path" type="text" />
|
||||
|
||||
<label for="cache_dir">缓存文件目录:</label>
|
||||
<input id="cache_dir" type="text"></input>
|
||||
<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"></input>
|
||||
<input id="ffmpeg_location" type="text" value="./ffmpeg/bin" />
|
||||
|
||||
<label for="log_file">日志路径:</label>
|
||||
<input id="log_file" type="text" value="/tmp/xiaomusic.txt"></input>
|
||||
<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"></input>
|
||||
<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"></input>
|
||||
<input id="exclude_dirs" type="text" value="@eaDir,tmp" />
|
||||
|
||||
<label for="music_path_depth">目录深度:</label>
|
||||
<input id="music_path_depth" type="number" value="10"></input>
|
||||
<input id="music_path_depth" type="number" value="10" />
|
||||
|
||||
<label for="search_prefix">XIAOMUSIC_SEARCH(歌曲下载方式):</label>
|
||||
<select id="search_prefix">
|
||||
@@ -97,7 +105,7 @@ var vConsole = new window.VConsole();
|
||||
</select>
|
||||
|
||||
<label for="proxy">XIAOMUSIC_PROXY(ytsearch需要):</label>
|
||||
<input id="proxy" type="text" placeholder="http://192.168.2.5:8080"></input>
|
||||
<input id="proxy" type="text" placeholder="http://192.168.2.5:8080" />
|
||||
|
||||
<label for="remove_id3tag">去除MP3 ID3v2和填充:</label>
|
||||
<select id="remove_id3tag">
|
||||
@@ -112,7 +120,7 @@ var vConsole = new window.VConsole();
|
||||
</select>
|
||||
|
||||
<label for="miio_tts_command">MiIO tts 指令(解决部分型号没有提示音的问题):</label>
|
||||
<input id="miio_tts_command" type="text" placeholder="如:5 或者 5-3"></input>
|
||||
<input id="miio_tts_command" type="text" placeholder="如:5 或者 5-3" />
|
||||
|
||||
<label for="disable_httpauth">关闭控制台密码验证:</label>
|
||||
<select id="disable_httpauth">
|
||||
@@ -120,9 +128,9 @@ var vConsole = new window.VConsole();
|
||||
<option value="false">false</option>
|
||||
</select>
|
||||
<label for="httpauth_username">控制台账户:</label>
|
||||
<input id="httpauth_username" type="text" value=""></input>
|
||||
<input id="httpauth_username" type="text" value="" />
|
||||
<label for="httpauth_password">控制台密码:</label>
|
||||
<input id="httpauth_password" type="password" value=""></input>
|
||||
<input id="httpauth_password" type="password" value="" />
|
||||
|
||||
<label for="disable_download">关闭下载功能:</label>
|
||||
<select id="disable_download">
|
||||
@@ -131,12 +139,12 @@ var vConsole = new window.VConsole();
|
||||
</select>
|
||||
|
||||
<label for="use_music_audio_id">触屏版显示歌曲ID:</label>
|
||||
<input id="use_music_audio_id" type="text" value="1582971365183456177"></input>
|
||||
<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"></input>
|
||||
<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"></input>
|
||||
<input id="fuzzy_match_cutoff" type="number" value="0.6" />
|
||||
|
||||
<label for="enable_fuzzy_match">开启模糊搜索:</label>
|
||||
<select id="enable_fuzzy_match">
|
||||
@@ -156,28 +164,33 @@ var vConsole = new window.VConsole();
|
||||
<option value="false" selected>false</option>
|
||||
</select>
|
||||
|
||||
<label for="port">监听端口(修改后需要重启):</label>
|
||||
<input id="port" type="number" value="8090"></input>
|
||||
|
||||
<label for="public_port">外网访问端口(0表示跟监听端口一致):</label>
|
||||
<input id="public_port" type="number" value="0"></input>
|
||||
|
||||
<label for="pull_ask_sec">获取对话记录间隔(秒):</label>
|
||||
<input id="pull_ask_sec" type="number" value="1"></input>
|
||||
<input id="pull_ask_sec" type="number" value="1" />
|
||||
|
||||
<label for="delay_sec">下一首歌延迟播放秒数:</label>
|
||||
<input id="delay_sec" type="number" value="3"></input>
|
||||
<input id="delay_sec" type="number" value="3" />
|
||||
|
||||
<label for="stop_tts_msg">停止提示音:</label>
|
||||
<input id="stop_tts_msg" type="text" value="收到,再见"></input>
|
||||
<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="播放本地歌曲,本地播放歌曲"></input>
|
||||
<input id="keywords_playlocal" type="text" value="播放本地歌曲,本地播放歌曲" />
|
||||
<label for="keywords_play">播放歌曲口令:</label>
|
||||
<input id="keywords_play" type="text" value="播放歌曲,放歌曲"></input>
|
||||
<input id="keywords_play" type="text" value="播放歌曲,放歌曲" />
|
||||
<label for="keywords_playlist">播放列表口令:</label>
|
||||
<input id="keywords_playlist" type="text" value="播放列表,播放歌单"></input>
|
||||
<input id="keywords_playlist" type="text" value="播放列表,播放歌单" />
|
||||
<label for="keywords_stop">停止口令:</label>
|
||||
<input id="keywords_stop" type="text" value="关机,暂停,停止,停止播放"></input>
|
||||
<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">
|
||||
@@ -191,8 +204,11 @@ var vConsole = new window.VConsole();
|
||||
<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"></input>
|
||||
<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>
|
||||
|
||||
@@ -16,13 +16,19 @@ $(function(){
|
||||
});
|
||||
};
|
||||
|
||||
function updateCheckbox(selector, mi_did, device_list) {
|
||||
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;
|
||||
@@ -64,7 +70,8 @@ $(function(){
|
||||
// 拉取现有配置
|
||||
$.get("/getsetting?need_device_list=true", function(data, status) {
|
||||
console.log(data, status);
|
||||
updateCheckbox("#mi_did", data.mi_did, data.device_list);
|
||||
const accountPassValid = data.account && data.password;
|
||||
updateCheckbox("#mi_did", data.mi_did, data.device_list, accountPassValid);
|
||||
|
||||
// 初始化显示
|
||||
for (const key in data) {
|
||||
|
||||
@@ -21,7 +21,6 @@ button:active, .button:active {
|
||||
}
|
||||
label {
|
||||
margin-left: 10px;
|
||||
width: 300px;
|
||||
}
|
||||
input,select {
|
||||
margin-left: 5%;
|
||||
@@ -100,3 +99,67 @@ footer {
|
||||
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;
|
||||
}
|
||||
BIN
xiaomusic/static/icons/android/android-launchericon-144-144.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
xiaomusic/static/icons/android/android-launchericon-192-192.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
xiaomusic/static/icons/android/android-launchericon-48-48.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
xiaomusic/static/icons/android/android-launchericon-512-512.png
Normal file
|
After Width: | Height: | Size: 281 KiB |
BIN
xiaomusic/static/icons/android/android-launchericon-72-72.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
xiaomusic/static/icons/android/android-launchericon-96-96.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
xiaomusic/static/icons/ios/100.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
xiaomusic/static/icons/ios/1024.png
Normal file
|
After Width: | Height: | Size: 990 KiB |
BIN
xiaomusic/static/icons/ios/114.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
xiaomusic/static/icons/ios/120.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
xiaomusic/static/icons/ios/128.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
xiaomusic/static/icons/ios/144.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
xiaomusic/static/icons/ios/152.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
xiaomusic/static/icons/ios/16.png
Normal file
|
After Width: | Height: | Size: 657 B |
BIN
xiaomusic/static/icons/ios/167.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
xiaomusic/static/icons/ios/180.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
xiaomusic/static/icons/ios/192.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
xiaomusic/static/icons/ios/20.png
Normal file
|
After Width: | Height: | Size: 891 B |
BIN
xiaomusic/static/icons/ios/256.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
xiaomusic/static/icons/ios/29.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
xiaomusic/static/icons/ios/32.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
xiaomusic/static/icons/ios/40.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
xiaomusic/static/icons/ios/50.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
xiaomusic/static/icons/ios/512.png
Normal file
|
After Width: | Height: | Size: 281 KiB |
BIN
xiaomusic/static/icons/ios/57.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
xiaomusic/static/icons/ios/58.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
xiaomusic/static/icons/ios/60.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
xiaomusic/static/icons/ios/64.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
xiaomusic/static/icons/ios/72.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
xiaomusic/static/icons/ios/76.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
xiaomusic/static/icons/ios/80.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
xiaomusic/static/icons/ios/87.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
xiaomusic/static/icons/windows11/LargeTile.scale-100.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
xiaomusic/static/icons/windows11/LargeTile.scale-125.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
xiaomusic/static/icons/windows11/LargeTile.scale-150.png
Normal file
|
After Width: | Height: | Size: 233 KiB |
BIN
xiaomusic/static/icons/windows11/LargeTile.scale-200.png
Normal file
|
After Width: | Height: | Size: 404 KiB |
BIN
xiaomusic/static/icons/windows11/LargeTile.scale-400.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
xiaomusic/static/icons/windows11/SmallTile.scale-100.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
xiaomusic/static/icons/windows11/SmallTile.scale-125.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
xiaomusic/static/icons/windows11/SmallTile.scale-150.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
xiaomusic/static/icons/windows11/SmallTile.scale-200.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
xiaomusic/static/icons/windows11/SmallTile.scale-400.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
xiaomusic/static/icons/windows11/SplashScreen.scale-100.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
xiaomusic/static/icons/windows11/SplashScreen.scale-125.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
xiaomusic/static/icons/windows11/SplashScreen.scale-150.png
Normal file
|
After Width: | Height: | Size: 227 KiB |
BIN
xiaomusic/static/icons/windows11/SplashScreen.scale-200.png
Normal file
|
After Width: | Height: | Size: 394 KiB |
BIN
xiaomusic/static/icons/windows11/SplashScreen.scale-400.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
xiaomusic/static/icons/windows11/Square150x150Logo.scale-100.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
xiaomusic/static/icons/windows11/Square150x150Logo.scale-125.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
xiaomusic/static/icons/windows11/Square150x150Logo.scale-150.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
xiaomusic/static/icons/windows11/Square150x150Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
xiaomusic/static/icons/windows11/Square150x150Logo.scale-400.png
Normal file
|
After Width: | Height: | Size: 379 KiB |
|
After Width: | Height: | Size: 760 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 760 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 9.9 KiB |