mirror of
https://github.com/hanxi/xiaomusic.git
synced 2025-12-06 14:52:50 +08:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76aeb20268 | ||
|
|
de36ff7d24 | ||
|
|
4b5d5b3a0a | ||
|
|
59f150ebbd | ||
|
|
4e2d39abac | ||
|
|
a35cde5d4a | ||
|
|
bbdf41f334 | ||
|
|
529aedede0 | ||
|
|
55eaa6e751 | ||
|
|
ca59e594b4 | ||
|
|
836fde01b7 | ||
|
|
050ded6b2e | ||
|
|
b9e1abff6b | ||
|
|
f962fcaa99 | ||
|
|
eb35da595f | ||
|
|
26a8b2412f | ||
|
|
601bff4404 | ||
|
|
1fadc9a479 | ||
|
|
69e53569ca | ||
|
|
49a43fb997 | ||
|
|
5145ae399d | ||
|
|
612eb636be | ||
|
|
657af667ef | ||
|
|
9541071c3a | ||
|
|
dfc78b6af5 | ||
|
|
bea7b2d4eb | ||
|
|
ddb3e9e03b | ||
|
|
812965f054 | ||
|
|
006ea2d283 | ||
|
|
08a22ca03f | ||
|
|
7a32917b63 | ||
|
|
54b4417069 | ||
|
|
17d7f54c20 | ||
|
|
2261b5ba53 | ||
|
|
833cb1a24a | ||
|
|
4494b54c15 | ||
|
|
594db5aa0c |
30
.github/workflows/ci.yml
vendored
Normal file
30
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
runs-on: ubuntu-latest
|
||||
# run unless event type is pull_request
|
||||
if: github.event_name != 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ secrets.DOCKERHUB_USERNAME }}/xiaomusic:${{ github.ref_name }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -163,3 +163,4 @@ cython_debug/
|
||||
|
||||
ffmpeg
|
||||
music
|
||||
test.sh
|
||||
|
||||
38
README.md
38
README.md
@@ -43,6 +43,10 @@ pdm run xiaomusic.py
|
||||
|
||||
> 本地音乐会搜索 mp3 和 flac 格式的文件,下载的歌曲是 mp3 格式的。
|
||||
|
||||
## 其他参数
|
||||
|
||||
- XIAOMUSIC_ACTIVE_CMD 环境变量,配置成'play,random_play',在非播放状态下,只有这两个指令(播放歌曲和随机播放)可以触发,触发后,xiaomusic进入playing状态,其他指令则可以正常触发。
|
||||
|
||||
## 在 Docker 里使用
|
||||
|
||||
```shell
|
||||
@@ -67,7 +71,6 @@ direct connection
|
||||
|
||||
见 <https://github.com/hanxi/xiaomusic/issues/2> 和 <https://github.com/hanxi/xiaomusic/issues/11>
|
||||
|
||||
|
||||
### 本地编译Docker Image
|
||||
|
||||
```shell
|
||||
@@ -91,12 +94,12 @@ services:
|
||||
volumes:
|
||||
- ./music:/app/music
|
||||
environment:
|
||||
- MI_USER: '小米账号'
|
||||
- MI_PASS: '小米密码'
|
||||
- MI_DID: 00000
|
||||
- MI_HARDWARE: 'L07A'
|
||||
- XIAOMUSIC_SEARCH: 'bilisearch:'
|
||||
- XIAOMUSIC_HOSTNAME: '192.168.2.5'
|
||||
MI_USER: '小米账号'
|
||||
MI_PASS: '小米密码'
|
||||
MI_DID: 00000
|
||||
MI_HARDWARE: 'L07A'
|
||||
XIAOMUSIC_SEARCH: 'bilisearch:'
|
||||
XIAOMUSIC_HOSTNAME: '192.168.2.5'
|
||||
```
|
||||
|
||||
|
||||
@@ -115,13 +118,13 @@ services:
|
||||
volumes:
|
||||
- ./music:/app/music
|
||||
environment:
|
||||
- MI_USER: '小米账号'
|
||||
- MI_PASS: '小米密码'
|
||||
- MI_DID: 00000
|
||||
- MI_HARDWARE: 'L07A'
|
||||
- XIAOMUSIC_SEARCH: 'ytsearch:'
|
||||
- XIAOMUSIC_PROXY: 'http://192.168.2.5:8080'
|
||||
- XIAOMUSIC_HOSTNAME: '192.168.2.5'
|
||||
MI_USER: '小米账号'
|
||||
MI_PASS: '小米密码'
|
||||
MI_DID: 00000
|
||||
MI_HARDWARE: 'L07A'
|
||||
XIAOMUSIC_SEARCH: 'ytsearch:'
|
||||
XIAOMUSIC_PROXY: 'http://192.168.2.5:8080'
|
||||
XIAOMUSIC_HOSTNAME: '192.168.2.5'
|
||||
```
|
||||
|
||||
|
||||
@@ -131,7 +134,9 @@ services:
|
||||
|
||||
- ip 是 XIAOMUSIC_HOSTNAME 设置的
|
||||
- 8090 是默认端口
|
||||
|
||||
- 新功能
|
||||
- 显示正在播放的歌曲
|
||||
- 模糊搜索本地歌曲
|
||||
|
||||
## 感谢
|
||||
|
||||
@@ -140,7 +145,8 @@ services:
|
||||
- [xiaogpt](https://github.com/yihong0618/xiaogpt)
|
||||
- [MiService](https://github.com/yihong0618/MiService)
|
||||
- [yt-dlp](https://github.com/yt-dlp/yt-dlp)
|
||||
|
||||
- [NAS部署教程](https://post.m.smzdm.com/p/avpe7n99/)
|
||||
- [群晖部署教程](https://post.m.smzdm.com/p/a7px7dol/)
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
37
newversion.sh
Normal file → Executable file
37
newversion.sh
Normal file → Executable file
@@ -1,7 +1,34 @@
|
||||
version="$1"
|
||||
sed -i "s/version.*/version = \"$version\"/" ./pyproject.toml
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
version_file=./pyproject.toml
|
||||
# 获取当前版本号
|
||||
current_version=$(grep -oE "version = \"[0-9]+\.[0-9]+\.[0-9]+\"" $version_file | cut -d'"' -f2)
|
||||
echo "当前版本号: "$current_version
|
||||
|
||||
# 将版本号分割成三部分
|
||||
major=$(echo $current_version | cut -d'.' -f1)
|
||||
minor=$(echo $current_version | cut -d'.' -f2)
|
||||
patch=$(echo $current_version | cut -d'.' -f3)
|
||||
|
||||
echo "major: $major"
|
||||
echo "minor: $minor"
|
||||
echo "patch: $patch"
|
||||
|
||||
# 将补丁号加1
|
||||
patch=$((patch + 1))
|
||||
|
||||
# 生成新版本号
|
||||
new_version="$major.$minor.$patch"
|
||||
|
||||
# 将新版本号写入文件
|
||||
sed -i "s/version.*/version = \"$new_version\"/g" $version_file
|
||||
|
||||
echo "新版本号:$new_version"
|
||||
|
||||
git diff
|
||||
git add ./pyproject.toml
|
||||
git commit -m "new version v$version"
|
||||
git tag v$version
|
||||
git add $version_file
|
||||
git commit -m "new version v$new_version"
|
||||
git tag v$new_version
|
||||
git push -u origin main --tags
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "xiaomusic"
|
||||
version = "0.1.14"
|
||||
version = "0.1.26"
|
||||
description = "Play Music with xiaomi AI speaker"
|
||||
authors = [
|
||||
{name = "涵曦", email = "im.hanxi@gmail.com"},
|
||||
|
||||
@@ -398,6 +398,6 @@ yarl==1.9.2 \
|
||||
--hash=sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9 \
|
||||
--hash=sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3 \
|
||||
--hash=sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560
|
||||
yt-dlp==2023.12.30 \
|
||||
--hash=sha256:a11862e57721b0a0f0883dfeb5a4d79ba213a2d4c45e1880e9fd70f8e6570c38 \
|
||||
--hash=sha256:c00d9a71d64472ad441bcaa1ec0c3797d6e60c9f934f270096a96fe51657e7b3
|
||||
yt-dlp==2024.2.2.232707.dev0 \
|
||||
--hash=sha256:7ff05434979d89e2a7c344d6771cc60229901fe4182c19a756472bb49a50e3b8 \
|
||||
--hash=sha256:fd8d85f64842038cbdaaf7ee5812d98beed64fdcb4612f6e80b2c721bad13262
|
||||
|
||||
@@ -53,6 +53,11 @@ def main():
|
||||
dest="config",
|
||||
help="config file path",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ffmpeg_location",
|
||||
dest="ffmpeg_location",
|
||||
help="ffmpeg bin path",
|
||||
)
|
||||
|
||||
options = parser.parse_args()
|
||||
config = Config.from_options(options)
|
||||
|
||||
@@ -66,8 +66,8 @@ KEY_MATCH_ORDER = [
|
||||
]
|
||||
|
||||
SUPPORT_MUSIC_TYPE = [
|
||||
"mp3",
|
||||
"flac",
|
||||
".mp3",
|
||||
".flac",
|
||||
]
|
||||
|
||||
|
||||
@@ -88,6 +88,8 @@ class Config:
|
||||
search_prefix: str = os.getenv(
|
||||
"XIAOMUSIC_SEARCH", "ytsearch:"
|
||||
) # "bilisearch:" or "ytsearch:"
|
||||
ffmpeg_location: str = os.getenv("XIAOMUSIC_FFMPEG_LOCATION", "./ffmpeg/bin")
|
||||
active_cmd: str = os.getenv("XIAOMUSIC_ACTIVE_CMD", "play,random_play")
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.proxy:
|
||||
|
||||
@@ -22,12 +22,20 @@ def allcmds():
|
||||
return KEY_WORD_DICT
|
||||
|
||||
|
||||
@app.route("/getvolume")
|
||||
@app.route("/getvolume", methods=["GET"])
|
||||
def getvolume():
|
||||
return {
|
||||
"volume": xiaomusic.get_volume(),
|
||||
}
|
||||
|
||||
@app.route("/searchmusic", methods=["GET"])
|
||||
def searchmusic():
|
||||
name = request.args.get('name')
|
||||
return xiaomusic.searchmusic(name)
|
||||
|
||||
@app.route("/playingmusic", methods=["GET"])
|
||||
def playingmusic():
|
||||
return xiaomusic.playingmusic()
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def redirect_to_index():
|
||||
|
||||
@@ -39,8 +39,9 @@ $(function(){
|
||||
}
|
||||
|
||||
$("#play").on("click", () => {
|
||||
name = $("#music-name").val();
|
||||
let cmd = "播放歌曲"+name;
|
||||
var search_key = $("#music-name").val();
|
||||
var filename=$("#music-filename").val();
|
||||
let cmd = "播放歌曲"+search_key+"|"+filename;
|
||||
sendcmd(cmd);
|
||||
});
|
||||
|
||||
@@ -63,4 +64,39 @@ $(function(){
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 监听输入框的输入事件
|
||||
$("#music-name").on('input', function() {
|
||||
var inputValue = $(this).val();
|
||||
// 发送Ajax请求
|
||||
$.ajax({
|
||||
url: "searchmusic", // 服务器端处理脚本
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
data: {
|
||||
name: inputValue
|
||||
},
|
||||
success: function(data) {
|
||||
// 清空datalist
|
||||
$("#autocomplete-list").empty();
|
||||
// 添加新的option元素
|
||||
$.each(data, function(i, item) {
|
||||
$('<option>').val(item).appendTo("#autocomplete-list");
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function get_playing_music() {
|
||||
$.get("/playingmusic", function(data, status) {
|
||||
console.log(data);
|
||||
$("#playering-music").text(data);
|
||||
});
|
||||
}
|
||||
|
||||
// 每3秒获取下正在播放的音乐
|
||||
get_playing_music();
|
||||
setInterval(() => {
|
||||
get_playing_music();
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
@@ -25,9 +25,38 @@
|
||||
}
|
||||
input {
|
||||
margin: 10px;
|
||||
width: 200px;
|
||||
width: 300px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.container{
|
||||
width: 280px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
@keyframes text-scroll {
|
||||
0% {
|
||||
left: 100%;
|
||||
}
|
||||
25% {
|
||||
left: 50%;
|
||||
}
|
||||
50% {
|
||||
left: 0%;
|
||||
}
|
||||
75% {
|
||||
left: -50%;
|
||||
}
|
||||
100% {
|
||||
left: -100%;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
animation: text-scroll 10s linear infinite;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -44,8 +73,13 @@
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
<input id="music-name" type="text" placeholder="请输入歌曲名称"></input>
|
||||
<button id="play">播放</button>
|
||||
<datalist id="autocomplete-list"></datalist>
|
||||
<input id="music-name" type="text" placeholder="请输入搜索关键词(如:MV高清版 周杰伦 七里香)" list="autocomplete-list"></input>
|
||||
<input id="music-filename" type="text" placeholder="请输入保存为的文件名称(如:周杰伦七里香)"></input>
|
||||
</div>
|
||||
<button id="play">播放</button>
|
||||
<div class="container">
|
||||
<div id="playering-music" class="text"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,6 +7,7 @@ import socket
|
||||
from http.cookies import SimpleCookie
|
||||
from typing import AsyncIterator
|
||||
from urllib.parse import urlparse
|
||||
import difflib
|
||||
|
||||
from requests.utils import cookiejar_from_dict
|
||||
|
||||
@@ -61,3 +62,6 @@ def validate_proxy(proxy_str: str) -> bool:
|
||||
|
||||
return True
|
||||
|
||||
# 模糊搜索
|
||||
def fuzzyfinder(user_input, collection):
|
||||
return difflib.get_close_matches(user_input, collection, 10, cutoff=0.1)
|
||||
|
||||
@@ -30,6 +30,7 @@ from xiaomusic.config import (
|
||||
from xiaomusic.utils import (
|
||||
calculate_tts_elapse,
|
||||
parse_cookie_string,
|
||||
fuzzyfinder,
|
||||
)
|
||||
|
||||
EOF = object()
|
||||
@@ -57,6 +58,8 @@ class XiaoMusic:
|
||||
self.port = config.port
|
||||
self.proxy = config.proxy
|
||||
self.search_prefix = config.search_prefix
|
||||
self.ffmpeg_location = config.ffmpeg_location
|
||||
self.active_cmd = config.active_cmd.split(",")
|
||||
|
||||
# 下载对象
|
||||
self.download_proc = None
|
||||
@@ -66,6 +69,9 @@ class XiaoMusic:
|
||||
self._next_timer = None
|
||||
self._timeout = 0
|
||||
self._volume = 50
|
||||
self._all_music = {}
|
||||
self._play_list = []
|
||||
self._playing = False
|
||||
|
||||
# 关机定时器
|
||||
self._stop_timer = None
|
||||
@@ -76,20 +82,25 @@ class XiaoMusic:
|
||||
self.log.addHandler(RichHandler())
|
||||
self.log.debug(config)
|
||||
|
||||
# 启动时重新生成一次播放列表
|
||||
self.gen_all_music_list()
|
||||
|
||||
self.log.debug("ffmpeg_location: %s", self.ffmpeg_location)
|
||||
|
||||
async def poll_latest_ask(self):
|
||||
async with ClientSession() as session:
|
||||
session._cookie_jar = self.cookie_jar
|
||||
while True:
|
||||
# self.log.debug(
|
||||
# "Listening new message, timestamp: %s", self.last_timestamp
|
||||
# )
|
||||
self.log.debug(
|
||||
"Listening new message, timestamp: %s", self.last_timestamp
|
||||
)
|
||||
await self.get_latest_ask_from_xiaoai(session)
|
||||
start = time.perf_counter()
|
||||
# self.log.debug("Polling_event, timestamp: %s", self.last_timestamp)
|
||||
self.log.debug("Polling_event, timestamp: %s", self.last_timestamp)
|
||||
await self.polling_event.wait()
|
||||
if (d := time.perf_counter() - start) < 1:
|
||||
# sleep to avoid too many request
|
||||
# self.log.debug("Sleep %f, timestamp: %s", d, self.last_timestamp)
|
||||
self.log.debug("Sleep %f, timestamp: %s", d, self.last_timestamp)
|
||||
await asyncio.sleep(1 - d)
|
||||
|
||||
async def init_all_data(self, session):
|
||||
@@ -210,6 +221,7 @@ class XiaoMusic:
|
||||
def set_last_record(self, query):
|
||||
self.last_record = {
|
||||
"query": query,
|
||||
"ctrl_panel": True,
|
||||
}
|
||||
self.new_record_event.set()
|
||||
|
||||
@@ -291,7 +303,7 @@ class XiaoMusic:
|
||||
return True
|
||||
|
||||
# 下载歌曲
|
||||
async def download(self, name):
|
||||
async def download(self, search_key, name):
|
||||
if self.download_proc:
|
||||
try:
|
||||
self.download_proc.kill()
|
||||
@@ -300,7 +312,7 @@ class XiaoMusic:
|
||||
|
||||
sbp_args = (
|
||||
"yt-dlp",
|
||||
f"{self.search_prefix}{name}",
|
||||
f"{self.search_prefix}{search_key}",
|
||||
"-x",
|
||||
"--audio-format",
|
||||
"mp3",
|
||||
@@ -309,7 +321,7 @@ class XiaoMusic:
|
||||
"-o",
|
||||
f"{name}.mp3",
|
||||
"--ffmpeg-location",
|
||||
"./ffmpeg/bin",
|
||||
f"{self.ffmpeg_location}",
|
||||
"--no-playlist",
|
||||
)
|
||||
|
||||
@@ -317,44 +329,76 @@ class XiaoMusic:
|
||||
sbp_args += ("--proxy", f"{self.proxy}")
|
||||
|
||||
self.download_proc = await asyncio.create_subprocess_exec(*sbp_args)
|
||||
await self.do_tts(f"正在下载歌曲{name}")
|
||||
|
||||
def get_filename(self, name):
|
||||
filename = os.path.join(self.music_path, name)
|
||||
return filename
|
||||
await self.do_tts(f"正在下载歌曲{search_key}")
|
||||
|
||||
# 本地是否存在歌曲
|
||||
def local_exist(self, name):
|
||||
for tp in SUPPORT_MUSIC_TYPE:
|
||||
filename = self.get_filename(f"{name}.{tp}")
|
||||
self.log.debug("try local_exist. filename:%s", filename)
|
||||
if os.path.exists(filename):
|
||||
return filename
|
||||
def get_filename(self, name):
|
||||
if name not in self._all_music:
|
||||
self.log.debug("get_filename not in. name:%s", name)
|
||||
return ""
|
||||
filename = self._all_music[name]
|
||||
self.log.debug("try get_filename. filename:%s", filename)
|
||||
if os.path.exists(filename):
|
||||
return filename
|
||||
return ""
|
||||
|
||||
# 获取歌曲播放地址
|
||||
def get_file_url(self, filename):
|
||||
self.log.debug("get_file_url. filename:%s", filename)
|
||||
def get_file_url(self, name):
|
||||
filename = self.get_filename(name)
|
||||
self.log.debug("get_file_url. name:%s, filename:%s", name, filename)
|
||||
encoded_name = urllib.parse.quote(filename)
|
||||
return f"http://{self.hostname}:{self.port}/{encoded_name}"
|
||||
|
||||
# 随机获取一首音乐
|
||||
def random_music(self):
|
||||
files = os.listdir(self.music_path)
|
||||
# 过滤音乐文件
|
||||
music_files = []
|
||||
for file in files:
|
||||
for tp in SUPPORT_MUSIC_TYPE:
|
||||
if file.endswith(f".{tp}"):
|
||||
music_files.append(file)
|
||||
# 递归获取目录下所有歌曲,生成随机播放列表
|
||||
def gen_all_music_list(self):
|
||||
self._all_music = {}
|
||||
for root, dirs, filenames in os.walk(self.music_path):
|
||||
for filename in filenames:
|
||||
self.log.debug("gen_all_music_list. filename:%s", filename)
|
||||
# 过滤隐藏文件
|
||||
if filename.startswith("."):
|
||||
continue
|
||||
# 过滤非音乐文件
|
||||
(name, extension) = os.path.splitext(filename)
|
||||
self.log.debug(
|
||||
"gen_all_music_list. filename:%s, name:%s, extension:%s",
|
||||
filename,
|
||||
name,
|
||||
extension,
|
||||
)
|
||||
if extension not in SUPPORT_MUSIC_TYPE:
|
||||
continue
|
||||
|
||||
if len(music_files) == 0:
|
||||
# 歌曲名字相同会覆盖
|
||||
self._all_music[name] = os.path.join(root, filename)
|
||||
self._play_list = list(self._all_music.keys())
|
||||
random.shuffle(self._play_list)
|
||||
self.log.debug(self._all_music)
|
||||
|
||||
# 把下载的音乐加入播放列表
|
||||
def add_download_music(self, name):
|
||||
self._all_music[name] = os.path.join(self.music_path, f"{name}.mp3")
|
||||
if name not in self._play_list:
|
||||
self._play_list.append(name)
|
||||
self.log.debug("add_music %s", name)
|
||||
self.log.debug(self._play_list)
|
||||
|
||||
# 获取下一首
|
||||
def get_next_music(self):
|
||||
play_list_len = len(self._play_list)
|
||||
if play_list_len == 0:
|
||||
self.log.warning(f"没有随机到歌曲")
|
||||
return ""
|
||||
# 随机选择一个文件
|
||||
music_file = random.choice(music_files)
|
||||
(filename, extension) = os.path.splitext(music_file)
|
||||
self.log.info(f"随机到歌曲{filename}{extension}")
|
||||
index = 0
|
||||
try:
|
||||
index = self._play_list.index(self.cur_music)
|
||||
except ValueError:
|
||||
pass
|
||||
next_index = index + 1
|
||||
if next_index >= play_list_len:
|
||||
next_index = 0
|
||||
filename = self._play_list[next_index]
|
||||
return filename
|
||||
|
||||
# 获取文件播放时长
|
||||
@@ -367,8 +411,9 @@ class XiaoMusic:
|
||||
|
||||
# 设置下一首歌曲的播放定时器
|
||||
def set_next_music_timeout(self):
|
||||
sec = int(self.get_file_duration(self.cur_music))
|
||||
self.log.info(f"歌曲{self.cur_music}的时长{sec}秒")
|
||||
filename = self.get_filename(self.cur_music)
|
||||
sec = int(self.get_file_duration(filename))
|
||||
self.log.info(f"歌曲 {self.cur_music} : {filename} 的时长 {sec} 秒")
|
||||
if self._next_timer:
|
||||
self._next_timer.cancel()
|
||||
self.log.info(f"定时器已取消")
|
||||
@@ -402,10 +447,11 @@ class XiaoMusic:
|
||||
new_record = self.last_record
|
||||
self.polling_event.clear() # stop polling when processing the question
|
||||
query = new_record.get("query", "").strip()
|
||||
self.log.debug("收到消息:%s", query)
|
||||
ctrl_panel = new_record.get("ctrl_panel", False)
|
||||
self.log.debug("收到消息:%s 控制面板:%s", query, ctrl_panel)
|
||||
|
||||
# 匹配命令
|
||||
opvalue, oparg = self.match_cmd(query)
|
||||
opvalue, oparg = self.match_cmd(query, ctrl_panel)
|
||||
if not opvalue:
|
||||
await asyncio.sleep(1)
|
||||
continue
|
||||
@@ -417,7 +463,7 @@ class XiaoMusic:
|
||||
self.log.warning(f"执行出错 {str(e)}\n{traceback.format_exc()}")
|
||||
|
||||
# 匹配命令
|
||||
def match_cmd(self, query):
|
||||
def match_cmd(self, query, ctrl_panel):
|
||||
for opkey in KEY_MATCH_ORDER:
|
||||
patternarg = rf"(.*){opkey}(.*)"
|
||||
# 匹配参数
|
||||
@@ -436,28 +482,45 @@ class XiaoMusic:
|
||||
)
|
||||
oparg = argafter
|
||||
opvalue = KEY_WORD_DICT[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)
|
||||
self.log.info(
|
||||
"匹配到指令. opkey:%s opvalue:%s oparg:%s", opkey, opvalue, oparg
|
||||
)
|
||||
return (opvalue, oparg)
|
||||
if self._playing:
|
||||
self.log.info("未匹配到指令,自动停止")
|
||||
return ("stop", {})
|
||||
return (None, None)
|
||||
|
||||
# 播放歌曲
|
||||
async def play(self, **kwargs):
|
||||
name = kwargs["arg1"]
|
||||
if name == "":
|
||||
self._playing = True
|
||||
parts = kwargs["arg1"].split("|")
|
||||
search_key = parts[0]
|
||||
name = parts[1] if len(parts) > 1 else search_key
|
||||
if search_key == "" and name == "":
|
||||
await self.play_next()
|
||||
return
|
||||
if name == "":
|
||||
name = search_key
|
||||
self.log.debug("play. search_key:%s name:%s", search_key, name)
|
||||
filename = self.get_filename(name)
|
||||
|
||||
filename = self.local_exist(name)
|
||||
if len(filename) <= 0:
|
||||
await self.download(name)
|
||||
self.log.info("正在下载中 %s", name)
|
||||
filename = self.get_filename(f"{name}.mp3")
|
||||
await self.download(search_key, name)
|
||||
self.log.info("正在下载中 %s", search_key + ":" + name)
|
||||
await self.download_proc.wait()
|
||||
# 把文件插入到播放列表里
|
||||
self.add_download_music(name)
|
||||
|
||||
self.cur_music = filename
|
||||
url = self.get_file_url(filename)
|
||||
self.cur_music = name
|
||||
self.log.info("cur_music %s", self.cur_music)
|
||||
url = self.get_file_url(name)
|
||||
self.log.info("播放 %s", url)
|
||||
await self.stop_if_xiaoai_is_playing()
|
||||
await self.mina_service.play_by_url(self.device_id, url)
|
||||
@@ -468,10 +531,10 @@ class XiaoMusic:
|
||||
# 下一首
|
||||
async def play_next(self, **kwargs):
|
||||
self.log.info("下一首")
|
||||
(name, _) = os.path.splitext(os.path.basename(self.cur_music))
|
||||
name = self.cur_music
|
||||
self.log.debug("play_next. name:%s, cur_music:%s", name, self.cur_music)
|
||||
if self.play_type == PLAY_TYPE_ALL or name == "":
|
||||
name = self.random_music()
|
||||
name = self.get_next_music()
|
||||
if name == "":
|
||||
await self.do_tts(f"本地没有歌曲")
|
||||
return
|
||||
@@ -491,9 +554,12 @@ class XiaoMusic:
|
||||
async def random_play(self, **kwargs):
|
||||
self.play_type = PLAY_TYPE_ALL
|
||||
await self.do_tts(f"已经设置为全部循环并随机播放")
|
||||
# 重新生成随机播放列表
|
||||
self.gen_all_music_list()
|
||||
await self.play_next()
|
||||
|
||||
async def stop(self, **kwargs):
|
||||
self._playing = False
|
||||
if self._next_timer:
|
||||
self._next_timer.cancel()
|
||||
self.log.info(f"定时器已取消")
|
||||
@@ -523,3 +589,14 @@ class XiaoMusic:
|
||||
|
||||
def get_volume(self):
|
||||
return self._volume
|
||||
|
||||
# 搜索音乐
|
||||
def searchmusic(self, name):
|
||||
search_list = fuzzyfinder(name, self._play_list)
|
||||
self.log.debug("searchmusic. name:%s search_list:%s", name, search_list)
|
||||
return search_list
|
||||
|
||||
# 正在播放中的音乐
|
||||
def playingmusic(self):
|
||||
self.log.debug("playingmusic. cur_music:%s", self.cur_music)
|
||||
return self.cur_music
|
||||
|
||||
Reference in New Issue
Block a user