diff --git a/plugins/httppost.py b/plugins/httppost.py new file mode 100644 index 0000000..3884758 --- /dev/null +++ b/plugins/httppost.py @@ -0,0 +1,9 @@ +import requests +target="HTTP://192.168.1.10:58091/items/" +def httppost(data,url=target): + global log + # 发起请求, + with requests.post(url,json=data, timeout=5) as response: # 增加超时以避免长时间挂起 + response.raise_for_status() # 如果响应不是200,引发HTTPError异常 + log.info(f"httppost url:{url} data :{data} response:{response.text}") + diff --git a/pyproject.toml b/pyproject.toml index fb3c0bc..c1ba415 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ "python-multipart>=0.0.12", "requests>=2.32.3", "sentry-sdk[fastapi]==1.45.1", + "python-socketio>=5.12.1", ] requires-python = ">=3.10" readme = "README.md" diff --git a/xiaomusic/cli.py b/xiaomusic/cli.py index dcddb66..7ddf4d7 100644 --- a/xiaomusic/cli.py +++ b/xiaomusic/cli.py @@ -40,7 +40,7 @@ def main(): from xiaomusic import __version__ from xiaomusic.config import Config from xiaomusic.httpserver import HttpInit - from xiaomusic.httpserver import app as HttpApp + from xiaomusic.httpserver import socketio_app as HttpApp from xiaomusic.xiaomusic import XiaoMusic parser = argparse.ArgumentParser() diff --git a/xiaomusic/httpserver.py b/xiaomusic/httpserver.py index 4257cf7..e877541 100644 --- a/xiaomusic/httpserver.py +++ b/xiaomusic/httpserver.py @@ -9,7 +9,7 @@ import urllib.parse from contextlib import asynccontextmanager from dataclasses import asdict from typing import TYPE_CHECKING, Annotated - +import socketio if TYPE_CHECKING: from xiaomusic.xiaomusic import XiaoMusic @@ -55,7 +55,13 @@ from xiaomusic.utils import ( xiaomusic: "XiaoMusic" = None config = None log = None - +from pydantic import BaseModel +# 3thplay指令 +class Item(BaseModel): + action:str + args:str +# 在线用户 +onlines= set() @asynccontextmanager async def app_lifespan(app): @@ -104,6 +110,42 @@ app = FastAPI( openapi_url=None, ) +# 创建Socket.IO实例 +sio = socketio.AsyncServer( + async_mode='asgi', + cors_allowed_origins='*' # 允许所有跨域请求,生产环境应限制 +) +# 将Socket.IO挂载到FastAPI应用 +socketio_app = socketio.ASGIApp( + socketio_server=sio, + other_asgi_app=app, + socketio_path='/socket.io' +) + +# Socket.IO事件处理 +@sio.event +async def connect(sid, environ, auth): + global onlines + print(f'客户端连接: {sid}') + onlines.update([sid]) + await sio.emit('message', {'data': '欢迎连接'}, room=sid) + +@sio.event +async def disconnect(sid): + print(f'客户端断开: {sid}') + onlines.discard(sid) + +@sio.on("message") +async def custom_event(sid, data): + log.info(f'收到来自 {sid} 的数据: {data}') + await sio.emit('response', {"action":'切歌','status':data}) + +@app.post("/items/") +async def create_item(item: Item): + result = {**item.dict()} + await sio.emit('response',{"action":item.action,"args":item.args,"status":item.args},) + return onlines + app.add_middleware( CORSMiddleware, allow_origins=["*"], # 允许访问的源 @@ -135,11 +177,11 @@ class AuthStaticFiles(StaticFiles): def HttpInit(_xiaomusic): - global xiaomusic, config, log + global xiaomusic, config, log,onlines xiaomusic = _xiaomusic config = xiaomusic.config log = xiaomusic.log - + onlines=set() folder = os.path.dirname(__file__) app.mount("/static", AuthStaticFiles(directory=f"{folder}/static"), name="static") reset_http_server() diff --git a/xiaomusic/static/3thdplay.mp3 b/xiaomusic/static/3thdplay.mp3 new file mode 100644 index 0000000..fff4e2f Binary files /dev/null and b/xiaomusic/static/3thdplay.mp3 differ diff --git a/xiaomusic/static/3thplay.html b/xiaomusic/static/3thplay.html new file mode 100644 index 0000000..16f35cd --- /dev/null +++ b/xiaomusic/static/3thplay.html @@ -0,0 +1,499 @@ + + + + + + + + Mini音乐播放器 + + + + + + +
+ +
+ 封面 +
+ +
+

Let Go

+

Avril Lavigne

+
+ +
+
+
+ +
+ 00:00 + 00:00 +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ + + + + +
+ + diff --git a/xiaomusic/utils.py b/xiaomusic/utils.py index 5778cac..753f831 100644 --- a/xiaomusic/utils.py +++ b/xiaomusic/utils.py @@ -234,7 +234,19 @@ def traverse_music_directory(directory, depth, exclude_dirs, support_extension): _append_files_result(result, root, root, files, support_extension) return result - +# 发送给网页3thplay,用于三者设备播放 +async def thdplay(action,args="/static/3thdplay.mp3",target="HTTP://192.168.1.10:58091/items/"): + # 接口地址 target,在参数文件指定 + data={"action":action,"args":args} + async with aiohttp.ClientSession() as session: + async with session.post( + target,json=data, timeout=5 + ) as response: # 增加超时以避免长时间挂起 + # 如果响应不是200,引发异常 + response.raise_for_status() + # 读取响应文本 + text = await response.text() + return '[]' not in text async def downloadfile(url): # 清理和验证URL # 解析URL diff --git a/xiaomusic/xiaomusic.py b/xiaomusic/xiaomusic.py index 158eb55..ccacbb0 100644 --- a/xiaomusic/xiaomusic.py +++ b/xiaomusic/xiaomusic.py @@ -58,6 +58,7 @@ from xiaomusic.utils import ( set_music_tag_to_file, traverse_music_directory, try_add_access_control_param, + thdplay, ) @@ -132,7 +133,9 @@ class XiaoMusic: self.public_port = self.config.public_port if self.public_port == 0: self.public_port = self.port - + #自动3thplay生成播放 post url + self.thdtarget= f"{self.hostname}:{self.public_port}/items/" # "HTTP://192.168.1.10:58091/items/" + self.active_cmd = self.config.active_cmd.split(",") self.exclude_dirs = set(self.config.exclude_dirs.split(",")) self.music_path_depth = self.config.music_path_depth @@ -1289,13 +1292,33 @@ class XiaoMusic: # 获取音量 async def get_volume(self, did="", **kwargs): return await self.devices[did].get_volume() - + # 3thdplay.html 的音量设置消息发送 需要配置文件加入自定义指令 + # "user_key_word_dict": { + #"音量": "set_myvolume", + #"继续": "stop", + #"大点音": "exec#setmyvolume(\"up\")", + #"小点音": "exec#setmyvolume(\"down\")", + + async def set_myvolume(self, did="", arg1=0, **kwargs): + if did not in self.devices: + self.log.info(f"设备 did:{did} 不存在, 不能设置音量") + return + if arg1=="up": + await thdplay('up','',self.thdtarget) + + elif arg1=="down": + await thdplay('down','',self.thdtarget) + else: + volume = chinese_to_number(arg1) + await thdplay('volume',str(volume),self.thdtarget) + # 设置音量 async def set_volume(self, did="", arg1=0, **kwargs): if did not in self.devices: self.log.info(f"设备 did:{did} 不存在, 不能设置音量") return volume = int(arg1) + await thdplay('volume',str(volume),self.thdtarget) return await self.devices[did].set_volume(volume) # 搜索音乐 @@ -1664,8 +1687,12 @@ class XiaoMusicDevice: sec, url = await self.xiaomusic.get_music_sec_url(name) await self.group_force_stop_xiaoai() self.log.info(f"播放 {url}") - results = await self.group_player_play(url, name) - if all(ele is None for ele in results): + # 有3方设备打开 /static/3thplay.html 通过socketio连接返回true 忽律小爱音箱的播放 + online=await thdplay('play',url,self.xiaomusic.thdtarget) + if not online: + + results = await self.group_player_play(url, name) + if all(ele is None for ele in results): self.log.info(f"播放 {name} 失败. 失败次数: {self._play_failed_cnt}") await asyncio.sleep(1) if ( @@ -2057,6 +2084,9 @@ class XiaoMusicDevice: await self.do_tts(self.config.stop_tts_msg) await asyncio.sleep(3) # 等它说完 # 取消组内所有的下一首歌曲的定时器 + if await thdplay('stop','',self.xiaomusic.thdtarget): + + return self.cancel_group_next_timer() await self.group_force_stop_xiaoai() self.log.info("stop now")