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

Compare commits

..

40 Commits

Author SHA1 Message Date
涵曦
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
16 changed files with 321 additions and 111 deletions

View File

@@ -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

View File

@@ -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 | [小爱音箱Play2019款](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 用来播放本地歌曲的口令前缀,本地找不到时不会下载歌曲,默认是 "播放本地歌曲,本地播放歌曲" ,可以用英文逗号分割配置多个。

View File

@@ -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": "关机,暂停,停止,停止播放"
}

View File

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

@@ -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"},
]

View File

@@ -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"]

View File

@@ -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

View File

@@ -1 +1 @@
__version__ = "0.1.81"
__version__ = "0.1.90"

View File

@@ -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:

View File

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

View File

@@ -23,7 +23,7 @@ $(function(){
// 拉取版本
$.get("/getversion", function(data, status) {
console.log(data, status, data["version"]);
$("#version").text(`(${data.version})`);
$("#version").text(`${data.version}`);
});
// 拉取播放列表

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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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) => {

View File

@@ -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,
)