mirror of
https://github.com/hanxi/xiaomusic.git
synced 2025-12-06 14:52:50 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c912a51be | ||
|
|
bda55a1faa | ||
|
|
5b5f957f8e | ||
|
|
12f54e3ad4 | ||
|
|
d6594e1270 | ||
|
|
b678447417 | ||
|
|
02508f6997 | ||
|
|
d50fff9e31 | ||
|
|
2d7d7ddc95 | ||
|
|
9c9825d423 | ||
|
|
f788c0f37b | ||
|
|
36d72d1eca | ||
|
|
6b17779c59 | ||
|
|
759130e38d | ||
|
|
49f727477e | ||
|
|
43886116c1 | ||
|
|
a38194027d | ||
|
|
eab4f4bd46 | ||
|
|
6b38676766 |
26
.github/workflows/release.yml
vendored
26
.github/workflows/release.yml
vendored
@@ -10,31 +10,25 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release-pypi:
|
||||
name: Build and Release PyPI
|
||||
pypi-publish:
|
||||
name: upload release to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- uses: pdm-project/setup-pdm@v3
|
||||
|
||||
- name: Publish package distributions to PyPI
|
||||
run: pdm publish
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Build artifacts
|
||||
run: |
|
||||
pip install build
|
||||
python -m build
|
||||
|
||||
- uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
node-version: 16.x
|
||||
|
||||
- run: npx changelogithub
|
||||
continue-on-error: true
|
||||
|
||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,3 +1,25 @@
|
||||
## v0.3.17 (2024-07-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- 优化日志输出,尝试排查延迟播放的问题
|
||||
|
||||
## v0.3.16 (2024-07-28)
|
||||
|
||||
## v0.3.15 (2024-07-28)
|
||||
|
||||
### Fix
|
||||
|
||||
- 修复自定义口令重复的问题
|
||||
- 修复日志输出问题
|
||||
- 修复退出异常问题
|
||||
|
||||
## v0.3.14 (2024-07-28)
|
||||
|
||||
### Feat
|
||||
|
||||
- 优化播放延迟问题,并新增配置下一首播放的延迟秒数
|
||||
|
||||
## v0.3.13 (2024-07-24)
|
||||
|
||||
### Fix
|
||||
|
||||
@@ -37,7 +37,7 @@ services:
|
||||
```yaml
|
||||
docker run -p 8090:8090 \
|
||||
-v ./music:/app/music \
|
||||
-v ./conf:/app/conf
|
||||
-v ./conf:/app/conf \
|
||||
hanxi/xiaomusic
|
||||
```
|
||||
|
||||
|
||||
26
pdm.lock
generated
26
pdm.lock
generated
@@ -976,14 +976,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
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.5.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03bfe9ab5bdc0b08470c3b261643ad54ea86edc32b64d1e080892d7953add3ad"},
|
||||
{file = "ruff-0.5.3.tar.gz", hash = "sha256:2a3eb4f1841771fa5b67a56be9c2d16fd3cc88e378bd86aaeaec2f7e6bcdd0a2"},
|
||||
{file = "ruff-0.5.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93789f14ca2244fb91ed481456f6d0bb8af1f75a330e133b67d08f06ad85b516"},
|
||||
{file = "ruff-0.5.4.tar.gz", hash = "sha256:2795726d5f71c4f4e70653273d1c23a8182f07dd8e48c12de5d867bfb7557eed"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1094,7 +1094,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.30.1"
|
||||
version = "0.30.3"
|
||||
requires_python = ">=3.8"
|
||||
summary = "The lightning-fast ASGI server."
|
||||
groups = ["default"]
|
||||
@@ -1105,13 +1105,13 @@ dependencies = [
|
||||
"typing-extensions>=4.0; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"},
|
||||
{file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"},
|
||||
{file = "uvicorn-0.30.3-py3-none-any.whl", hash = "sha256:94a3608da0e530cea8f69683aa4126364ac18e3826b6630d1a65f4638aade503"},
|
||||
{file = "uvicorn-0.30.3.tar.gz", hash = "sha256:0d114d0831ff1adbf231d358cbf42f17333413042552a624ea6a9b4c33dcfd81"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.30.1"
|
||||
version = "0.30.3"
|
||||
extras = ["standard"]
|
||||
requires_python = ">=3.8"
|
||||
summary = "The lightning-fast ASGI server."
|
||||
@@ -1122,14 +1122,14 @@ dependencies = [
|
||||
"httptools>=0.5.0",
|
||||
"python-dotenv>=0.13",
|
||||
"pyyaml>=5.1",
|
||||
"uvicorn==0.30.1",
|
||||
"uvicorn==0.30.3",
|
||||
"uvloop!=0.15.0,!=0.15.1,>=0.14.0; (sys_platform != \"cygwin\" and sys_platform != \"win32\") and platform_python_implementation != \"PyPy\"",
|
||||
"watchfiles>=0.13",
|
||||
"websockets>=10.4",
|
||||
]
|
||||
files = [
|
||||
{file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"},
|
||||
{file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"},
|
||||
{file = "uvicorn-0.30.3-py3-none-any.whl", hash = "sha256:94a3608da0e530cea8f69683aa4126364ac18e3826b6630d1a65f4638aade503"},
|
||||
{file = "uvicorn-0.30.3.tar.gz", hash = "sha256:0d114d0831ff1adbf231d358cbf42f17333413042552a624ea6a9b4c33dcfd81"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1345,7 +1345,7 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "yt-dlp"
|
||||
version = "2024.7.16"
|
||||
version = "2024.7.25"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A feature-rich command-line audio/video downloader"
|
||||
groups = ["default"]
|
||||
@@ -1361,6 +1361,6 @@ dependencies = [
|
||||
"websockets>=12.0",
|
||||
]
|
||||
files = [
|
||||
{file = "yt_dlp-2024.7.16-py3-none-any.whl", hash = "sha256:424805a112e757b141e767bc938d49db56d13d6415a92fa4cd8acadd55790be0"},
|
||||
{file = "yt_dlp-2024.7.16.tar.gz", hash = "sha256:c5bd517a49dea1923ec8e14f51858f10fd89dfece14cb701392b480b41b2f516"},
|
||||
{file = "yt_dlp-2024.7.25-py3-none-any.whl", hash = "sha256:f44b5f33776b4f718900c670fe6e4698fb6fcd426455cd837cf25a1d6d4d9560"},
|
||||
{file = "yt_dlp-2024.7.25.tar.gz", hash = "sha256:7587aa25e236cf7b14bdb9378bbffff51202d901b04202be0cf62cbb56d3b52c"},
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "xiaomusic"
|
||||
version = "0.3.13"
|
||||
version = "0.3.17"
|
||||
description = "Play Music with xiaomi AI speaker"
|
||||
authors = [
|
||||
{name = "涵曦", email = "im.hanxi@gmail.com"},
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.3.13"
|
||||
__version__ = "0.3.17"
|
||||
|
||||
@@ -78,19 +78,60 @@ def main():
|
||||
xiaomusic = XiaoMusic(config)
|
||||
HttpInit(xiaomusic)
|
||||
|
||||
from uvicorn.config import LOGGING_CONFIG
|
||||
|
||||
LOGGING_CONFIG["formatters"]["access"] = {
|
||||
"format": f"%(asctime)s [{__version__}] [%(levelname)s] %(filename)s:%(lineno)d: %(message)s",
|
||||
"datefmt": "[%X]",
|
||||
}
|
||||
LOGGING_CONFIG["handlers"]["access"] = {
|
||||
"level": "INFO",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"formatter": "access",
|
||||
"filename": config.log_file,
|
||||
"maxBytes": 10 * 1024 * 1024,
|
||||
"backupCount": 1,
|
||||
LOGGING_CONFIG = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": f"%(asctime)s [{__version__}] [%(levelname)s] %(message)s",
|
||||
"datefmt": "[%X]",
|
||||
"use_colors": False,
|
||||
},
|
||||
"access": {
|
||||
"format": f"%(asctime)s [{__version__}] [%(levelname)s] %(message)s",
|
||||
"datefmt": "[%X]",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"default": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
"access": {
|
||||
"formatter": "access",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stdout",
|
||||
},
|
||||
"file": {
|
||||
"level": "INFO",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"formatter": "access",
|
||||
"filename": config.log_file,
|
||||
"maxBytes": 10 * 1024 * 1024,
|
||||
"backupCount": 1,
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"uvicorn": {
|
||||
"handlers": [
|
||||
"default",
|
||||
"file",
|
||||
],
|
||||
"level": "INFO",
|
||||
},
|
||||
"uvicorn.error": {
|
||||
"level": "INFO",
|
||||
},
|
||||
"uvicorn.access": {
|
||||
"handlers": [
|
||||
"access",
|
||||
"file",
|
||||
],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
uvicorn.run(
|
||||
HttpApp,
|
||||
|
||||
@@ -136,6 +136,7 @@ class Config:
|
||||
remove_id3tag: bool = (
|
||||
os.getenv("XIAOMUSIC_REMOVE_ID3TAG", "false").lower() == "true"
|
||||
)
|
||||
delay_sec: int = int(os.getenv("XIAOMUSIC_DELAY_SEC", 3)) # 下一首歌延迟播放秒数
|
||||
|
||||
def append_keyword(self, keys, action):
|
||||
for key in keys.split(","):
|
||||
@@ -146,9 +147,11 @@ class Config:
|
||||
def append_user_keyword(self):
|
||||
for k, v in self.user_key_word_dict.items():
|
||||
self.key_word_dict[k] = v
|
||||
self.key_match_order.append(k)
|
||||
if k not in self.key_match_order:
|
||||
self.key_match_order.append(k)
|
||||
|
||||
def init_keyword(self):
|
||||
self.key_match_order = default_key_match_order()
|
||||
self.append_keyword(self.keywords_playlocal, "playlocal")
|
||||
self.append_keyword(self.keywords_play, "play")
|
||||
self.append_keyword(self.keywords_stop, "stop")
|
||||
|
||||
@@ -29,9 +29,11 @@ log = None
|
||||
@asynccontextmanager
|
||||
async def app_lifespan(app):
|
||||
if xiaomusic is not None:
|
||||
task = asyncio.create_task(xiaomusic.run_forever())
|
||||
yield
|
||||
task.cancel()
|
||||
asyncio.create_task(xiaomusic.run_forever())
|
||||
try:
|
||||
yield
|
||||
except Exception as e:
|
||||
log.exception(f"Execption {e}")
|
||||
|
||||
|
||||
security = HTTPBasic()
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Debug For XiaoMusic</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?version=1721862430">
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?version=1722202968">
|
||||
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||
<script src="/static/jquery-3.7.1.min.js?version=1721862430"></script>
|
||||
<script src="/static/jquery-3.7.1.min.js?version=1722202968"></script>
|
||||
|
||||
<script>
|
||||
var vConsole = new window.VConsole();
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>小爱音箱操控面板</title>
|
||||
<script src="/static/jquery-3.7.1.min.js?version=1721862430"></script>
|
||||
<script src="/static/app.js?version=1721862430"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?version=1721862430">
|
||||
<script src="/static/jquery-3.7.1.min.js?version=1722202968"></script>
|
||||
<script src="/static/app.js?version=1722202968"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?version=1722202968">
|
||||
|
||||
<!--
|
||||
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>M3U to JSON Converter</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?version=1721862430">
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?version=1722202968">
|
||||
<!--
|
||||
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>小爱音箱操控面板</title>
|
||||
<script src="/static/jquery-3.7.1.min.js?version=1721862430"></script>
|
||||
<script src="/static/setting.js?version=1721862430"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?version=1721862430">
|
||||
<script src="/static/jquery-3.7.1.min.js?version=1722202968"></script>
|
||||
<script src="/static/setting.js?version=1722202968"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css?version=1722202968">
|
||||
|
||||
<!--
|
||||
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
|
||||
@@ -130,6 +130,9 @@ var vConsole = new window.VConsole();
|
||||
<label for="public_port">外网访问端口(0表示跟监听端口一致):</label>
|
||||
<input id="public_port" type="number" value="0"></input>
|
||||
|
||||
<label for="delay_sec">下一首歌延迟播放秒数:</label>
|
||||
<input id="delay_sec" type="number" value="3"></input>
|
||||
|
||||
<label for="stop_tts_msg">停止提示音:</label>
|
||||
<input id="stop_tts_msg" type="text" value="收到,再见"></input>
|
||||
<label for="keywords_playlocal">播放本地歌曲口令:</label>
|
||||
|
||||
@@ -68,9 +68,8 @@ $(function(){
|
||||
|
||||
// 初始化显示
|
||||
for (const key in data) {
|
||||
if (data.hasOwnProperty(key)) {
|
||||
const $element = $("#" + key);
|
||||
if ($element.length && data[key] !== '') {
|
||||
if ($element.length) {
|
||||
if (data[key] === true) {
|
||||
$element.val('true');
|
||||
} else if (data[key] === false) {
|
||||
@@ -79,7 +78,6 @@ $(function(){
|
||||
$element.val(data[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
autoSelectOne();
|
||||
|
||||
@@ -855,7 +855,7 @@ class XiaoMusicDevice:
|
||||
async def _play(self, name="", search_key=""):
|
||||
if search_key == "" and name == "":
|
||||
if self.check_play_next():
|
||||
await self.play_next()
|
||||
await self._play_next()
|
||||
return
|
||||
else:
|
||||
name = self.cur_music
|
||||
@@ -888,7 +888,7 @@ class XiaoMusicDevice:
|
||||
or (name not in self._play_list)
|
||||
):
|
||||
name = self.get_next_music()
|
||||
self.log.info(f"play_next. name:{name}, cur_music:{self.cur_music}")
|
||||
self.log.info(f"_play_next. name:{name}, cur_music:{self.cur_music}")
|
||||
if name == "":
|
||||
await self.do_tts("本地没有歌曲")
|
||||
return
|
||||
@@ -899,7 +899,7 @@ class XiaoMusicDevice:
|
||||
self._last_cmd = "playlocal"
|
||||
if name == "":
|
||||
if self.check_play_next():
|
||||
await self.play_next()
|
||||
await self._play_next()
|
||||
return
|
||||
else:
|
||||
name = self.cur_music
|
||||
@@ -914,11 +914,14 @@ class XiaoMusicDevice:
|
||||
await self._playmusic(name)
|
||||
|
||||
async def _playmusic(self, name):
|
||||
# 取消组内所有的下一首歌曲的定时器
|
||||
self.cancel_group_next_timer()
|
||||
|
||||
self._playing = True
|
||||
self.cur_music = name
|
||||
self.log.info(f"cur_music {self.cur_music}")
|
||||
sec, url = await self.xiaomusic.get_music_sec_url(name)
|
||||
await self.group_force_stop_xiaoai()
|
||||
# await self.group_force_stop_xiaoai()
|
||||
self.log.info(f"播放 {url}")
|
||||
results = await self.group_player_play(url)
|
||||
if all(ele is None for ele in results):
|
||||
@@ -928,11 +931,10 @@ class XiaoMusicDevice:
|
||||
await self._play_next()
|
||||
return
|
||||
|
||||
self.log.info("已经开始播放了")
|
||||
self.log.info(f"【{name}】已经开始播放了")
|
||||
|
||||
# 取消组内所有的下一首歌曲的定时器
|
||||
self.cancel_group_next_timer()
|
||||
# 设置下一首歌曲的播放定时器
|
||||
sec = sec + self.config.delay_sec
|
||||
await self.set_next_music_timeout(sec)
|
||||
|
||||
async def do_tts(self, value):
|
||||
@@ -941,7 +943,7 @@ class XiaoMusicDevice:
|
||||
self.log.info("do_tts no value")
|
||||
return
|
||||
|
||||
await self.group_force_stop_xiaoai()
|
||||
# await self.group_force_stop_xiaoai()
|
||||
await self.text_to_speech(value)
|
||||
|
||||
# 最大等8秒
|
||||
@@ -1083,14 +1085,17 @@ class XiaoMusicDevice:
|
||||
def check_play_next(self):
|
||||
# 当前歌曲不在当前播放列表
|
||||
if self.cur_music not in self._play_list:
|
||||
self.log.info(f"当前歌曲 {self.cur_music} 不在当前播放列表")
|
||||
return True
|
||||
|
||||
# 当前没我在播放的歌曲
|
||||
if self.cur_music == "":
|
||||
self.log.info("当前没我在播放的歌曲")
|
||||
return True
|
||||
else:
|
||||
# 当前播放的歌曲不存在了
|
||||
if not self.xiaomusic.is_music_exist(self.cur_music):
|
||||
self.log.info(f"当前播放的歌曲 {self.cur_music} 不存在了")
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -1141,6 +1146,7 @@ class XiaoMusicDevice:
|
||||
async def _do_next():
|
||||
await asyncio.sleep(self._timeout)
|
||||
try:
|
||||
self.log.info("定时器时间到了")
|
||||
await self.play_next()
|
||||
except Exception as e:
|
||||
self.log.error(f"Execption {e}")
|
||||
|
||||
Reference in New Issue
Block a user