mirror of
https://github.com/hanxi/xiaomusic.git
synced 2025-12-06 14:52:50 +08:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8f1157e27 | ||
|
|
06558c24b7 | ||
|
|
6bd399b654 | ||
|
|
228d89f1f8 | ||
|
|
e97639302f | ||
|
|
7f4e51be08 | ||
|
|
cdab5fc92d | ||
|
|
6efe498f2a | ||
|
|
0f3f2e47f5 | ||
|
|
3b720b7367 | ||
|
|
9a3e513b6c | ||
|
|
5a8e5dfa82 | ||
|
|
70d9ad93cb | ||
|
|
87b3411f5e | ||
|
|
5c88c79ac6 | ||
|
|
5df91e7a59 | ||
|
|
71e9c15b5d | ||
|
|
c151144a5a | ||
|
|
82a3373e72 | ||
|
|
29ef5f238f | ||
|
|
1809a2ab54 | ||
|
|
3b1684f553 | ||
|
|
d088374333 | ||
|
|
80da6bd1e6 | ||
|
|
619bb9c853 | ||
|
|
5add7b7a5c | ||
|
|
f61f14e16c | ||
|
|
125421db22 | ||
|
|
98b73f72df | ||
|
|
e68bc3b937 | ||
|
|
ab447a4633 | ||
|
|
23d321a722 | ||
|
|
20945954b1 | ||
|
|
d6c2078917 | ||
|
|
a5b8dc639c | ||
|
|
84751e0d68 | ||
|
|
e759658481 | ||
|
|
d83100588f | ||
|
|
83d0e02eb4 | ||
|
|
20f1f33b6c |
@@ -1,4 +1,5 @@
|
||||
FROM python:3.10 AS builder
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
WORKDIR /app
|
||||
COPY requirements.txt .
|
||||
RUN python3 -m venv .venv && .venv/bin/pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
65
README.md
65
README.md
@@ -13,6 +13,8 @@
|
||||
|
||||
<https://github.com/hanxi/xiaomusic>
|
||||
|
||||
> 初次安装遇到问题请查阅 <https://github.com/hanxi/xiaomusic/issues/99> 上是否已经有解决办法。
|
||||
|
||||
## 最简配置运行
|
||||
|
||||
已经支持在 web 页面配置其他参数,docker compose 配置如下:
|
||||
@@ -50,7 +52,7 @@ docker run -e MI_USER='小米账号' \
|
||||
|
||||
### ✨ 修改8090端口
|
||||
|
||||
如果需要修改 8090 端口为其他端口,比如 5678,需要这样配,3个数字都需要是 5678
|
||||
如果需要修改 8090 端口为其他端口,比如 5678,需要这样配,3个数字都需要是 5678 。见 <https://github.com/hanxi/xiaomusic/issues/19>
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -72,6 +74,37 @@ services:
|
||||
|
||||
其中 XIAOMUSIC_VERBOSE 设置为 'true' 时表示开启 debug 日志,遇到问题可以去 web 设置页面底部【下载日志文件】按钮,然后搜索一下日志文件内容确保里面没有账号密码信息后(有就删除这些敏感信息),然后在提 issues 反馈问题时把下载的日志文件带上。
|
||||
|
||||
## pip 方式安装运行
|
||||
|
||||
```shell
|
||||
> pip install xiaomusic
|
||||
> xiaomusic --help
|
||||
__ __ _ __ __ _
|
||||
\ \/ / (_) __ _ ___ | \/ | _ _ ___ (_) ___
|
||||
\ / | | / _` | / _ \ | |\/| | | | | | / __| | | / __|
|
||||
/ \ | | | (_| | | (_) | | | | | | |_| | \__ \ | | | (__
|
||||
/_/\_\ |_| \__,_| \___/ |_| |_| \__,_| |___/ |_| \___|
|
||||
XiaoMusic v0.1.81 by: github.com/hanxi
|
||||
|
||||
usage: xiaomusic.py [-h] [--hardware HARDWARE] [--account ACCOUNT]
|
||||
[--password PASSWORD] [--cookie COOKIE] [--verbose]
|
||||
[--config CONFIG] [--ffmpeg_location FFMPEG_LOCATION]
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--hardware 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>
|
||||
|
||||
## 开发环境运行
|
||||
|
||||
- 使用 install_dependencies.sh 下载依赖
|
||||
@@ -108,20 +141,20 @@ pdm run xiaomusic.py
|
||||
|
||||
## 已测试支持的设备
|
||||
|
||||
```txt
|
||||
- L06A
|
||||
- L07A
|
||||
- S12
|
||||
- S12A
|
||||
- LX5A
|
||||
- LX05
|
||||
- L16A
|
||||
- L17A
|
||||
- LX06
|
||||
- LX01
|
||||
- L05B
|
||||
- L05C
|
||||
````
|
||||
| 型号 | 名称 |
|
||||
| ---- | ---------------------------------------------------------------------------------------------- |
|
||||
| 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 | [小米AI音箱](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.s12) |
|
||||
| S12A | - |
|
||||
| LX5A | [小爱音箱 万能遥控版](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.lx5a) |
|
||||
| LX05 | [小爱音箱Play(2019款)](https://home.mi.com/baike/index.html#/detail?model=xiaomi.wifispeaker.lx05) |
|
||||
| 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) |
|
||||
|
||||
型号与产品名称对照可以在这里查询 <https://home.miot-spec.com/s/xiaomi.wifispeaker>
|
||||
|
||||
@@ -290,7 +323,7 @@ services:
|
||||
- XIAOMUSIC_HTTPAUTH_PASSWORD 配置 web 控制台密码
|
||||
- XIAOMUSIC_CONF_PATH 用来存放配置文件的目录,记得把目录映射到主机,默认情况会把配置存放在music目录,具体见 <https://github.com/hanxi/xiaomusic/issues/74>
|
||||
- XIAOMUSIC_DISABLE_DOWNLOAD 设为 true 时关闭下载功能,见 <https://github.com/hanxi/xiaomusic/issues/82>
|
||||
- XIAOMUSIC_USE_MUSIC_API 设为 true 时使用 player_play_music 接口播放音乐,用于兼容不能播放的型号
|
||||
- XIAOMUSIC_USE_MUSIC_API 设为 true 时使用 player_play_music 接口播放音乐,用于兼容不能播放的型号,v0.1.86 之后的版本可以不用设置。
|
||||
- XIAOMUSIC_KEYWORDS_PLAY 用来播放歌曲的口令前缀,默认是 "播放歌曲,放歌曲" ,可以用英文逗号分割配置多个
|
||||
- XIAOMUSIC_KEYWORDS_STOP 用来关机的口令,默认是 "关机,暂停,停止" ,可以用英文逗号分割配置多个。
|
||||
- XIAOMUSIC_KEYWORDS_PLAYLOCAL 用来播放本地歌曲的口令前缀,本地找不到时不会下载歌曲,默认是 "播放本地歌曲,本地播放歌曲" ,可以用英文逗号分割配置多个。
|
||||
|
||||
@@ -24,5 +24,9 @@
|
||||
"use_music_api": false,
|
||||
"log_file": "/tmp/xiaomusic.txt",
|
||||
"fuzzy_match_cutoff": 0.6,
|
||||
"enable_fuzzy_match": true
|
||||
"enable_fuzzy_match": true,
|
||||
"stop_tts_msg": "收到,再见",
|
||||
"keywords_playlocal": "播放本地歌曲,本地播放歌曲",
|
||||
"keywords_play": "播放歌曲,放歌曲",
|
||||
"keywords_stop": "关机,暂停,停止,停止播放"
|
||||
}
|
||||
@@ -4,14 +4,47 @@
|
||||
# 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/
|
||||
}
|
||||
|
||||
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/
|
||||
}
|
||||
|
||||
# 基于架构执行不同的操作
|
||||
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
|
||||
|
||||
14
pdm.lock
generated
14
pdm.lock
generated
@@ -5,7 +5,7 @@
|
||||
groups = ["default", "lint"]
|
||||
strategy = ["cross_platform"]
|
||||
lock_version = "4.4.1"
|
||||
content_hash = "sha256:813253734c7d7835a76cd87fe8fe0329e02ad067f535aee6a9e11cb106569dd2"
|
||||
content_hash = "sha256:ac53cf6421de7aded8475907adc40a716a3e5c6429c614b93e5cfbddea36d048"
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
@@ -519,7 +519,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "miservice-fork"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "XiaoMi Cloud Service fork from https://github.com/Yonsm/MiService"
|
||||
dependencies = [
|
||||
@@ -528,8 +528,8 @@ dependencies = [
|
||||
"rich",
|
||||
]
|
||||
files = [
|
||||
{file = "miservice_fork-2.6.0-py3-none-any.whl", hash = "sha256:98169a77ea41a7b9392e1b1fab8cb80a4165fed8a9e882d9ada9a16dd1120347"},
|
||||
{file = "miservice_fork-2.6.0.tar.gz", hash = "sha256:a59d337d1f7a92566aa147e96595a8d2f5bf3f7000ae5e7dd9ed451f18d6e2fd"},
|
||||
{file = "miservice_fork-2.6.1-py3-none-any.whl", hash = "sha256:9b2cc4208486bbbf788d1bde6e2cbc70f241ce10db4dca6f918076a2d2942a39"},
|
||||
{file = "miservice_fork-2.6.1.tar.gz", hash = "sha256:1702281e1e9827958eb3e82bc3242cd013c018e9aa1de8509b4805b5ccf5e60c"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -832,7 +832,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "yt-dlp"
|
||||
version = "2024.6.24.232830.dev0"
|
||||
version = "2024.7.1.232715.dev0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A feature-rich command-line audio/video downloader"
|
||||
dependencies = [
|
||||
@@ -846,6 +846,6 @@ dependencies = [
|
||||
"websockets>=12.0",
|
||||
]
|
||||
files = [
|
||||
{file = "yt_dlp-2024.6.24.232830.dev0-py3-none-any.whl", hash = "sha256:efffecef44ce688e9ee3c02226eb1ba4ad64b37744726e9e4df5c2bd04ea93c5"},
|
||||
{file = "yt_dlp-2024.6.24.232830.dev0.tar.gz", hash = "sha256:0e89b46958984954393692a8c41e0f6d76a773be2df381c3d3a4ff24ce89aa32"},
|
||||
{file = "yt_dlp-2024.7.1.232715.dev0-py3-none-any.whl", hash = "sha256:e9ab443353da0c8f01587b031fb84b2cc42eae82aeaa03a9ce5ed6edc301b503"},
|
||||
{file = "yt_dlp-2024.7.1.232715.dev0.tar.gz", hash = "sha256:4f1ab25318c9156cca0b7308bdd2aeb3e7f01e8d9fb83916b4719010038170c8"},
|
||||
]
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
[project]
|
||||
name = "xiaomusic"
|
||||
version = "0.1.81"
|
||||
version = "0.1.90"
|
||||
description = "Play Music with xiaomi AI speaker"
|
||||
authors = [
|
||||
{name = "涵曦", email = "im.hanxi@gmail.com"},
|
||||
]
|
||||
dependencies = [
|
||||
"requests>=2.31.0",
|
||||
"aiohttp>=3.8.6",
|
||||
"miservice-fork>=2.5.0",
|
||||
"mutagen>=1.47.0",
|
||||
"yt-dlp>=2024.04.09",
|
||||
"yt-dlp>=2024.07.01",
|
||||
"flask[async]>=3.0.1",
|
||||
"waitress>=3.0.0",
|
||||
"flask-HTTPAuth>=4.8.0",
|
||||
@@ -23,7 +22,7 @@ license = {text = "MIT"}
|
||||
Homepage = "https://github.com/hanxi/xiaomusic"
|
||||
|
||||
[project.scripts]
|
||||
xiaogpt = "xiaomusic.cli:main"
|
||||
xiaomusic = "xiaomusic.cli:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["pdm-backend"]
|
||||
|
||||
@@ -305,9 +305,9 @@ MarkupSafe==2.1.4 \
|
||||
mdurl==0.1.2 \
|
||||
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
|
||||
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
|
||||
miservice-fork==2.6.0 \
|
||||
--hash=sha256:98169a77ea41a7b9392e1b1fab8cb80a4165fed8a9e882d9ada9a16dd1120347 \
|
||||
--hash=sha256:a59d337d1f7a92566aa147e96595a8d2f5bf3f7000ae5e7dd9ed451f18d6e2fd
|
||||
miservice-fork==2.6.1 \
|
||||
--hash=sha256:1702281e1e9827958eb3e82bc3242cd013c018e9aa1de8509b4805b5ccf5e60c \
|
||||
--hash=sha256:9b2cc4208486bbbf788d1bde6e2cbc70f241ce10db4dca6f918076a2d2942a39
|
||||
multidict==6.0.4 \
|
||||
--hash=sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9 \
|
||||
--hash=sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8 \
|
||||
@@ -472,6 +472,6 @@ yarl==1.9.2 \
|
||||
--hash=sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9 \
|
||||
--hash=sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3 \
|
||||
--hash=sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560
|
||||
yt-dlp==2024.6.24.232830.dev0 \
|
||||
--hash=sha256:0e89b46958984954393692a8c41e0f6d76a773be2df381c3d3a4ff24ce89aa32 \
|
||||
--hash=sha256:efffecef44ce688e9ee3c02226eb1ba4ad64b37744726e9e4df5c2bd04ea93c5
|
||||
yt-dlp==2024.7.1.232715.dev0 \
|
||||
--hash=sha256:4f1ab25318c9156cca0b7308bdd2aeb3e7f01e8d9fb83916b4719010038170c8 \
|
||||
--hash=sha256:e9ab443353da0c8f01587b031fb84b2cc42eae82aeaa03a9ce5ed6edc301b503
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.1.81"
|
||||
__version__ = "0.1.90"
|
||||
|
||||
@@ -88,6 +88,13 @@ class Config:
|
||||
enable_fuzzy_match: bool = (
|
||||
os.getenv("XIAOMUSIC_ENABLE_FUZZY_MATCH", "true").lower() == "true"
|
||||
)
|
||||
stop_tts_msg: str = os.getenv("XIAOMUSIC_STOP_TTS_MSG", "收到,再见")
|
||||
|
||||
keywords_playlocal: str = os.getenv(
|
||||
"XIAOMUSIC_KEYWORDS_PLAYLOCAL", "播放本地歌曲,本地播放歌曲"
|
||||
)
|
||||
keywords_play: str = os.getenv("XIAOMUSIC_KEYWORDS_PLAY", "播放歌曲,放歌曲")
|
||||
keywords_stop: str = os.getenv("XIAOMUSIC_KEYWORDS_STOP", "关机,暂停,停止,停止播放")
|
||||
|
||||
def append_keyword(self, keys, action):
|
||||
for key in keys.split(","):
|
||||
@@ -98,14 +105,9 @@ class Config:
|
||||
def __post_init__(self) -> None:
|
||||
if self.proxy:
|
||||
validate_proxy(self.proxy)
|
||||
keywords_playlocal = os.getenv(
|
||||
"XIAOMUSIC_KEYWORDS_PLAYLOCAL", "播放本地歌曲,本地播放歌曲"
|
||||
)
|
||||
self.append_keyword(keywords_playlocal, "playlocal")
|
||||
keywords_play = os.getenv("XIAOMUSIC_KEYWORDS_PLAY", "播放歌曲,放歌曲")
|
||||
self.append_keyword(keywords_play, "play")
|
||||
keywords_stop = os.getenv("XIAOMUSIC_KEYWORDS_STOP", "关机,暂停,停止")
|
||||
self.append_keyword(keywords_stop, "stop")
|
||||
self.append_keyword(self.keywords_playlocal, "playlocal")
|
||||
self.append_keyword(self.keywords_play, "play")
|
||||
self.append_keyword(self.keywords_stop, "stop")
|
||||
|
||||
# 保存配置到 config-example.json 文件
|
||||
# with open("config-example.json", "w") as f:
|
||||
|
||||
@@ -98,7 +98,6 @@ async def do_cmd():
|
||||
@auth.login_required
|
||||
async def getsetting():
|
||||
config = xiaomusic.getconfig()
|
||||
log.debug(config)
|
||||
|
||||
alldevices = await xiaomusic.call_main_thread_function(xiaomusic.getalldevices)
|
||||
log.info(alldevices)
|
||||
@@ -177,6 +176,16 @@ async def playurl():
|
||||
return await xiaomusic.call_main_thread_function(xiaomusic.play_url, arg1=url)
|
||||
|
||||
|
||||
@app.route("/debug_play_by_music_url", methods=["POST"])
|
||||
@auth.login_required
|
||||
async def debug_play_by_music_url():
|
||||
data = request.get_json()
|
||||
log.info(f"data:{data}")
|
||||
return await xiaomusic.call_main_thread_function(
|
||||
xiaomusic.debug_play_by_music_url, arg1=data
|
||||
)
|
||||
|
||||
|
||||
def static_path_handler(filename):
|
||||
log.debug(filename)
|
||||
log.debug(static_path)
|
||||
|
||||
@@ -23,7 +23,7 @@ $(function(){
|
||||
// 拉取版本
|
||||
$.get("/getversion", function(data, status) {
|
||||
console.log(data, status, data["version"]);
|
||||
$("#version").text(`(${data.version})`);
|
||||
$("#version").text(`${data.version}`);
|
||||
});
|
||||
|
||||
// 拉取播放列表
|
||||
|
||||
40
xiaomusic/static/debug.html
Normal file
40
xiaomusic/static/debug.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Debug For XiaoMusic</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css">
|
||||
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||
<script src="/static/jquery-3.7.1.min.js"></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);
|
||||
}
|
||||
});
|
||||
}
|
||||
</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>
|
||||
</body>
|
||||
<footer>
|
||||
<p>Powered by <a href="https://github.com/hanxi/xiaomusic" target="_blank">xiaomusic</a></p>
|
||||
</footer>
|
||||
</html>
|
||||
@@ -8,7 +8,11 @@
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h2>小爱音箱操控面板<span id="version">(版本未知)</span></h2>
|
||||
<h2>小爱音箱操控面板
|
||||
(<a id="version" href="https://github.com/hanxi/xiaomusic/releases">
|
||||
版本未知
|
||||
</a>)
|
||||
</h2>
|
||||
<hr>
|
||||
<div id="cmds">
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h2>小爱音箱设置面板<span id="version">(版本未知)</span></h2>
|
||||
<h2>小爱音箱设置面板
|
||||
(<a id="version" href="https://github.com/hanxi/xiaomusic/releases">
|
||||
版本未知
|
||||
</a>)
|
||||
</h2>
|
||||
<hr>
|
||||
<div class="rows">
|
||||
<label for="mi_did">MI_DID:</label>
|
||||
@@ -35,6 +39,8 @@
|
||||
<hr>
|
||||
|
||||
<a href="/static/m3u.html" target="_blank">m3u文件转换工具</a>
|
||||
<hr>
|
||||
<a href="/static/debug.html" target="_blank">调试工具</a>
|
||||
|
||||
<footer>
|
||||
<p>Powered by <a href="https://github.com/hanxi/xiaomusic" target="_blank">xiaomusic</a></p>
|
||||
|
||||
@@ -2,7 +2,7 @@ $(function(){
|
||||
// 拉取版本
|
||||
$.get("/getversion", function(data, status) {
|
||||
console.log(data, status, data["version"]);
|
||||
$("#version").text(`(${data.version})`);
|
||||
$("#version").text(`${data.version}`);
|
||||
});
|
||||
|
||||
const updateSelectOptions = (selectId, optionsList, selectedOption) => {
|
||||
|
||||
@@ -34,7 +34,6 @@ from xiaomusic.utils import (
|
||||
find_best_match,
|
||||
fuzzyfinder,
|
||||
get_local_music_duration,
|
||||
get_random,
|
||||
get_web_music_duration,
|
||||
parse_cookie_string,
|
||||
walk_to_depth,
|
||||
@@ -107,9 +106,12 @@ class XiaoMusic:
|
||||
self.set_last_record("get_volume#")
|
||||
|
||||
def setup_logger(self):
|
||||
log_format = f"%(asctime)s [{__version__}] [%(levelname)s] %(message)s"
|
||||
date_format = "[%X]"
|
||||
formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
||||
logging.basicConfig(
|
||||
format=f"%(asctime)s [{__version__}] [%(levelname)s] %(message)s",
|
||||
datefmt="[%X]",
|
||||
format=log_format,
|
||||
datefmt=date_format,
|
||||
)
|
||||
|
||||
log_file = self.config.log_file
|
||||
@@ -121,6 +123,7 @@ class XiaoMusic:
|
||||
handler = RotatingFileHandler(
|
||||
self.config.log_file, maxBytes=10 * 1024 * 1024, backupCount=1
|
||||
)
|
||||
handler.setFormatter(formatter)
|
||||
self.log = logging.getLogger("xiaomusic")
|
||||
self.log.addHandler(handler)
|
||||
self.log.setLevel(logging.DEBUG if self.config.verbose else logging.INFO)
|
||||
@@ -284,20 +287,32 @@ class XiaoMusic:
|
||||
self.new_record_event.set()
|
||||
|
||||
async def do_tts(self, value):
|
||||
self.log.info("do_tts: %s", value)
|
||||
self.log.info(f"try do_tts value:{value}")
|
||||
if not value:
|
||||
self.log.info("do_tts no value")
|
||||
return
|
||||
|
||||
await self.force_stop_xiaoai()
|
||||
try:
|
||||
await self.mina_service.text_to_speech(self.device_id, value)
|
||||
except Exception as e:
|
||||
self.log.error(f"Execption {e}")
|
||||
# 最大等8秒
|
||||
sec = min(8, int(len(value) / 3.3))
|
||||
sec = min(8, int(len(value) / 3))
|
||||
await asyncio.sleep(sec)
|
||||
self.log.debug(f"do_tts. cur_music:{self.cur_music}")
|
||||
if self._playing and not self.is_downloading():
|
||||
self.log.info(f"do_tts ok. cur_music:{self.cur_music}")
|
||||
await self.check_replay()
|
||||
|
||||
# 继续播放被打断的歌曲
|
||||
async def check_replay(self):
|
||||
if self.isplaying() and not self.isdownloading():
|
||||
# 继续播放歌曲
|
||||
self.log.info("继续播放歌曲")
|
||||
self.log.info("现在继续播放歌曲")
|
||||
await self.play()
|
||||
else:
|
||||
self.log.info(
|
||||
f"不会继续播放歌曲. isplaying:{self.isplaying()} isdownloading:{self.isdownloading()}"
|
||||
)
|
||||
|
||||
async def do_set_volume(self, value):
|
||||
value = int(value)
|
||||
@@ -308,21 +323,40 @@ class XiaoMusic:
|
||||
except Exception as e:
|
||||
self.log.error(f"Execption {e}")
|
||||
|
||||
async def get_if_xiaoai_is_playing(self):
|
||||
playing_info = await self.mina_service.player_get_status(self.device_id)
|
||||
self.log.info(playing_info)
|
||||
# WTF xiaomi api
|
||||
is_playing = (
|
||||
json.loads(playing_info.get("data", {}).get("info", "{}")).get("status", -1)
|
||||
== 1
|
||||
)
|
||||
return is_playing
|
||||
|
||||
async def stop_if_xiaoai_is_playing(self):
|
||||
is_playing = await self.get_if_xiaoai_is_playing()
|
||||
if is_playing:
|
||||
# stop it
|
||||
ret = await self.mina_service.player_stop(self.device_id)
|
||||
self.log.info(f"force_stop_xiaoai player_stop ret:{ret}")
|
||||
|
||||
async def force_stop_xiaoai(self):
|
||||
ret = await self.mina_service.player_pause(self.device_id)
|
||||
self.log.debug(f"force_stop_xiaoai player_pause ret:{ret}")
|
||||
# ret = await self.mina_service.player_stop(self.device_id)
|
||||
# self.log.debug(f"force_stop_xiaoai player_stop ret:{ret}")
|
||||
self.log.info(f"force_stop_xiaoai player_pause ret:{ret}")
|
||||
await self.stop_if_xiaoai_is_playing()
|
||||
|
||||
# 是否在下载中
|
||||
def is_downloading(self):
|
||||
def isdownloading(self):
|
||||
if not self.download_proc:
|
||||
return False
|
||||
if (
|
||||
self.download_proc.returncode is not None
|
||||
and self.download_proc.returncode < 0
|
||||
):
|
||||
|
||||
if self.download_proc.returncode is not None:
|
||||
self.log.info(
|
||||
f"Process exited with returncode:{self.download_proc.returncode}"
|
||||
)
|
||||
return False
|
||||
|
||||
self.log.info("Download Process is still running.")
|
||||
return True
|
||||
|
||||
# 下载歌曲
|
||||
@@ -351,7 +385,8 @@ class XiaoMusic:
|
||||
if self.proxy:
|
||||
sbp_args += ("--proxy", f"{self.proxy}")
|
||||
|
||||
self.log.info(f"download: {sbp_args}")
|
||||
cmd = " ".join(sbp_args)
|
||||
self.log.info(f"download cmd: {cmd}")
|
||||
self.download_proc = await asyncio.create_subprocess_exec(*sbp_args)
|
||||
await self.do_tts(f"正在下载歌曲{search_key}")
|
||||
|
||||
@@ -476,6 +511,7 @@ class XiaoMusic:
|
||||
if not self.config.music_list_json:
|
||||
return
|
||||
|
||||
self._all_radio = {}
|
||||
music_list = json.loads(self.config.music_list_json)
|
||||
try:
|
||||
for item in music_list:
|
||||
@@ -551,7 +587,7 @@ class XiaoMusic:
|
||||
|
||||
if self._next_timer:
|
||||
self._next_timer.cancel()
|
||||
self.log.info("定时器已取消")
|
||||
self.log.info("旧定时器已取消")
|
||||
|
||||
self._timeout = sec
|
||||
|
||||
@@ -602,6 +638,7 @@ class XiaoMusic:
|
||||
opvalue, oparg = self.match_cmd(query, ctrl_panel)
|
||||
if not opvalue:
|
||||
await asyncio.sleep(1)
|
||||
await self.check_replay()
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -610,8 +647,26 @@ class XiaoMusic:
|
||||
except Exception as e:
|
||||
self.log.warning(f"执行出错 {str(e)}\n{traceback.format_exc()}")
|
||||
|
||||
# 检查是否匹配到完全一样的指令
|
||||
def check_full_match_cmd(self, query, ctrl_panel):
|
||||
if query in self.config.key_match_order:
|
||||
opkey = query
|
||||
opvalue = self.config.key_word_dict.get(opkey)
|
||||
if ctrl_panel or self.isplaying():
|
||||
return opvalue
|
||||
else:
|
||||
if not self.active_cmd or opvalue in self.active_cmd:
|
||||
return opvalue
|
||||
return None
|
||||
|
||||
# 匹配命令
|
||||
def match_cmd(self, query, ctrl_panel):
|
||||
# 优先处理完全匹配
|
||||
opvalue = self.check_full_match_cmd(query, ctrl_panel)
|
||||
if opvalue:
|
||||
self.log.info(f"完全匹配指令. query:{query} opvalue:{opvalue}")
|
||||
return (opvalue, "")
|
||||
|
||||
for opkey in self.config.key_match_order:
|
||||
patternarg = rf"(.*){opkey}(.*)"
|
||||
# 匹配参数
|
||||
@@ -629,20 +684,16 @@ class XiaoMusic:
|
||||
argafter,
|
||||
)
|
||||
oparg = argafter
|
||||
opvalue = self.config.key_word_dict.get(opkey)
|
||||
if not ctrl_panel and not self._playing:
|
||||
if self.active_cmd and opvalue not in self.active_cmd:
|
||||
self.log.debug(f"不在激活命令中 {opvalue}")
|
||||
continue
|
||||
if opkey in KEY_WORD_ARG_BEFORE_DICT:
|
||||
oparg = argpre
|
||||
self.log.info(
|
||||
"匹配到指令. opkey:%s opvalue:%s oparg:%s", opkey, opvalue, oparg
|
||||
)
|
||||
opvalue = self.config.key_word_dict.get(opkey)
|
||||
if not ctrl_panel and not self.isplaying():
|
||||
if self.active_cmd and opvalue not in self.active_cmd:
|
||||
self.log.ifno(f"不在激活命令中 {opvalue}")
|
||||
continue
|
||||
self.log.info(f"匹配到指令. opkey:{opkey} opvalue:{opvalue} oparg:{oparg}")
|
||||
return (opvalue, oparg)
|
||||
if self._playing:
|
||||
self.log.info("未匹配到指令,自动停止")
|
||||
return ("stop", "notts")
|
||||
self.log.info(f"未匹配到指令 {query} {ctrl_panel}")
|
||||
return (None, None)
|
||||
|
||||
# 判断是否播放下一首歌曲
|
||||
@@ -656,36 +707,16 @@ class XiaoMusic:
|
||||
return True
|
||||
return False
|
||||
|
||||
async def _play_by_music_url(self, device_id, url):
|
||||
audio_id = get_random(30)
|
||||
audio_type = ""
|
||||
if self.config.hardware in ["LX04", "X10A", "X08A"]:
|
||||
audio_type = "MUSIC"
|
||||
music = {
|
||||
"payload": {
|
||||
"audio_items": [
|
||||
{"item_id": {"audio_id": audio_id}, "stream": {"url": url}}
|
||||
],
|
||||
"audio_type": audio_type,
|
||||
}
|
||||
}
|
||||
return await self.mina_service.ubus_request(
|
||||
device_id,
|
||||
"player_play_music",
|
||||
"mediaplayer",
|
||||
{"startaudioid": audio_id, "music": json.dumps(music)},
|
||||
)
|
||||
|
||||
async def play_url(self, **kwargs):
|
||||
url = kwargs.get("arg1", "")
|
||||
if self.config.use_music_api:
|
||||
ret = await self._play_by_music_url(self.device_id, url)
|
||||
self.log.debug(
|
||||
ret = await self.play_by_music_url(self.device_id, url)
|
||||
self.log.info(
|
||||
f"play_url play_by_music_url {self.config.hardware}. ret:{ret} url:{url}"
|
||||
)
|
||||
else:
|
||||
ret = await self.mina_service.play_by_url(self.device_id, url)
|
||||
self.log.debug(
|
||||
self.log.info(
|
||||
f"play_url play_by_url {self.config.hardware}. ret:{ret} url:{url}"
|
||||
)
|
||||
return ret
|
||||
@@ -729,8 +760,8 @@ class XiaoMusic:
|
||||
self.cur_music = name
|
||||
self.log.info(f"cur_music {self.cur_music}")
|
||||
sec, url = await self.get_music_sec_url(name)
|
||||
self.log.info(f"播放 {url}")
|
||||
await self.force_stop_xiaoai()
|
||||
self.log.info(f"播放 {url}")
|
||||
await self.play_url(arg1=url)
|
||||
self.log.info("已经开始播放了")
|
||||
# 设置下一首歌曲的播放定时器
|
||||
@@ -855,11 +886,12 @@ class XiaoMusic:
|
||||
async def stop(self, **kwargs):
|
||||
self._playing = False
|
||||
if kwargs.get("arg1", "") != "notts":
|
||||
await self.do_tts("收到指令,再见")
|
||||
await self.do_tts(self.config.stop_tts_msg)
|
||||
if self._next_timer:
|
||||
self._next_timer.cancel()
|
||||
self.log.info("定时器已取消")
|
||||
await self.force_stop_xiaoai()
|
||||
self.log.info("stop now")
|
||||
|
||||
async def stop_after_minute(self, **kwargs):
|
||||
if self._stop_timer:
|
||||
@@ -1000,3 +1032,50 @@ class XiaoMusic:
|
||||
self.new_record_event.set()
|
||||
result = await future
|
||||
return result
|
||||
|
||||
async def play_by_music_url(self, deviceId, url, _type=2):
|
||||
self.log.info(f"play_by_music_url url:{url}, type:{_type}")
|
||||
audio_type = ""
|
||||
if _type == 1:
|
||||
# If set to MUSIC, the light will be on
|
||||
audio_type = "MUSIC"
|
||||
audio_id = "1582971365183456177"
|
||||
music = {
|
||||
"payload": {
|
||||
"audio_type": audio_type,
|
||||
"audio_items": [
|
||||
{
|
||||
"item_id": {
|
||||
"audio_id": audio_id,
|
||||
"cp": {
|
||||
"album_id": "-1",
|
||||
"episode_index": 0,
|
||||
"id": "372639235",
|
||||
"name": "xiaowei",
|
||||
},
|
||||
},
|
||||
"stream": {"url": url},
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
data = {"startaudioid": audio_id, "music": json.dumps(music)}
|
||||
self.log.info(json.dumps(data))
|
||||
return await self.mina_service.ubus_request(
|
||||
deviceId,
|
||||
"player_play_music",
|
||||
"mediaplayer",
|
||||
data,
|
||||
)
|
||||
|
||||
async def debug_play_by_music_url(self, arg1=None):
|
||||
if arg1 is None:
|
||||
arg1 = {}
|
||||
data = arg1
|
||||
self.log.info(f"debug_play_by_music_url: {data}")
|
||||
return await self.mina_service.ubus_request(
|
||||
self.device_id,
|
||||
"player_play_music",
|
||||
"mediaplayer",
|
||||
data,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user