mirror of
https://github.com/hanxi/xiaomusic.git
synced 2025-12-06 14:52:50 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c68fa71d24 | ||
|
|
24b46729e3 | ||
|
|
13336cd02c | ||
|
|
67be9125d8 | ||
|
|
1b0a6dd7f4 | ||
|
|
11053ecfa7 | ||
|
|
0736cb0fb7 | ||
|
|
2acb668101 | ||
|
|
32fb0e9548 |
36
README.md
36
README.md
@@ -51,12 +51,44 @@ docker run -e MI_USER=<your-xiaomi-account> -e MI_PASS=<your-xiaomi-password> -e
|
||||
- 注意端口必须映射为与容器内一致,XIAOMUSIC_HOSTNAME 需要设置为宿主机的 IP 地址,否则小爱无法正常播放。
|
||||
- 可以把 /app/music 目录映射到本地,用于保存下载的歌曲。
|
||||
|
||||
XIAOMUSIC_PROXY参数格式参考 yt-dlp 文档说明:
|
||||
```
|
||||
Use the specified HTTP/HTTPS/SOCKS proxy. To
|
||||
enable SOCKS proxy, specify a proper scheme,
|
||||
e.g. socks5://user:pass@127.0.0.1:1080/.
|
||||
Pass in an empty string (--proxy "") for
|
||||
direct connection
|
||||
```
|
||||
|
||||
### 本地编译Docker Image
|
||||
|
||||
```shell
|
||||
docker build -t xiaomusic .
|
||||
```
|
||||
|
||||
### docker compose 示例
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
xiaomusic:
|
||||
image: hanxi/xiaomusic
|
||||
container_name: xiaomusic
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8090:8090
|
||||
volumes:
|
||||
- ./music:/app/music
|
||||
environment:
|
||||
- MI_USER: '小米账号'
|
||||
- MI_PASS: '小米密码'
|
||||
- MI_DID: 00000
|
||||
- MI_HARDWARE: 'L07A'
|
||||
- XIAOMUSIC_PROXY: 'http://192.168.2.5:8080'
|
||||
- XIAOMUSIC_HOSTNAME: '192.168.2.5'
|
||||
```
|
||||
|
||||
|
||||
## 简易的控制面板
|
||||
|
||||
浏览器进入 <http://192.168.2.5:8090>
|
||||
@@ -71,3 +103,7 @@ docker build -t xiaomusic .
|
||||
- [xiaogpt](https://github.com/yihong0618/xiaogpt)
|
||||
- [yt-dlp](https://github.com/yt-dlp/yt-dlp)
|
||||
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#hanxi/xiaomusic&Date)
|
||||
|
||||
@@ -4,4 +4,4 @@ git diff
|
||||
git add ./pyproject.toml
|
||||
git commit -m "new version v$version"
|
||||
git tag v$version
|
||||
#git push --tags
|
||||
git push -u origin main --tags
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "xiaomusic"
|
||||
version = "0.1.11"
|
||||
version = "0.1.13"
|
||||
description = "Play Music with xiaomi AI speaker"
|
||||
authors = [
|
||||
{name = "涵曦", email = "im.hanxi@gmail.com"},
|
||||
|
||||
@@ -31,7 +31,7 @@ HARDWARE_COMMAND_DICT = {
|
||||
# add more here
|
||||
}
|
||||
|
||||
DEFAULT_COMMAND = ("5-1", "5-5")
|
||||
DEFAULT_COMMAND = ("5-1", "5-5", "2-1")
|
||||
|
||||
KEY_WORD_DICT = {
|
||||
"播放歌曲": "play",
|
||||
@@ -79,12 +79,15 @@ class Config:
|
||||
mi_did: str = os.getenv("MI_DID", "")
|
||||
mute_xiaoai: bool = True
|
||||
cookie: str = ""
|
||||
use_command: bool = True
|
||||
use_command: bool = False
|
||||
verbose: bool = False
|
||||
music_path: str = os.getenv("XIAOMUSIC_MUSIC_PATH", "music")
|
||||
hostname: str = os.getenv("XIAOMUSIC_HOSTNAME", "192.168.2.5")
|
||||
port: int = int(os.getenv("XIAOMUSIC_PORT", "8090"))
|
||||
proxy: str | None = os.getenv("XIAOMUSIC_PROXY", None)
|
||||
search_prefix: str = os.getenv(
|
||||
"XIAOMUSIC_SEARCH", "ytsearch:"
|
||||
) # "bilisearch:" or "ytsearch:"
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.proxy:
|
||||
|
||||
@@ -22,6 +22,13 @@ def allcmds():
|
||||
return KEY_WORD_DICT
|
||||
|
||||
|
||||
@app.route("/getvolume")
|
||||
def getvolume():
|
||||
return {
|
||||
"volume": xiaomusic.get_volume(),
|
||||
}
|
||||
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def redirect_to_index():
|
||||
return send_from_directory("static", "index.html")
|
||||
|
||||
@@ -1,69 +1,55 @@
|
||||
$(function(){
|
||||
// 拉取所有可操作的命令
|
||||
$.get("/allcmds", function(data, status) {
|
||||
console.log(data, status);
|
||||
$container=$("#cmds");
|
||||
append_op_button_name("下一首");
|
||||
append_op_button_name("全部循环");
|
||||
append_op_button_name("关机");
|
||||
append_op_button_name("单曲循环");
|
||||
append_op_button_name("播放歌曲");
|
||||
append_op_button_name("随机播放");
|
||||
|
||||
$container=$("#cmds");
|
||||
// 遍历数据
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (key != "分钟后关机"
|
||||
&& key != "放歌曲"
|
||||
&& key != "停止播放"
|
||||
&& !key.includes("#")) {
|
||||
append_op_button_name(key);
|
||||
}
|
||||
}
|
||||
$container.append($("<hr>"));
|
||||
|
||||
$container.append($("<hr>"));
|
||||
append_op_button_name("10分钟后关机");
|
||||
append_op_button_name("30分钟后关机");
|
||||
append_op_button_name("60分钟后关机");
|
||||
append_op_button_name("10分钟后关机");
|
||||
append_op_button_name("30分钟后关机");
|
||||
append_op_button_name("60分钟后关机");
|
||||
|
||||
$container.append($("<hr>"));
|
||||
append_op_button_volume("声音设为5", 5);
|
||||
append_op_button_volume("声音设为10", 10);
|
||||
append_op_button_volume("声音设为30", 30);
|
||||
append_op_button_volume("声音设为50", 50);
|
||||
append_op_button_volume("声音设为80", 80);
|
||||
append_op_button_volume("声音设为100", 100);
|
||||
// 拉取声音
|
||||
$.get("/getvolume", function(data, status) {
|
||||
console.log(data, status, data["volume"]);
|
||||
$("#volume").val(data.volume);
|
||||
});
|
||||
|
||||
function append_op_button_volume(name, value) {
|
||||
append_op_button(name, "set_volume#"+value);
|
||||
}
|
||||
function append_op_button_name(name) {
|
||||
append_op_button(name, name);
|
||||
}
|
||||
|
||||
function append_op_button(name, cmd) {
|
||||
// 创建按钮
|
||||
const $button = $("<button>");
|
||||
$button.text(name);
|
||||
$button.attr("type", "button");
|
||||
// 创建按钮
|
||||
const $button = $("<button>");
|
||||
$button.text(name);
|
||||
$button.attr("type", "button");
|
||||
|
||||
// 设置按钮点击事件
|
||||
$button.on("click", () => {
|
||||
// 发起post请求
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/cmd",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({"cmd": cmd}),
|
||||
success: () => {
|
||||
// 请求成功时执行的操作
|
||||
},
|
||||
error: () => {
|
||||
// 请求失败时执行的操作
|
||||
}
|
||||
});
|
||||
});
|
||||
// 设置按钮点击事件
|
||||
$button.on("click", () => {
|
||||
sendcmd(cmd);
|
||||
});
|
||||
|
||||
// 添加按钮到容器
|
||||
$container.append($button);
|
||||
// 添加按钮到容器
|
||||
$container.append($button);
|
||||
}
|
||||
|
||||
$("#play").on("click", () => {
|
||||
name = $("#music-name").val();
|
||||
let cmd = "播放歌曲"+name;
|
||||
sendcmd(cmd);
|
||||
});
|
||||
|
||||
$("#volume").on('input', function () {
|
||||
var value = $(this).val();
|
||||
sendcmd("set_volume#"+value);
|
||||
});
|
||||
|
||||
function sendcmd(cmd) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/cmd",
|
||||
@@ -76,5 +62,5 @@ $(function(){
|
||||
// 请求失败时执行的操作
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,13 +10,18 @@
|
||||
margin: 10px;
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
border: none; /* 无边框 */
|
||||
color: white; /* 白色文字 */
|
||||
text-align: center; /* 文字居中 */
|
||||
text-decoration: none; /* 无下划线 */
|
||||
display: inline-block; /* 行内块元素 */
|
||||
border-radius: 10px; /* 圆角 */
|
||||
background-color: #008CBA; /* 蓝色 */
|
||||
border: none;
|
||||
color: white;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
border-radius: 10px;
|
||||
background-color: #008CBA;
|
||||
}
|
||||
button:active {
|
||||
font-weight:bold;
|
||||
background-color: #007CBA;
|
||||
transform: translateY(2px);
|
||||
}
|
||||
input {
|
||||
margin: 10px;
|
||||
@@ -31,6 +36,13 @@
|
||||
<div id="cmds">
|
||||
</div>
|
||||
<hr>
|
||||
<div style="margin-left: 20px;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="#8e43e7" style="height: 48px; width: 48px;"><path d="M550.826667 154.666667a47.786667 47.786667 0 0 0-19.84 4.48L298.666667 298.666667H186.453333A80 80 0 0 0 106.666667 378.453333v267.093334A80 80 0 0 0 186.453333 725.333333H298.666667l232.32 139.52a47.786667 47.786667 0 0 0 19.84 4.48A46.506667 46.506667 0 0 0 597.333333 822.826667V201.173333a46.506667 46.506667 0 0 0-46.506666-46.506666zM554.666667 822.826667c0 3.413333-3.84 3.84-3.84 3.84L320 688.853333l-9.6-6.186666H186.453333A37.12 37.12 0 0 1 149.333333 645.546667V378.453333A37.12 37.12 0 0 1 186.453333 341.333333h123.946667l10.24-6.186666 229.546667-137.6s3.84 0 3.84 3.84zM667.52 346.026667a21.333333 21.333333 0 0 0 0 30.293333 192 192 0 0 1 0 271.36 21.333333 21.333333 0 0 0 0 30.293333 21.333333 21.333333 0 0 0 30.293333 0 234.666667 234.666667 0 0 0 0-331.946666 21.333333 21.333333 0 0 0-30.293333 0z"></path><path d="M804.48 219.52a21.333333 21.333333 0 0 0-30.293333 30.293333 370.986667 370.986667 0 0 1 0 524.373334 21.333333 21.333333 0 0 0 0 30.293333 21.333333 21.333333 0 0 0 30.293333 0 414.08 414.08 0 0 0 0-584.96z"></path></svg>
|
||||
<input id="volume" type="range"></input>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
<input id="music-name" type="text" placeholder="请输入歌曲名称"></input>
|
||||
<button id="play">播放</button>
|
||||
|
||||
@@ -56,6 +56,7 @@ class XiaoMusic:
|
||||
self.hostname = config.hostname
|
||||
self.port = config.port
|
||||
self.proxy = config.proxy
|
||||
self.search_prefix = config.search_prefix
|
||||
|
||||
# 下载对象
|
||||
self.download_proc = None
|
||||
@@ -64,6 +65,7 @@ class XiaoMusic:
|
||||
self.cur_music = ""
|
||||
self._next_timer = None
|
||||
self._timeout = 0
|
||||
self._volume = 50
|
||||
|
||||
# 关机定时器
|
||||
self._stop_timer = None
|
||||
@@ -237,16 +239,19 @@ class XiaoMusic:
|
||||
await self.wait_for_tts_finish()
|
||||
|
||||
async def do_set_volume(self, value):
|
||||
value = int(value)
|
||||
if not self.config.use_command:
|
||||
try:
|
||||
self.log.debug("do_set_volume not use_command value:%d", value)
|
||||
await self.mina_service.player_set_volume(self.device_id, value)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
self.log.debug("do_set_volume use_command value:%d", value)
|
||||
await miio_command(
|
||||
self.miio_service,
|
||||
self.config.mi_did,
|
||||
f"{self.config.volume_command} {value}",
|
||||
f"{self.config.volume_command}=#{value}",
|
||||
)
|
||||
|
||||
async def wait_for_tts_finish(self):
|
||||
@@ -295,7 +300,7 @@ class XiaoMusic:
|
||||
|
||||
sbp_args = (
|
||||
"yt-dlp",
|
||||
f"ytsearch:{name}",
|
||||
f"{self.search_prefix}{name}",
|
||||
"-x",
|
||||
"--audio-format",
|
||||
"mp3",
|
||||
@@ -512,4 +517,8 @@ class XiaoMusic:
|
||||
async def set_volume(self, **kwargs):
|
||||
value = kwargs["arg1"]
|
||||
await self.do_set_volume(value)
|
||||
self._volume = int(value)
|
||||
self.log.info(f"声音设置为{value}")
|
||||
|
||||
def get_volume(self):
|
||||
return self._volume
|
||||
|
||||
Reference in New Issue
Block a user