From 382b0e160118faaf3a1763ae198b62391a10fc49 Mon Sep 17 00:00:00 2001 From: EillesWan Date: Wed, 1 May 2024 01:16:05 +0800 Subject: [PATCH] =?UTF-8?q?websocket=E6=92=AD=E6=94=BE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + LICENSE.md | 5 +- Musicreater/__init__.py | 4 +- Musicreater/plugin/websocket.py | 40 --------- Musicreater/plugin/websocket/main.py | 123 ++++++++++++++++++++++++--- Musicreater/subclass.py | 58 +++++++++++-- Musicreater/utils.py | 8 +- README.md | 7 +- clean_update.py | 4 +- example_websocket.py | 20 +++++ 10 files changed, 197 insertions(+), 73 deletions(-) delete mode 100644 Musicreater/plugin/websocket.py create mode 100644 example_websocket.py diff --git a/.gitignore b/.gitignore index b1a39f9..9432ddf 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ RES.txt /Packer/*.MPK /Packer/checksum.txt /bgArrayLib +/fcwslib # Byte-compiled / optimized __pycache__/ diff --git a/LICENSE.md b/LICENSE.md index 98b28e0..15693bd 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -7,9 +7,9 @@ 5. Apache2.0 协议的英文原文副本可见下文 > The English Translation of the TERMS AND CONDITIONS above is listed below -> +> > This translated version is for reference only and has no legal effect. -> +> > The version with legal effect is the Chinese version above. **Note, The TERMS AND CONDITIONS below should and must be above all others in this project** @@ -23,7 +23,6 @@ 3. Apache 2.0 License. 5. A copy of the original Apache Lisence 2.0 can be found below. - ```text Apache License Version 2.0, January 2004 diff --git a/Musicreater/__init__.py b/Musicreater/__init__.py index e5e1f37..f42bebd 100644 --- a/Musicreater/__init__.py +++ b/Musicreater/__init__.py @@ -17,8 +17,8 @@ Terms & Conditions: License.md in the root directory # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md -__version__ = "2.0.0" -__vername__ = "全新组织架构" +__version__ = "2.1.0" +__vername__ = "Websocket支持" __author__ = ( ("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), diff --git a/Musicreater/plugin/websocket.py b/Musicreater/plugin/websocket.py deleted file mode 100644 index 13b1b5e..0000000 --- a/Musicreater/plugin/websocket.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -""" -存放有关WebSocket服务器操作的内容 -""" - -""" -版权所有 © 2024 音·创 开发者 -Copyright © 2024 all the developers of Musicreater - -开源相关声明请见 仓库根目录下的 License.md -Terms & Conditions: License.md in the root directory -""" - -# 睿乐组织 开发交流群 861684859 -# Email TriM-Organization@hotmail.com -# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md - - -import fcwslib - -# 这个库有问题,正在检修 - - -class Plugin(fcwslib.Plugin): - async def on_connect(self) -> None: - print("对象已被连接") - await self.send_command("list", callback=self.list) - await self.subscribe("PlayerMessage", callback=self.player_message) - - async def on_disconnect(self) -> None: - print("对象停止连接") - - async def on_receive(self, response) -> None: - print("已接收非常规回复 {}".format(response)) - - async def list(self, response) -> None: - print("已收取指令执行回复 {}".format(response)) - - async def player_message(self, response) -> None: - print("已收取玩家事件回复 {}".format(response)) diff --git a/Musicreater/plugin/websocket/main.py b/Musicreater/plugin/websocket/main.py index 5ea71ca..028dbca 100644 --- a/Musicreater/plugin/websocket/main.py +++ b/Musicreater/plugin/websocket/main.py @@ -11,19 +11,120 @@ Terms & Conditions: License.md in the root directory # Email TriM-Organization@hotmail.com # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md + import fcwslib +import asyncio +import uuid +import time -# from ...main import MidiConvert +from ...main import MidiConvert +from ...subclass import ProgressBarStyle +from ...types import Optional, Literal, Tuple, List -# from ..main import ConvertConfig -# from ...subclass import MineCommand +from ...subclass import MineCommand -# def open_websocket_server( -# midi_cvt: MidiConvert, -# data_cfg: ConvertConfig, -# player: str = "@a", -# server_dist: str = "localhost", -# server_port: int = 8000, -# ): -# wssever = fcwslib.Server(server=server_dist,port=server_port,debug_mode=False) \ No newline at end of file +def to_websocket_server( + midi_cvt_lst: List[MidiConvert], + server_dist: str, + server_port: int, + progressbar_style: Optional[ProgressBarStyle], +) -> None: + """ + 将midi以延迟播放器形式转换为mcstructure结构文件后打包成附加包,并在附加包中生成相应地导入函数 + + Parameters + ---------- + midi_cvt: List[MidiConvert] + 一组用于转换的MidiConvert对象 + server_dist: str + WebSocket播放服务器开启地址 + server_port: str + WebSocket播放服务器开启端口 + progressbar_style: ProgressBarStyle 对象 + 进度条对象 + + Returns + ------- + None + """ + + replacement = str(uuid.uuid4()) + + musics = dict( + [ + (k.music_name, k.to_command_list_in_delay(replacement)[:2]) + for k in midi_cvt_lst + ] + ) + + class Plugin(fcwslib.Plugin): + async def on_connect(self) -> None: + print("已成功获连接") + await self.send_command("list", callback=self.cmd_feedback) + await self.subscribe("PlayerMessage", callback=self.player_message) + + async def on_disconnect(self) -> None: + print("连接已然终止") + await self.disconnect() + + async def on_receive(self, response) -> None: + print("已收取非已知列回复 {}".format(response)) + + async def cmd_feedback(self, response) -> None: + print("已收取指令执行回复 {}".format(response)) + + async def player_message(self, response) -> None: + print("已收取玩家事件回复 {}".format(response)) + if response["body"]["message"].startswith(("。播放", ".play")): + whom_to_play: str = response["body"]["sender"] + music_to_play: str = ( + response["body"]["message"] + .replace("。播放", "") + .replace(".play", "") + .strip() + ) + if music_to_play in musics.keys(): + self.check_play = True + delay_of_now = 0 + for cmd in musics[music_to_play][0]: + if not self.check_play: + break + _time = time.time() + if progressbar_style: + await self.send_command( + "title {} actionbar {}".format( + whom_to_play, + progressbar_style.play_output( + played_delays=delay_of_now, + total_delays=musics[music_to_play][1], + music_name=music_to_play, + ), + ), + callback=self.cmd_feedback, + ) + await self.send_command( + cmd.command_text.replace(replacement, whom_to_play), + callback=self.cmd_feedback, + ) + delay_of_now += cmd.delay + await asyncio.sleep((time.time() - _time) + cmd.delay / 20) + + else: + await self.send_command( + "tellraw {} {}{}{}".format( + whom_to_play, + r'{"rawtext":[{"text":"§c§l所选歌曲', + music_to_play, + '无法播放:播放列表不存在之"}]}', + ), + callback=self.cmd_feedback, + ) + elif response["body"]["message"].startswith( + ("。停止播放", ".stopplay", ".stoplay") + ): + self.check_play = False + + server = fcwslib.Server(server=server_dist, port=server_port, debug_mode=True) + server.add_plugin(Plugin) + asyncio.run(server.run_forever()) diff --git a/Musicreater/subclass.py b/Musicreater/subclass.py index 53b6970..51e2211 100644 --- a/Musicreater/subclass.py +++ b/Musicreater/subclass.py @@ -155,7 +155,7 @@ class MineNote: def encode(self, is_displacement_included: bool = True) -> bytes: """ 将数据打包为字节码 - + :param is_displacement_included:`bool` 是否包含声像偏移数据,默认为**是** :return bytes 打包好的字节码 @@ -662,9 +662,21 @@ class ProgressBarStyle: def __init__(self, base_s: str, to_play_s: str, played_s: str): """用于存储进度条样式的类 + + | 标识符 | 指定的可变量 | + |---------|----------------| + | `%%N` | 乐曲名(即传入的文件名)| + | `%%s` | 当前计分板值 | + | `%^s` | 计分板最大值 | + | `%%t` | 当前播放时间 | + | `%^t` | 曲目总时长 | + | `%%%` | 当前进度比率 | + | `_` | 用以表示进度条占位| + :param base_s 基础样式,用以定义进度条整体 :param to_play_s 进度条样式:尚未播放的样子 - :param played_s 已经播放的样子""" + :param played_s 已经播放的样子 + """ self.base_style = base_s self.to_play_style = to_play_s self.played_style = played_s @@ -704,11 +716,47 @@ class ProgressBarStyle: dst = ProgressBarStyle(self.base_style, self.to_play_style, self.played_style) return dst + def play_output( + self, + played_delays: int, + total_delays: int, + music_name: str = "无题", + ) -> str: + """ + 直接依照此格式输出一个进度条 + + :param played_delays: int 当前播放进度积分值 + :param total_delays: int 乐器总延迟数(积分数) + :param music_name: str 曲名 + """ + + return ( + self.base_style.replace(r"%%N", music_name) + .replace(r"%%s", str(played_delays)) + .replace(r"%^s", str(total_delays)) + .replace(r"%%t", mctick2timestr(played_delays)) + .replace(r"%^t", mctick2timestr(total_delays)) + .replace(r"%%%", str(int(10000 * played_delays / total_delays) / 100) + "%") + .replace( + "_", + self.played_style, + (played_delays * self.base_style.count("_") // total_delays) + 1, + ) + .replace("_", self.to_play_style) + ) + + +def mctick2timestr(mc_tick: int) -> str: + """ + 将《我的世界》的游戏刻计转为表示时间的字符串 + """ + return "{:0>2d}:{:0>2d}".format(mc_tick // 1200, (mc_tick // 20) % 60) + DEFAULT_PROGRESSBAR_STYLE = ProgressBarStyle( - r"▶ %%N [ %%s/%^s %%% __________ %%t|%^t ]", - r"§e=§r", - r"§7=§r", + r"▶ %%N [ %%s/%^s %%% §e__________§r %%t|%^t ]", + r"§7=", + r"=", ) """ 默认的进度条样式 diff --git a/Musicreater/utils.py b/Musicreater/utils.py index 8229500..e60f9a2 100644 --- a/Musicreater/utils.py +++ b/Musicreater/utils.py @@ -24,7 +24,7 @@ from .constants import ( MC_PITCHED_INSTRUMENT_LIST, MM_INSTRUMENT_RANGE_TABLE, ) -from .subclass import SingleNote, MineNote +from .subclass import SingleNote, MineNote, mctick2timestr from .types import ( Any, @@ -38,12 +38,6 @@ from .types import ( ) -def mctick2timestr(mc_tick: int) -> str: - """ - 将《我的世界》的游戏刻计转为表示时间的字符串 - """ - return str(int(int(mc_tick / 20) / 60)) + ":" + str(int(int(mc_tick / 20) % 60)) - def empty_midi_channels(channel_count: int = 17, staff: Any = {}) -> Dict[int, Any]: """ diff --git a/README.md b/README.md index 5e3ff2d..4e5c03b 100644 --- a/README.md +++ b/README.md @@ -40,18 +40,19 @@ ## 安装 🔳 - 使用 pypi - + ```bash pip install --upgrade Musicreater ``` - 如果无法更新最新,可以尝试: - + ```bash pip install --upgrade -i https://pypi.python.org/simple Musicreater ``` - 克隆仓库并安装(最新版本但**不推荐**) + ```bash git clone https://gitee.com/TriM-Organization/Musicreater.git cd Musicreater @@ -91,7 +92,7 @@ -
感谢 **油炸**\ 激励我们不断开发新的内容。
> 感谢广大群友为此库提供的测试和建议等 -> +> > 若您对我们有所贡献但您的名字没有出现在此列表中,请联系我们! ## 联系 📞 diff --git a/clean_update.py b/clean_update.py index ec1475d..02e15c1 100644 --- a/clean_update.py +++ b/clean_update.py @@ -7,14 +7,14 @@ console = Console() def main(): - with console.status("Find the full path of .egg-info folder"): + with console.status("寻众迹于 .egg-info 内"): egg_info: list = [] for file in os.listdir(): if file.endswith(".egg-info"): egg_info.append(file) console.print(file) for file in track( - ["build", "dist", "logs", *egg_info], description="Deleting files" + ["build", "dist", "logs", *egg_info], description="正删档" ): if os.path.isdir(file) and os.access(file, os.W_OK): shutil.rmtree(file) diff --git a/example_websocket.py b/example_websocket.py new file mode 100644 index 0000000..93cb651 --- /dev/null +++ b/example_websocket.py @@ -0,0 +1,20 @@ +import Musicreater +import Musicreater.plugin +import Musicreater.plugin.websocket + +import os + +dire = input("midi目录:") + +print( + Musicreater.plugin.websocket.to_websocket_server( + [ + Musicreater.MidiConvert.from_midi_file( + os.path.join(dire,names), old_exe_format=False + ) for names in os.listdir(dire,) if names.endswith((".mid",".midi")) + ], + input("服务器地址:"), + int(input("服务器端口:")), + Musicreater.DEFAULT_PROGRESSBAR_STYLE, + ) +)