基本的设计已经完成,今天休息一下

This commit is contained in:
2026-01-26 09:39:22 +08:00
parent 734ee2dd66
commit 0de959c396
40 changed files with 1372 additions and 209 deletions

View File

@@ -85,7 +85,7 @@ __all__ = [
"midi_inst_to_mc_sound", "midi_inst_to_mc_sound",
] ]
from .main import MusicSequence, MidiConvert from .old_main import MusicSequence, MidiConvert
from .subclass import ( from .subclass import (
MineNote, MineNote,

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
存储音·创新数据存储 存储 音·创 v3 的内部数据
""" """
""" """
版权所有 © 2025 金羿 版权所有 © 2026 金羿
Copyright © 2025 Eilles Copyright © 2026 Eilles
开源相关声明请见 仓库根目录下的 License.md 开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
@@ -16,7 +16,6 @@ Terms & Conditions: License.md in the root directory
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
# WARNING 本文件中使用之功能尚未启用
from math import sin, cos, asin, radians, degrees, sqrt, atan, inf, ceil from math import sin, cos, asin, radians, degrees, sqrt, atan, inf, ceil
from dataclasses import dataclass from dataclasses import dataclass
@@ -37,9 +36,7 @@ from typing import (
) )
from enum import Enum from enum import Enum
from .types import FittingFunctionType from .exceptions import SingleNoteDecodeError, ParameterTypeError
from .constants import MC_PITCHED_INSTRUMENT_LIST
from .paramcurve import ParamCurve from .paramcurve import ParamCurve
@@ -205,13 +202,21 @@ class SingleNote:
last_time=duration_, last_time=duration_,
mass_precision_time=code_buffer[6] if is_high_time_precision else 0, mass_precision_time=code_buffer[6] if is_high_time_precision else 0,
) )
except: except Exception as e:
# 我也不知道为什么这里要放一个异常处理
# 之前用到过吗?
# —— 2026.01.25 金羿
print( print(
"[Error] 单音符解析错误,字节码`{}`{}启用高精度时间偏移\n".format( "[Exception] 单音符解析错误,字节码`{}`{}启用高精度时间偏移\n".format(
code_buffer, "" if is_high_time_precision else "" code_buffer, "" if is_high_time_precision else ""
) )
) )
raise raise SingleNoteDecodeError(
e,
"技术信息:\nGROUP1\t`{}`\nCODE_BUFFER\t`{}`".format(
group_1, code_buffer
),
)
def encode(self, is_high_time_precision: bool = True) -> bytes: def encode(self, is_high_time_precision: bool = True) -> bytes:
""" """
@@ -267,7 +272,9 @@ class SingleNote:
self.extra_info[key[i]] = value[i] self.extra_info[key[i]] = value[i]
else: else:
# 提供简单报错就行了,如果放一堆 if 语句,降低处理速度 # 提供简单报错就行了,如果放一堆 if 语句,降低处理速度
raise TypeError("参数类型错误;键:`{}` 值:`{}`".format(key, value)) raise ParameterTypeError(
"参数类型错误;键:`{}` 值:`{}`".format(key, value)
)
def get_info(self, key: str, default: Any = None) -> Any: def get_info(self, key: str, default: Any = None) -> Any:
"""获取附加信息""" """获取附加信息"""
@@ -369,6 +376,7 @@ class MineNote:
note: SingleNote, note: SingleNote,
note_instrument: str, note_instrument: str,
sound_volume: float, sound_volume: float,
is_persiced_time: bool,
is_percussive_note: bool, is_percussive_note: bool,
sound_position: SoundAtmos, sound_position: SoundAtmos,
adjust_note_pitch: float = 0.0, adjust_note_pitch: float = 0.0,
@@ -391,7 +399,7 @@ class MineNote:
volume=sound_volume + adjust_note_volume, volume=sound_volume + adjust_note_volume,
start_tick=note.start_time, start_tick=note.start_time,
duration_tick=note.duration, duration_tick=note.duration,
persiced_time=note.high_precision_start_time, persiced_time=note.high_precision_start_time if is_persiced_time else 0,
percussive=is_percussive_note, percussive=is_percussive_note,
position=sound_position, position=sound_position,
) )
@@ -480,8 +488,8 @@ class SingleTrack(List[SingleNote]):
""" """
if not isinstance(item, SingleNote): if not isinstance(item, SingleNote):
raise TypeError( raise ParameterTypeError(
"单音轨类的元素类型须为单音符SingleNote不可为{}".format( "单音轨类的元素类型须为单音符(`SingleNote`),不可为:`{}`".format(
type(item).__name__ type(item).__name__
) )
) )
@@ -514,15 +522,16 @@ class SingleTrack(List[SingleNote]):
) -> Generator[MineNote, Any, None]: ) -> Generator[MineNote, Any, None]:
"""获取能够用以在我的世界播放的音符数据类""" """获取能够用以在我的世界播放的音符数据类"""
for note in self.get_range(range_start_time, range_end_time): for _note in self.get_range(range_start_time, range_end_time):
yield MineNote.from_single_note( yield MineNote.from_single_note(
note, note=_note,
self.track_instrument, note_instrument=self.track_instrument,
self.track_volume, sound_volume=self.track_volume,
self.is_percussive, is_persiced_time=self.is_high_time_precision,
self.sound_position, is_percussive_note=self.is_percussive,
sound_position=self.sound_position,
**{ **{
item.value: self.argument_curves[item].value_at(note.start_time) # type: ignore item.value: self.argument_curves[item].value_at(_note.start_time) # type: ignore
for item in CurvableParam for item in CurvableParam
if self.argument_curves[item] if self.argument_curves[item]
}, },
@@ -551,7 +560,9 @@ class SingleTrack(List[SingleNote]):
self.extra_info[key[i]] = value[i] self.extra_info[key[i]] = value[i]
else: else:
# 提供简单报错就行了,如果放一堆 if 语句,降低处理速度 # 提供简单报错就行了,如果放一堆 if 语句,降低处理速度
raise TypeError("参数类型错误;键:`{}` 值:`{}`".format(key, value)) raise ParameterTypeError(
"参数类型错误;键:`{}` 值:`{}`".format(key, value)
)
def get_info(self, key: str, default: Any = None) -> Any: def get_info(self, key: str, default: Any = None) -> Any:
"""获取附加信息""" """获取附加信息"""
@@ -632,7 +643,9 @@ class SingleMusic(List[SingleTrack]):
self.extra_info[key[i]] = value[i] self.extra_info[key[i]] = value[i]
else: else:
# 提供简单报错就行了,如果放一堆 if 语句,降低处理速度 # 提供简单报错就行了,如果放一堆 if 语句,降低处理速度
raise TypeError("参数类型错误;键:`{}` 值:`{}`".format(key, value)) raise ParameterTypeError(
"参数类型错误;键:`{}` 值:`{}`".format(key, value)
)
def get_info(self, key: str, default: Any = None) -> Any: def get_info(self, key: str, default: Any = None) -> Any:
"""获取附加信息""" """获取附加信息"""

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
一些报错类型 储 音·创 v3 用到的一些报错类型
""" """
""" """
版权所有 © 2025 金羿 & 诸葛亮与八卦阵 版权所有 © 2025 金羿 & 玉衡
Copyright © 2025 Eilles & bgArray Copyright © 2025 Eilles & Alioth
开源相关声明请见 仓库根目录下的 License.md 开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
@@ -17,146 +17,211 @@ Terms & Conditions: License.md in the root directory
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
class MSCTBaseException(Exception):
"""音·创 的所有错误均继承于此""" class MusicreaterBaseException(Exception):
"""音·创 v3 的所有错误均继承于此"""
def __init__(self, *args): def __init__(self, *args):
"""音·创 的所有错误均继承于此""" """音·创 的所有错误均继承于此"""
super().__init__("音·创", *args) super().__init__("[音·创] - ", *args)
def meow( def meow(self):
self,
):
for i in self.args: for i in self.args:
print(i + "") print(i + "~", end="")
def crash_it(self): def crash_it(self):
raise self raise self
def __str__(self) -> str:
return "".join(self.args)
class MidiFormatException(MSCTBaseException):
"""音·创 的所有MIDI格式错误均继承于此""" # =====================================
# NOTE
# 面对用户时候爆出去的我们认为这就是“外部错误”
# 如果是在程序内部数据传输等情况下出现的就是“内部错误”
# 例如,无法读取文件,这就是一个外部错误
# 某个参数的数据类型错误,这就是内部错误
# =====================================
class MusicreaterInnerlyError(MusicreaterBaseException):
"""内部错误"""
def __init__(self, *args): def __init__(self, *args):
"""音·创 的所有MIDI格式错误均继承于此""" """内部错误(面向开发者的报错信息)"""
super().__init__("MIDI 格式错误", *args) super().__init__("内部错误 - ", *args)
class MusicreaterOuterlyError(MusicreaterBaseException):
class MidiDestroyedError(MSCTBaseException): """外部错误"""
"""Midi文件损坏"""
def __init__(self, *args): def __init__(self, *args):
"""Midi文件损坏""" """外部错误(面向用户的报错信息)"""
super().__init__("MIDI文件损坏无法读取 MIDI 文件", *args) super().__init__("外部错误 - ", *args)
# class MidiUnboundError(MSCTBaseException):
# """未定义Midi对象无用"""
# def __init__(self, *args):
# """未绑定Midi对象"""
# super().__init__("未定义MidiFile对象你甚至没有对象就想要生孩子", *args)
# 此错误在本版本内已经不再使用
class CommandFormatError(MSCTBaseException, RuntimeError): class InnerlyParameterError(MusicreaterInnerlyError):
"""指令格式与目标格式不匹配而引起的错误""" """内部传参错误"""
def __init__(self, *args): def __init__(self, *args):
"""指令格式与目标格式不匹配而引起的错误""" """参数错误"""
super().__init__("指令格式不匹配", *args) super().__init__("传参错误 - ", *args)
# class CrossNoteError(MidiFormatException): class ParameterTypeError(InnerlyParameterError, TypeError):
# """同通道下同音符交叉出现所产生的错误""" """参数类型错误"""
# def __init__(self, *args):
# """同通道下同音符交叉出现所产生的错误"""
# super().__init__("同通道下同音符交叉", *args)
# 这TM是什么错误
# 我什么时候写的这玩意?
# 我哪知道这说的是啥?
#
# 我知道这是什么了 —— 金羿 2025 0401
# 两个其他属性相同的音符在同一个通道,出现连续两个开音信息和连续两个停止信息
# 那么这两个音符的音长无法判断。这是个好问题,但是不是我现在能解决的,也不是我们现在想解决的问题
class NotDefineTempoError(MidiFormatException):
"""没有Tempo设定导致时间无法计算的错误"""
def __init__(self, *args): def __init__(self, *args):
"""没有Tempo设定导致时间无法计算的错误""" """参数类型错误"""
super().__init__("在曲目开始时没有声明 Tempo未指定拍长", *args) super().__init__("参数类型错误:", *args)
class ChannelOverFlowError(MidiFormatException): class ParameterValueError(InnerlyParameterError, ValueError):
"""一个midi中含有过多的通道""" """参数值存在错误"""
def __init__(self, max_channel=16, *args):
"""一个midi中含有过多的通道"""
super().__init__("含有过多的通道(数量应≤{}".format(max_channel), *args)
class NotDefineProgramError(MidiFormatException):
"""没有Program设定导致没有乐器可以选择的错误"""
def __init__(self, *args): def __init__(self, *args):
"""没有Program设定导致没有乐器可以选择的错误""" """参数其值存在错误"""
super().__init__("未指定演奏乐器", *args) super().__init__("参数数值错误:", *args)
class NoteOnOffMismatchError(MidiFormatException):
"""音符开音和停止不匹配的错误""" class OuterlyParameterError(MusicreaterOuterlyError):
"""外部参数错误"""
def __init__(self, *args): def __init__(self, *args):
"""音符开音和停止不匹配的错误""" """参数错误"""
super().__init__("音符不匹配", *args) super().__init__("参数错误 - ", *args)
class LyricMismatchError(MSCTBaseException): class ZeroSpeedError(OuterlyParameterError, ZeroDivisionError):
"""歌词匹配解析错误"""
def __init__(self, *args):
"""有可能产生了错误的歌词解析"""
super().__init__("歌词解析错误", *args)
class ZeroSpeedError(MSCTBaseException, ZeroDivisionError):
"""以0作为播放速度的错误""" """以0作为播放速度的错误"""
def __init__(self, *args): def __init__(self, *args):
"""以0作为播放速度的错误""" """以0作为播放速度的错误"""
super().__init__("播放速度为零", *args) super().__init__("播放速度为零", *args)
class IllegalMinimumVolumeError(MSCTBaseException, ValueError): class IllegalMinimumVolumeError(OuterlyParameterError, ValueError):
"""最小播放音量有误的错误""" """最小播放音量有误的错误"""
def __init__(self, *args): def __init__(self, *args):
"""最小播放音量错误""" """最小播放音量错误"""
super().__init__("最小播放音量超出范围", *args) super().__init__("最小播放音量超出范围", *args)
class MusicSequenceDecodeError(MSCTBaseException):
"""音乐序列解码错误"""
class NoteBinaryDecodeError(MusicreaterOuterlyError):
"""音乐存储二进制数据解码错误"""
def __init__(self, *args): def __init__(self, *args):
"""音乐序列无法正确解码的错误""" """音乐存储二进制数据无法正确解码"""
super().__init__("解码音符序列文件时出现问题", *args) super().__init__("解码音乐存储二进制数据时出现问题 - ", *args)
class MusicSequenceTypeError(MSCTBaseException): class SingleNoteDecodeError(NoteBinaryDecodeError):
"""音乐序列类型错误""" """单个音符的二进制数据解码错误"""
def __init__(self, *args): def __init__(self, *args):
"""无法识别音符序列字节码的类型""" """单个音符的二进制数据无法正确解码"""
super().__init__("错误的音符序列字节类型", *args) super().__init__("音符解码出错:", *args)
class MusicSequenceVerificationFailed(MusicSequenceDecodeError): class NoteBinaryFileTypeError(NoteBinaryDecodeError):
"""音乐序列校验失败""" """音乐存储二进制数据的文件类型错误"""
def __init__(self, *args): def __init__(self, *args):
"""音符序列文件与其校验值不一致""" """无法识别音乐存储文件的类型"""
super().__init__("音符序列文件校验失败", *args) super().__init__("无法识别音乐存储文件对应的类型:", *args)
class NoteBinaryFileVerificationFailed(NoteBinaryDecodeError):
"""音乐存储二进制数据校验失败"""
def __init__(self, *args):
"""音乐存储文件与其校验值不一致"""
super().__init__("音乐存储文件校验失败:", *args)
class PluginDefineError(MusicreaterInnerlyError):
"""插件定义错误(内部相关)"""
def __init__(self, *args):
"""插件本身存在错误"""
super().__init__("插件内部错误 - ", *args)
class PluginInstanceNotFoundError(PluginDefineError, LookupError):
"""插件实例未找到"""
def __init__(self, *args):
"""插件实例未找到"""
super().__init__("插件实例未找到:", *args)
class PluginAttributeNotFoundError(PluginDefineError, AttributeError):
"""插件属性定义错误"""
def __init__(self, *args):
"""插件属性定义错误"""
super().__init__("插件类的必要属性不存在:", *args)
class PluginMetainfoError(PluginDefineError):
"""插件元信息定义错误"""
def __init__(self, *args):
"""插件元信息定义错误"""
super().__init__("插件元信息定义错误 - ", *args)
class PluginMetainfoTypeError(PluginMetainfoError, TypeError):
"""插件元信息定义类型错误"""
def __init__(self, *args):
"""插件元信息定义类型错误"""
super().__init__("插件元信息类型错误:", *args)
class PluginMetainfoValueError(PluginMetainfoError, ValueError):
"""插件元信息定义值错误"""
def __init__(self, *args):
"""插件元信息定义值错误"""
super().__init__("插件元信息数值错误:", *args)
class PluginMetainfoNotFoundError(PluginMetainfoError, PluginAttributeNotFoundError):
"""插件元信息定义缺少错误"""
def __init__(self, *args):
"""插件元信息定义缺少错误"""
super().__init__("插件元信息未定义:", *args)
class PluginLoadError(MusicreaterOuterlyError):
"""插件加载错误(外部相关)"""
def __init__(self, *args):
"""插件加载错误"""
super().__init__("插件加载错误 - ", *args)
class PluginConfigRelatedError(MusicreaterOuterlyError):
"""插件配置相关错误"""
def __init__(self, *args):
"""插件配置相关错误"""
super().__init__("插件配置相关错误 - ", *args)
class PluginConfigLoadError(PluginLoadError, PluginConfigRelatedError):
"""插件配置加载错误"""
def __init__(self, *args):
"""配置文件无法加载"""
super().__init__("插件配置文件加载错误:", *args)
class PluginConfigDumpError(PluginConfigRelatedError):
"""插件配置保存错误"""
def __init__(self, *args):
"""配置文件无法保存"""
super().__init__("插件配置文件保存错误:", *args)

View File

@@ -18,8 +18,8 @@ Terms & Conditions: License.md in the root directory
from typing import Dict, List, Tuple from typing import Dict, List, Tuple
from .exceptions import * from .old_exceptions import *
from .main import ( from .old_main import (
MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE,
MM_CLASSIC_PITCHED_INSTRUMENT_TABLE, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE,
MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
@@ -30,7 +30,7 @@ from .main import (
from .constants import MIDI_PAN, MIDI_PROGRAM, MIDI_VOLUME from .constants import MIDI_PAN, MIDI_PROGRAM, MIDI_VOLUME
from .subclass import * from .subclass import *
from .types import ChannelType, FittingFunctionType from .old_types import ChannelType, FittingFunctionType
from .utils import * from .utils import *

View File

@@ -0,0 +1,165 @@
# -*- coding: utf-8 -*-
"""
存放一些报错类型
"""
"""
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
Copyright © 2025 Eilles & bgArray
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
class MSCTBaseException(Exception):
"""音·创 的所有错误均继承于此"""
def __init__(self, *args):
"""音·创 的所有错误均继承于此"""
super().__init__("音·创", *args)
def meow(
self,
):
for i in self.args:
print(i + "喵!")
def crash_it(self):
raise self
class MidiFormatException(MSCTBaseException):
"""音·创 的所有MIDI格式错误均继承于此"""
def __init__(self, *args):
"""音·创 的所有MIDI格式错误均继承于此"""
super().__init__("MIDI 格式错误", *args)
class MidiDestroyedError(MSCTBaseException):
"""Midi文件损坏"""
def __init__(self, *args):
"""Midi文件损坏"""
super().__init__("MIDI文件损坏无法读取 MIDI 文件", *args)
# class MidiUnboundError(MSCTBaseException):
# """未定义Midi对象无用"""
# def __init__(self, *args):
# """未绑定Midi对象"""
# super().__init__("未定义MidiFile对象你甚至没有对象就想要生孩子", *args)
# 此错误在本版本内已经不再使用
class CommandFormatError(MSCTBaseException, RuntimeError):
"""指令格式与目标格式不匹配而引起的错误"""
def __init__(self, *args):
"""指令格式与目标格式不匹配而引起的错误"""
super().__init__("指令格式不匹配", *args)
# class CrossNoteError(MidiFormatException):
# """同通道下同音符交叉出现所产生的错误"""
# def __init__(self, *args):
# """同通道下同音符交叉出现所产生的错误"""
# super().__init__("同通道下同音符交叉", *args)
# 这TM是什么错误
# 我什么时候写的这玩意?
# 我哪知道这说的是啥?
#
# 我知道这是什么了 —— 金羿 2025 0401
# 两个其他属性相同的音符在同一个通道,出现连续两个开音信息和连续两个停止信息
# 那么这两个音符的音长无法判断。这是个好问题,但是不是我现在能解决的,也不是我们现在想解决的问题
class NotDefineTempoError(MidiFormatException):
"""没有Tempo设定导致时间无法计算的错误"""
def __init__(self, *args):
"""没有Tempo设定导致时间无法计算的错误"""
super().__init__("在曲目开始时没有声明 Tempo未指定拍长", *args)
class ChannelOverFlowError(MidiFormatException):
"""一个midi中含有过多的通道"""
def __init__(self, max_channel=16, *args):
"""一个midi中含有过多的通道"""
super().__init__("含有过多的通道(数量应≤{}".format(max_channel), *args)
class NotDefineProgramError(MidiFormatException):
"""没有Program设定导致没有乐器可以选择的错误"""
def __init__(self, *args):
"""没有Program设定导致没有乐器可以选择的错误"""
super().__init__("未指定演奏乐器", *args)
class NoteOnOffMismatchError(MidiFormatException):
"""音符开音和停止不匹配的错误"""
def __init__(self, *args):
"""音符开音和停止不匹配的错误"""
super().__init__("音符不匹配", *args)
class LyricMismatchError(MSCTBaseException):
"""歌词匹配解析错误"""
def __init__(self, *args):
"""有可能产生了错误的歌词解析"""
super().__init__("歌词解析错误", *args)
# 已重构
class ZeroSpeedError(MSCTBaseException, ZeroDivisionError):
"""以0作为播放速度的错误"""
def __init__(self, *args):
"""以0作为播放速度的错误"""
super().__init__("播放速度为零", *args)
# 已重构
class IllegalMinimumVolumeError(MSCTBaseException, ValueError):
"""最小播放音量有误的错误"""
def __init__(self, *args):
"""最小播放音量错误"""
super().__init__("最小播放音量超出范围", *args)
# 已重构
class MusicSequenceDecodeError(MSCTBaseException):
"""音乐序列解码错误"""
def __init__(self, *args):
"""音乐序列无法正确解码的错误"""
super().__init__("解码音符序列文件时出现问题", *args)
# 已重构
class MusicSequenceTypeError(MSCTBaseException):
"""音乐序列类型错误"""
def __init__(self, *args):
"""无法识别音符序列字节码的类型"""
super().__init__("错误的音符序列字节类型", *args)
# 已重构
class MusicSequenceVerificationFailed(MusicSequenceDecodeError):
"""音乐序列校验失败"""
def __init__(self, *args):
"""音符序列文件与其校验值不一致"""
super().__init__("音符序列文件校验失败", *args)

View File

@@ -37,9 +37,9 @@ from itertools import chain
import mido import mido
from .constants import * from .constants import *
from .exceptions import * from .old_exceptions import *
from .subclass import * from .subclass import *
from .types import * from .old_types import *
from .utils import * from .utils import *
""" """

View File

@@ -16,7 +16,7 @@ import os
import shutil import shutil
from typing import Literal, Optional, Tuple from typing import Literal, Optional, Tuple
from ...main import MidiConvert from ...old_main import MidiConvert
from ...subclass import ProgressBarStyle from ...subclass import ProgressBarStyle
from ..archive import behavior_mcpack_manifest, compress_zipfile from ..archive import behavior_mcpack_manifest, compress_zipfile
from ..mcstructure import ( from ..mcstructure import (

View File

@@ -17,7 +17,7 @@ from typing import Optional
import brotli import brotli
from ...main import MidiConvert from ...old_main import MidiConvert
from ...subclass import MineCommand, ProgressBarStyle from ...subclass import MineCommand, ProgressBarStyle
from ..bdx import ( from ..bdx import (
bdx_move, bdx_move,

View File

@@ -14,7 +14,7 @@ Terms & Conditions: License.md in the root directory
import os import os
from typing import Literal from typing import Literal
from ...main import MidiConvert from ...old_main import MidiConvert
from ...subclass import MineCommand from ...subclass import MineCommand
from ..mcstructure import ( from ..mcstructure import (
COMPABILITY_VERSION_117, COMPABILITY_VERSION_117,

View File

@@ -16,8 +16,8 @@ Terms & Conditions: License.md in the root directory
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from ..exceptions import NotDefineProgramError, ZeroSpeedError from ..old_exceptions import NotDefineProgramError, ZeroSpeedError
from ..main import MidiConvert from ..old_main import MidiConvert
from ..subclass import MineCommand from ..subclass import MineCommand
from ..utils import inst_to_sould_with_deviation, perc_inst_to_soundID_withX from ..utils import inst_to_sould_with_deviation, perc_inst_to_soundID_withX

View File

@@ -19,7 +19,7 @@ from typing import List, Literal, Optional, Tuple
import fcwslib import fcwslib
from ...main import MidiConvert from ...old_main import MidiConvert
from ...subclass import MineCommand, ProgressBarStyle from ...subclass import MineCommand, ProgressBarStyle

73
Musicreater/old_types.py Normal file
View File

@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
"""
存放数据类型的定义
"""
"""
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
Copyright © 2025 Eilles & bgArray
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Callable, Dict, List, Literal, Mapping, Tuple, Union
from .subclass import MineNote
MidiNoteNameTableType = Mapping[int, Tuple[str, ...]]
"""
Midi音符名称对照表类型
"""
MidiInstrumentTableType = Mapping[int, str]
"""
Midi乐器对照表类型
"""
FittingFunctionType = Callable[[float], float]
"""
拟合函数类型
"""
ChannelType = Dict[
int,
Dict[
int,
List[
Union[
Tuple[Literal["PgmC"], int, int],
Tuple[Literal["NoteS"], int, int, int],
Tuple[Literal["NoteE"], int, int],
]
],
],
]
"""
以字典所标记的通道信息类型(已弃用)
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
"""
MineNoteChannelType = Mapping[
int,
List[MineNote,],
]
"""
我的世界通道信息类型
Dict[int,Dict[int,List[MineNote,],],]
"""
MineNoteTrackType = Mapping[
int,
List[MineNote,],
]

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
存储音·创音轨所需的参数曲线 存储 音·创 v3 内部数据使用的参数曲线
""" """
""" """
版权所有 © 2025 金羿 版权所有 © 2026 金羿
Copyright © 2025 Eilles Copyright © 2026 Eilles
开源相关声明请见 仓库根目录下的 License.md 开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
@@ -17,7 +17,7 @@ Terms & Conditions: License.md in the root directory
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
# WARNING 本文件中使用之功能尚未启用 # WARNING 本文件所含之功能未经完整测试
# 鉴于白谭若佬给出的建议:本功能应是处于低优先级开发的 # 鉴于白谭若佬给出的建议:本功能应是处于低优先级开发的
# 因此暂时用处不大,可以稍微放一会再进行开发 # 因此暂时用处不大,可以稍微放一会再进行开发
# 目前用人工智能生成了部分代码,只经过简单的测试 # 目前用人工智能生成了部分代码,只经过简单的测试

783
Musicreater/plugin.py Normal file
View File

@@ -0,0 +1,783 @@
# -*- coding: utf-8 -*-
"""
存储 音·创 v3 的插件接口与管理相关,提供抽象基类以供其他插件使用
"""
"""
版权所有 © 2025 金羿
Copyright © 2025 Eilles
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
# =====================
# NOTE: [WARNING]
# 这个文件是一坨屎山代码
# 请勿模仿,请多包容
# =====================
import sys
from abc import ABC, abstractmethod, ABCMeta
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import (
Dict,
Any,
Optional,
List,
Tuple,
Union,
Sequence,
BinaryIO,
Generator,
Iterator,
Set,
)
from itertools import chain
if sys.version_info >= (3, 11):
import tomllib
import tomli_w
else:
import tomli as tomllib # 第三方包
import tomli_w
from .exceptions import (
PluginConfigDumpError,
PluginConfigLoadError,
PluginMetainfoNotFoundError,
PluginMetainfoTypeError,
PluginMetainfoValueError,
PluginAttributeNotFoundError,
ParameterTypeError,
PluginInstanceNotFoundError,
)
from .data import SingleMusic, SingleTrack
__all__ = [
# 枚举类
"PluginType",
# 抽象基类/数据类(插件参数定义)
"PluginConfig",
"PluginMetaInformation",
# 抽象基类(插件定义)
"MusicInputPlugin",
"TrackInputPlugin",
"MusicOperatePlugin",
"TrackOperatePlugin",
"MusicOutputPlugin",
"TrackOutputPlugin",
"ServicePlugin",
"LibraryPlugin",
# 插件注册用装饰函数
"music_input_plugin",
"track_input_plugin",
"music_operate_plugin",
"track_operate_plugin",
"music_output_plugin",
"track_output_plugin",
"service_plugin",
"library_plugin",
# 全局插件注册表
"plugin_registry",
]
@dataclass
class PluginConfig(ABC):
"""插件配置基类"""
def to_dict(self) -> Dict[str, Any]:
"""字典化配置文件"""
return {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "PluginConfig":
"""从字典创建配置实例"""
# 只保留类中定义的字段
field_names = {f.name for f in cls.__dataclass_fields__.values()}
filtered_data = {k: v for k, v in data.items() if k in field_names}
return cls(**filtered_data)
def save_to_file(self, file_path: Path) -> None:
"""保存配置到文件"""
if file_path.suffix.upper() == ".TOML":
file_path.parent.mkdir(parents=True, exist_ok=True)
else:
raise PluginConfigDumpError(
"插件配置文件类型不应为`{}`,须为`TOML`格式。".format(file_path.suffix)
)
try:
with file_path.open("wb") as f:
tomli_w.dump(self.to_dict(), f, multiline_strings=False, indent=4)
except Exception as e:
raise PluginConfigDumpError(e)
@classmethod
def load_from_file(cls, file_path: Path) -> "PluginConfig":
"""从文件加载配置"""
try:
with file_path.open("rb") as f:
return cls.from_dict(tomllib.load(f))
except Exception as e:
raise PluginConfigLoadError(e)
class PluginType(str, Enum):
"""插件类型枚举"""
FUNCTION_IMPORT = "import_data"
FUNCTION_EXPORT = "export_data"
FUNCTION_OPERATE = "data_operate"
SERVICE = "service"
LIBRARY = "library"
@dataclass
class PluginMetaInformation(ABC):
"""插件元信息"""
name: str
"""插件名称,应为惟一之名"""
author: str
"""插件作者"""
description: str
"""插件简介"""
version: Tuple[int, ...]
"""插件版本号"""
type: PluginType
"""插件类型"""
license: str = "MIT License"
"""插件发布时采用的许可协议"""
dependencies: Sequence[str] = []
"""插件是否对其他插件存在依赖"""
class TopBasePlugin(ABC):
"""所有插件的抽象基类"""
metainfo: PluginMetaInformation
"""插件元信息"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if hasattr(cls, "metainfo"):
if not isinstance(cls.metainfo, PluginMetaInformation):
raise PluginMetainfoTypeError(
"类`{cls_name}`之属性`metainfo`的类型,必须为`PluginMetaInformation`".format(
cls_name=cls.__name__
)
)
else:
raise PluginMetainfoNotFoundError(
"类`{cls_name}`必须定义一个`metainfo`属性。".format(
cls_name=cls.__name__
)
)
class TopInOutBasePlugin(TopBasePlugin, ABC):
"""导入导出用抽象基类"""
supported_formats: Tuple[str, ...] = tuple()
"""支持的格式"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if hasattr(cls, "supported_formats"):
if cls.supported_formats:
# 强制转换为大写,并使用元组
cls.supported_formats = tuple(map(str.upper, cls.supported_formats))
else:
cls.supported_formats = tuple()
else:
raise PluginAttributeNotFoundError(
"用于导入导出数据的类`{cls_name}`必须定义一个`supported_formats`属性。".format(
cls_name=cls.__name__
)
)
def can_handle_file(self, file_path: Path) -> bool:
"""判断是否可处理某个文件"""
return file_path.suffix.upper().endswith(self.supported_formats)
def can_handle_format(self, format_name: str) -> bool:
"""判断是否可处理某个格式"""
return format_name.upper().endswith(self.supported_formats)
class MusicInputPlugin(TopInOutBasePlugin, ABC):
"""导入用插件抽象基类-完整曲目"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if cls.metainfo.type != PluginType.FUNCTION_IMPORT:
raise PluginMetainfoValueError(
"插件类`{cls_name}`是从`MusicInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
cls_name=cls.__name__,
cls_type=cls.metainfo.type.name,
)
)
@abstractmethod
def loadbytes(
self, bytes_buffer_in: BinaryIO, config: Optional[PluginConfig]
) -> "SingleMusic":
"""从字节流加载数据到完整曲目"""
pass
def load(self, file_path: Path, config: Optional[PluginConfig]) -> "SingleMusic":
"""从文件加载数据到完整曲目"""
with file_path.open("rb") as f:
return self.loadbytes(f, config)
class TrackInputPlugin(TopInOutBasePlugin, ABC):
"""导入用插件抽象基类-单个音轨"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if cls.metainfo.type != PluginType.FUNCTION_IMPORT:
raise PluginMetainfoValueError(
"插件类`{cls_name}`是从`TrackInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
cls_name=cls.__name__,
cls_type=cls.metainfo.type.name,
)
)
@abstractmethod
def loadbytes(
self, bytes_buffer_in: BinaryIO, config: Optional[PluginConfig]
) -> "SingleTrack":
"""从字节流加载音符数据到单个音轨"""
pass
def load(self, file_path: Path, config: Optional[PluginConfig]) -> "SingleTrack":
"""从文件加载音符数据到单个音轨"""
with file_path.open("rb") as f:
return self.loadbytes(f, config)
class MusicOperatePlugin(TopBasePlugin, ABC):
"""音乐处理用插件抽象基类-完整曲目"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if cls.metainfo.type != PluginType.FUNCTION_OPERATE:
raise PluginMetainfoValueError(
"插件类`{cls_name}`是从`MusicOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
cls_name=cls.__name__,
cls_type=cls.metainfo.type.name,
)
)
@abstractmethod
def process(
self, data: "SingleMusic", config: Optional[PluginConfig]
) -> "SingleMusic":
"""处理完整曲目的数据"""
pass
class TrackOperatePlugin(TopBasePlugin, ABC):
"""音乐处理用插件抽象基类-单个音轨"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if cls.metainfo.type != PluginType.FUNCTION_OPERATE:
raise PluginMetainfoValueError(
"插件类`{cls_name}`是从`TrackOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
cls_name=cls.__name__,
cls_type=cls.metainfo.type.name,
)
)
@abstractmethod
def process(
self, data: "SingleTrack", config: Optional[PluginConfig]
) -> "SingleTrack":
"""处理单个音轨的音符数据"""
pass
class MusicOutputPlugin(TopInOutBasePlugin, ABC):
"""导出用插件的抽象基类-完整曲目"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if cls.metainfo.type != PluginType.FUNCTION_EXPORT:
raise PluginMetainfoValueError(
"插件类`{cls_name}`是从`MusicOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
cls_name=cls.__name__,
cls_type=cls.metainfo.type.name,
)
)
@abstractmethod
def dumpbytes(
self, data: "SingleMusic", config: Optional[PluginConfig]
) -> BinaryIO:
"""将完整曲目导出为对应格式的字节流"""
pass
@abstractmethod
def dump(
self, data: "SingleMusic", file_path: Path, config: Optional[PluginConfig]
):
"""将完整曲目导出为对应格式的文件"""
pass
class TrackOutputPlugin(TopInOutBasePlugin, ABC):
"""导出用插件的抽象基类-单个音轨"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if cls.metainfo.type != PluginType.FUNCTION_EXPORT:
raise PluginMetainfoValueError(
"插件类`{cls_name}`是从`TrackOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
cls_name=cls.__name__,
cls_type=cls.metainfo.type.name,
)
)
@abstractmethod
def dumpbytes(
self, data: "SingleTrack", config: Optional[PluginConfig]
) -> BinaryIO:
"""将单个音轨导出为对应格式的字节流"""
pass
@abstractmethod
def dump(
self, data: "SingleTrack", file_path: Path, config: Optional[PluginConfig]
):
"""将单个音轨导出为对应格式的文件"""
pass
class ServicePlugin(TopBasePlugin, ABC):
"""服务插件抽象基类"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if cls.metainfo.type != PluginType.SERVICE:
raise PluginMetainfoValueError(
"插件类`{cls_name}`是从`ServicePlugin`继承的,该类的子类应当为一个`PluginType.SERVICE`类型的插件,而不是`PluginType.{cls_type}`".format(
cls_name=cls.__name__,
cls_type=cls.metainfo.type.name,
)
)
@abstractmethod
def serve(self, config: Optional[PluginConfig], *args) -> None:
"""服务插件的运行逻辑"""
pass
class LibraryPlugin(TopBasePlugin, ABC):
"""插件依赖库的抽象基类"""
def __init_subclass__(cls) -> None:
super().__init_subclass__()
if cls.metainfo.type != PluginType.LIBRARY:
raise PluginMetainfoValueError(
"插件类`{cls_name}`是从`LibraryPlugin`继承的,该类的子类应当为一个`PluginType.LIBRARY`类型的插件,而不是`PluginType.{cls_type}`".format(
cls_name=cls.__name__,
cls_type=cls.metainfo.type.name,
)
)
# 怎么?
# 插件的彼此依赖就不需要什么调用了吧
class PluginRegistry:
"""插件注册管理器"""
def __init__(self):
self._music_input_plugins: List[MusicInputPlugin] = []
self._track_input_plugins: List[TrackInputPlugin] = []
self._music_operate_plugins: List[MusicOperatePlugin] = []
self._track_operate_plugins: List[TrackOperatePlugin] = []
self._music_output_plugins: List[MusicOutputPlugin] = []
self._track_output_plugins: List[TrackOutputPlugin] = []
self._service_plugins: List[ServicePlugin] = []
self._library_plugins: List[LibraryPlugin] = []
def register_music_input_plugin(self, plugin_class: type) -> None:
"""注册输入插件-整首曲目"""
plugin_instance = plugin_class()
self._music_input_plugins.append(plugin_instance)
def register_track_input_plugin(self, plugin_class: type) -> None:
"""注册输入插件-单个音轨"""
plugin_instance = plugin_class()
self._track_input_plugins.append(plugin_instance)
def register_music_operate_plugin(self, plugin_class: type) -> None:
"""注册曲目处理插件"""
plugin_instance = plugin_class()
self._music_operate_plugins.append(plugin_instance)
def register_track_operate_plugin(self, plugin_class: type) -> None:
"""注册音轨处理插件"""
plugin_instance = plugin_class()
self._track_operate_plugins.append(plugin_instance)
def register_music_output_plugin(self, plugin_class: type) -> None:
"""注册输出插件-整首曲目"""
plugin_instance = plugin_class()
self._music_output_plugins.append(plugin_instance)
def register_track_output_plugin(self, plugin_class: type) -> None:
"""注册输出插件-单个音轨"""
plugin_instance = plugin_class()
self._track_output_plugins.append(plugin_instance)
def register_service_plugin(self, plugin_class: type) -> None:
"""注册服务插件"""
plugin_instance = plugin_class()
self._service_plugins.append(plugin_instance)
def register_library_plugin(self, plugin_class: type) -> None:
"""注册支持库插件"""
plugin_instance = plugin_class()
self._library_plugins.append(plugin_instance)
def get_music_input_plugin_by_format(
self, filepath_or_format: Union[Path, str]
) -> Generator[MusicInputPlugin, None, None]:
"""通过指定输入的文件或格式,以获取对应的全曲导入用插件"""
if isinstance(filepath_or_format, str):
for plugin in self._music_input_plugins:
if plugin.can_handle_format(filepath_or_format):
yield plugin
elif isinstance(filepath_or_format, Path):
for plugin in self._music_input_plugins:
if plugin.can_handle_file(filepath_or_format):
yield plugin
else:
raise ParameterTypeError(
"用于指定“导入全曲的数据之类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
type(filepath_or_format), filepath_or_format
)
)
def get_track_input_plugin_by_format(
self, filepath_or_format: Union[Path, str]
) -> Generator[TrackInputPlugin, None, None]:
"""通过指定输入的文件或格式,以获取对应的单音轨导入用插件"""
if isinstance(filepath_or_format, str):
for plugin in self._track_input_plugins:
if plugin.can_handle_format(filepath_or_format):
yield plugin
elif isinstance(filepath_or_format, Path):
for plugin in self._track_input_plugins:
if plugin.can_handle_file(filepath_or_format):
yield plugin
else:
raise ParameterTypeError(
"用于指定“导入单个音轨的数据之类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
type(filepath_or_format), filepath_or_format
)
)
def get_music_output_plugin_by_format(
self, filepath_or_format: Union[Path, str]
) -> Generator[MusicOutputPlugin, None, None]:
"""通过指定输出的文件或格式,以获取对应的导出全曲用插件"""
if isinstance(filepath_or_format, str):
for plugin in self._music_output_plugins:
if plugin.can_handle_format(filepath_or_format):
yield plugin
elif isinstance(filepath_or_format, Path):
for plugin in self._music_output_plugins:
if plugin.can_handle_file(filepath_or_format):
yield plugin
else:
raise ParameterTypeError(
"用于指定“全曲数据导出的类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
type(filepath_or_format), filepath_or_format
)
)
def get_track_output_plugin_by_format(
self, filepath_or_format: Union[Path, str]
) -> Generator[TrackOutputPlugin, None, None]:
"""通过指定输出的文件或格式,以获取对应的导出单个音轨用插件"""
if isinstance(filepath_or_format, str):
for plugin in self._track_output_plugins:
if plugin.can_handle_format(filepath_or_format):
yield plugin
elif isinstance(filepath_or_format, Path):
for plugin in self._track_output_plugins:
if plugin.can_handle_file(filepath_or_format):
yield plugin
else:
raise ParameterTypeError(
"用于指定“单音轨数据导出的类型”的参数,其类型须为`Path`路径或字符串,而非`{}`类型的`{}`值".format(
type(filepath_or_format), filepath_or_format
)
)
def get_music_input_plugin(self, plugin_name: str) -> MusicInputPlugin:
"""获取指定名称的全曲导入用插件,当名称重叠时,取版本号最大的"""
try:
return max(
filter(
lambda plugin: plugin.metainfo.name == plugin_name,
self._music_input_plugins,
),
key=lambda plugin: plugin.metainfo.version,
)
except ValueError:
raise PluginInstanceNotFoundError(
"未找到“用于导入曲目、名为`{}`”的插件".format(plugin_name)
)
def get_track_input_plugin(self, plugin_name: str) -> TrackInputPlugin:
"""获取指定名称的单音轨导入用插件,当名称重叠时,取版本号最大的"""
try:
return max(
filter(
lambda plugin: plugin.metainfo.name == plugin_name,
self._track_input_plugins,
),
key=lambda plugin: plugin.metainfo.version,
)
except ValueError:
raise PluginInstanceNotFoundError(
"未找到“用于导入单个音轨、名为`{}`”的插件".format(plugin_name)
)
def get_music_operate_plugin(self, plugin_name: str) -> MusicOperatePlugin:
"""获取指定名称的全曲处理用插件,当名称重叠时,取版本号最大的"""
try:
return max(
filter(
lambda plugin: plugin.metainfo.name == plugin_name,
self._music_operate_plugins,
),
key=lambda plugin: plugin.metainfo.version,
)
except ValueError:
raise PluginInstanceNotFoundError(
"未找到“用于处理整个曲目、名为`{}`”的插件".format(plugin_name)
)
def get_track_operate_plugin(self, plugin_name: str) -> TrackOperatePlugin:
"""获取指定名称的单音轨处理用插件,当名称重叠时,取版本号最大的"""
try:
return max(
filter(
lambda plugin: plugin.metainfo.name == plugin_name,
self._track_operate_plugins,
),
key=lambda plugin: plugin.metainfo.version,
)
except ValueError:
raise PluginInstanceNotFoundError(
"未找到“用于处理单个音轨、名为`{}`”的插件".format(plugin_name)
)
def get_music_output_plugin(self, plugin_name: str) -> MusicOutputPlugin:
"""获取指定名称的导出全曲用插件,当名称重叠时,取版本号最大的"""
try:
return max(
filter(
lambda plugin: plugin.metainfo.name == plugin_name,
self._music_output_plugins,
),
key=lambda plugin: plugin.metainfo.version,
)
except ValueError:
raise PluginMetainfoNotFoundError(
"未找到“用于导出完整曲目、名为`{}`”的插件".format(plugin_name)
)
def get_track_output_plugin(self, plugin_name: str) -> TrackOutputPlugin:
"""获取指定名称的导出单音轨用插件,当名称重叠时,取版本号最大的"""
try:
return max(
filter(
lambda plugin: plugin.metainfo.name == plugin_name,
self._track_output_plugins,
),
key=lambda plugin: plugin.metainfo.version,
)
except ValueError:
raise PluginMetainfoNotFoundError(
"未找到“用于导出单个音轨、名为`{}`”的插件".format(plugin_name)
)
def get_service_plugin(self, plugin_name: str) -> ServicePlugin:
"""获取服务用插件,当名称重叠时,取版本号最大的"""
try:
return max(
filter(
lambda plugin: plugin.metainfo.name == plugin_name,
self._service_plugins,
),
key=lambda plugin: plugin.metainfo.version,
)
except ValueError:
raise PluginInstanceNotFoundError(
"未找到名为`{}`的服务用插件".format(plugin_name)
)
def get_library_plugin(self, plugin_name: str) -> LibraryPlugin:
"""获取依赖库类插件,当名称重叠时,取版本号最高的"""
try:
return max(
filter(
lambda plugin: plugin.metainfo.name == plugin_name,
self._library_plugins,
),
key=lambda plugin: plugin.metainfo.version,
)
except ValueError:
raise PluginInstanceNotFoundError(
"未找到名为`{}`的依赖库插件".format(plugin_name)
)
def supported_input_formats(self) -> Set[str]:
"""所有支持的导入格式"""
return set(
chain.from_iterable(
plugin.supported_formats
for plugin in chain(
self._music_input_plugins, self._track_input_plugins
)
)
)
def supported_output_formats(self) -> Set[str]:
"""所有支持的导出格式"""
return set(
chain.from_iterable(
plugin.supported_formats
for plugin in chain(
self._music_output_plugins, self._track_output_plugins
)
)
)
plugin_registry = PluginRegistry()
"""全局插件注册表实例"""
def music_input_plugin(metainfo: PluginMetaInformation):
"""全曲输入用插件装饰器"""
def decorator(cls):
global plugin_registry
cls.metainfo = metainfo
plugin_registry.register_music_input_plugin(cls)
return cls
return decorator
def track_input_plugin(metainfo: PluginMetaInformation):
"""单轨输入用插件装饰器"""
def decorator(cls):
global plugin_registry
cls.metainfo = metainfo
plugin_registry.register_track_input_plugin(cls)
return cls
return decorator
def music_operate_plugin(metainfo: PluginMetaInformation):
"""全曲处理用插件装饰器"""
def decorator(cls):
global plugin_registry
cls.metainfo = metainfo
plugin_registry.register_music_operate_plugin(cls)
return cls
return decorator
def track_operate_plugin(metainfo: PluginMetaInformation):
"""音轨处理插件装饰器"""
def decorator(cls):
global plugin_registry
cls.metainfo = metainfo
plugin_registry.register_track_operate_plugin(cls)
return cls
return decorator
def music_output_plugin(metainfo: PluginMetaInformation):
"""乐曲输出用插件装饰器"""
def decorator(cls):
global plugin_registry
cls.metainfo = metainfo
plugin_registry.register_music_output_plugin(cls)
return cls
return decorator
def track_output_plugin(metainfo: PluginMetaInformation):
"""音轨输出用插件装饰器"""
def decorator(cls):
global plugin_registry
cls.metainfo = metainfo
plugin_registry.register_track_output_plugin(cls)
return cls
return decorator
def service_plugin(metainfo: PluginMetaInformation):
"""服务插件装饰器"""
def decorator(cls):
global plugin_registry
cls.metainfo = metainfo
plugin_registry.register_service_plugin(cls)
return cls
return decorator
def library_plugin(metainfo: PluginMetaInformation):
"""支持库插件装饰器"""
def decorator(cls):
global plugin_registry
cls.metainfo = metainfo
plugin_registry.register_library_plugin(cls)
return cls
return decorator

22
Musicreater/plugins.py Normal file
View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""
存储 音·创 v3 的插件管理和上层设计内容
"""
"""
版权所有 © 2025 金羿
Copyright © 2025 Eilles
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import List, Optional, Dict, Generator, Any
from pathlib import Path
from .plugin import MusicInputPlugin, MusicOperatePlugin, MusicOutputPlugin, TrackInputPlugin, TrackOperatePlugin, TrackOutputPlugin, ServicePlugin, LibraryPlugin

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
放数据类型的定义 储 音·创 v3 定义的一些数据类型,可以用于类型检查器
""" """
""" """
版权所有 © 2025 金羿 & 诸葛亮与八卦阵 版权所有 © 2025 金羿 & 玉衡
Copyright © 2025 Eilles & bgArray Copyright © 2025 Eilles & Alioth
开源相关声明请见 仓库根目录下的 License.md 开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
@@ -16,58 +16,10 @@ Terms & Conditions: License.md in the root directory
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Callable, Dict, List, Literal, Mapping, Tuple, Union from typing import Callable, Dict, List, Literal, Mapping, Tuple, Union
from .subclass import MineNote
MidiNoteNameTableType = Mapping[int, Tuple[str, ...]]
"""
Midi音符名称对照表类型
"""
MidiInstrumentTableType = Mapping[int, str]
"""
Midi乐器对照表类型
"""
FittingFunctionType = Callable[[float], float] FittingFunctionType = Callable[[float], float]
""" """
拟合函数类型 拟合函数类型
""" """
ChannelType = Dict[
int,
Dict[
int,
List[
Union[
Tuple[Literal["PgmC"], int, int],
Tuple[Literal["NoteS"], int, int, int],
Tuple[Literal["NoteE"], int, int],
]
],
],
]
"""
以字典所标记的通道信息类型(已弃用)
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
"""
MineNoteChannelType = Mapping[
int,
List[MineNote,],
]
"""
我的世界通道信息类型
Dict[int,Dict[int,List[MineNote,],],]
"""
MineNoteTrackType = Mapping[
int,
List[MineNote,],
]

View File

@@ -40,9 +40,9 @@ from .constants import (
MM_INSTRUMENT_DEVIATION_TABLE, MM_INSTRUMENT_DEVIATION_TABLE,
MM_INSTRUMENT_RANGE_TABLE, MM_INSTRUMENT_RANGE_TABLE,
) )
from .exceptions import MusicSequenceDecodeError from .old_exceptions import MusicSequenceDecodeError
from .subclass import MineNote, mctick2timestr, SingleNoteBox from .subclass import MineNote, mctick2timestr, SingleNoteBox
from .types import MidiInstrumentTableType, MineNoteChannelType, FittingFunctionType from .old_types import MidiInstrumentTableType, MineNoteChannelType, FittingFunctionType
def empty_midi_channels( def empty_midi_channels(

View File

@@ -1,15 +1,15 @@
import Musicreater import Musicreater
import Musicreater.experiment import Musicreater.experiment
import Musicreater.plugin import Musicreater.old_plugin
# import Musicreater.previous # import Musicreater.previous
from Musicreater.plugin.addonpack import ( from Musicreater.old_plugin.addonpack import (
to_addon_pack_in_delay, to_addon_pack_in_delay,
to_addon_pack_in_repeater, to_addon_pack_in_repeater,
to_addon_pack_in_score, to_addon_pack_in_score,
) )
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score from Musicreater.old_plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
from Musicreater.plugin.mcstructfile import ( from Musicreater.old_plugin.mcstructfile import (
to_mcstructure_file_in_delay, to_mcstructure_file_in_delay,
to_mcstructure_file_in_repeater, to_mcstructure_file_in_repeater,
to_mcstructure_file_in_score, to_mcstructure_file_in_score,
@@ -21,7 +21,7 @@ MSCT_MAIN = (
# Musicreater.previous, # Musicreater.previous,
) )
MSCT_PLUGIN = (Musicreater.plugin,) MSCT_PLUGIN = (Musicreater.old_plugin,)
MSCT_PLUGIN_FUNCTION = ( MSCT_PLUGIN_FUNCTION = (
to_addon_pack_in_delay, to_addon_pack_in_delay,

View File

@@ -19,18 +19,18 @@ Terms & Conditions: ./License.md
import os import os
import Musicreater import Musicreater
from Musicreater.plugin.addonpack import ( from Musicreater.old_plugin.addonpack import (
to_addon_pack_in_delay, to_addon_pack_in_delay,
to_addon_pack_in_repeater, to_addon_pack_in_repeater,
to_addon_pack_in_score, to_addon_pack_in_score,
) )
from Musicreater.plugin.mcstructfile import ( from Musicreater.old_plugin.mcstructfile import (
to_mcstructure_file_in_delay, to_mcstructure_file_in_delay,
to_mcstructure_file_in_repeater, to_mcstructure_file_in_repeater,
to_mcstructure_file_in_score, to_mcstructure_file_in_score,
) )
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score from Musicreater.old_plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
# 获取midi列表 # 获取midi列表
midi_path = input(f"请输入MIDI路径") midi_path = input(f"请输入MIDI路径")

View File

@@ -1,9 +1,9 @@
import Musicreater.experiment import Musicreater.experiment
import Musicreater.plugin import Musicreater.old_plugin
import Musicreater.plugin.mcstructfile import Musicreater.old_plugin.mcstructfile
print( print(
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay( Musicreater.old_plugin.mcstructfile.to_mcstructure_file_in_delay(
Musicreater.experiment.FutureMidiConvertM4.from_midi_file( Musicreater.experiment.FutureMidiConvertM4.from_midi_file(
input("midi路径:"), old_exe_format=False input("midi路径:"), old_exe_format=False
), ),

View File

@@ -1,9 +1,9 @@
import Musicreater import Musicreater
import Musicreater.plugin import Musicreater.old_plugin
import Musicreater.plugin.mcstructfile import Musicreater.old_plugin.mcstructfile
print( print(
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay( Musicreater.old_plugin.mcstructfile.to_mcstructure_file_in_delay(
Musicreater.MidiConvert.from_midi_file( Musicreater.MidiConvert.from_midi_file(
input("midi路径:"), input("midi路径:"),
old_exe_format=False, old_exe_format=False,

View File

@@ -1,13 +1,13 @@
import Musicreater import Musicreater
import Musicreater.plugin import Musicreater.old_plugin
import Musicreater.plugin.websocket import Musicreater.old_plugin.websocket
import os import os
dire = input("midi目录") dire = input("midi目录")
print( print(
Musicreater.plugin.websocket.to_websocket_server( Musicreater.old_plugin.websocket.to_websocket_server(
[ [
Musicreater.MidiConvert.from_midi_file( Musicreater.MidiConvert.from_midi_file(
os.path.join(dire, names), old_exe_format=False os.path.join(dire, names), old_exe_format=False

View File

@@ -11,7 +11,7 @@ import shutil
from typing import Optional, Tuple from typing import Optional, Tuple
import Musicreater.experiment import Musicreater.experiment
from Musicreater.plugin.archive import compress_zipfile from Musicreater.old_plugin.archive import compress_zipfile
from Musicreater.utils import guess_deviation, is_in_diapason from Musicreater.utils import guess_deviation, is_in_diapason

View File

@@ -4,6 +4,8 @@
requires-python = ">= 3.8, < 4.0" requires-python = ">= 3.8, < 4.0"
dependencies = [ dependencies = [
"mido >= 1.3", "mido >= 1.3",
"tomli>=2.4.0; python_version < '3.11'",
"tomli-w>=1.0.0",
"xxhash >= 3", "xxhash >= 3",
] ]
@@ -47,7 +49,7 @@
full = [ full = [
"TrimMCStruct <= 0.0.5.9", "TrimMCStruct <= 0.0.5.9",
"brotli >= 1.0.0", "brotli >= 1.0.0",
"numpy" "numpy",
] ]
dev = [ dev = [
"TrimMCStruct <= 0.0.5.9", "TrimMCStruct <= 0.0.5.9",

View File

@@ -1,6 +1,6 @@
import Musicreater.experiment import Musicreater.experiment
import Musicreater.plugin import Musicreater.old_plugin
import Musicreater.plugin.mcstructfile import Musicreater.old_plugin.mcstructfile
msct = Musicreater.experiment.FutureMidiConvertKamiRES.from_midi_file( msct = Musicreater.experiment.FutureMidiConvertKamiRES.from_midi_file(
input("midi路径:"), old_exe_format=False input("midi路径:"), old_exe_format=False
@@ -24,7 +24,7 @@ for name in sorted(
print( print(
"\n输出:", "\n输出:",
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay( Musicreater.old_plugin.mcstructfile.to_mcstructure_file_in_delay(
msct, msct,
opt, opt,
# Musicreater.plugin.ConvertConfig(input("输出路径:"),), # Musicreater.plugin.ConvertConfig(input("输出路径:"),),

View File

@@ -1,6 +1,6 @@
import Musicreater.experiment import Musicreater.experiment
import Musicreater.plugin import Musicreater.old_plugin
import Musicreater.plugin.mcstructfile import Musicreater.old_plugin.mcstructfile
msct = Musicreater.experiment.FutureMidiConvertLyricSupport.from_midi_file( msct = Musicreater.experiment.FutureMidiConvertLyricSupport.from_midi_file(
input("midi路径:"), old_exe_format=False input("midi路径:"), old_exe_format=False
@@ -24,7 +24,7 @@ opt = input("输出路径:")
print( print(
"\n输出:", "\n输出:",
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay( Musicreater.old_plugin.mcstructfile.to_mcstructure_file_in_delay(
msct, msct,
opt, opt,
# Musicreater.plugin.ConvertConfig(input("输出路径:"),), # Musicreater.plugin.ConvertConfig(input("输出路径:"),),

90
uv.lock generated
View File

@@ -1,5 +1,5 @@
version = 1 version = 1
revision = 1 revision = 3
requires-python = ">=3.8, <4.0" requires-python = ">=3.8, <4.0"
resolution-markers = [ resolution-markers = [
"python_full_version >= '3.10'", "python_full_version >= '3.10'",
@@ -578,6 +578,9 @@ name = "musicreater"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "mido" }, { name = "mido" },
{ name = "tomli" },
{ name = "tomli-w", version = "1.0.0", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version < '3.9'" },
{ name = "tomli-w", version = "1.2.0", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version >= '3.9'" },
{ name = "xxhash" }, { name = "xxhash" },
] ]
@@ -592,6 +595,9 @@ dev = [
] ]
full = [ full = [
{ name = "brotli" }, { name = "brotli" },
{ name = "numpy", version = "1.24.4", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version < '3.9'" },
{ name = "numpy", version = "2.0.2", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version == '3.9.*'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "trimmcstruct" }, { name = "trimmcstruct" },
] ]
@@ -601,8 +607,11 @@ requires-dist = [
{ name = "brotli", marker = "extra == 'full'", specifier = ">=1.0.0" }, { name = "brotli", marker = "extra == 'full'", specifier = ">=1.0.0" },
{ name = "dill", marker = "extra == 'dev'" }, { name = "dill", marker = "extra == 'dev'" },
{ name = "mido", specifier = ">=1.3" }, { name = "mido", specifier = ">=1.3" },
{ name = "numpy", marker = "extra == 'full'" },
{ name = "pyinstaller", marker = "extra == 'dev'" }, { name = "pyinstaller", marker = "extra == 'dev'" },
{ name = "rich", marker = "extra == 'dev'" }, { name = "rich", marker = "extra == 'dev'" },
{ name = "tomli" },
{ name = "tomli-w" },
{ name = "trimmcstruct", marker = "extra == 'dev'", specifier = "<=0.0.5.9" }, { name = "trimmcstruct", marker = "extra == 'dev'", specifier = "<=0.0.5.9" },
{ name = "trimmcstruct", marker = "extra == 'full'", specifier = "<=0.0.5.9" }, { name = "trimmcstruct", marker = "extra == 'full'", specifier = "<=0.0.5.9" },
{ name = "twine", marker = "extra == 'dev'" }, { name = "twine", marker = "extra == 'dev'" },
@@ -1041,6 +1050,85 @@ wheels = [
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/58/29/93c53c098d301132196c3238c312825324740851d77a8500a2462c0fd888/setuptools-80.8.0-py3-none-any.whl", hash = "sha256:95a60484590d24103af13b686121328cc2736bee85de8936383111e421b9edc0" }, { url = "https://mirror.nju.edu.cn/pypi/web/packages/58/29/93c53c098d301132196c3238c312825324740851d77a8500a2462c0fd888/setuptools-80.8.0-py3-none-any.whl", hash = "sha256:95a60484590d24103af13b686121328cc2736bee85de8936383111e421b9edc0" },
] ]
[[package]]
name = "tomli"
version = "2.4.0"
source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }
sdist = { url = "https://mirror.nju.edu.cn/pypi/web/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c" }
wheels = [
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4" },
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a" },
]
[[package]]
name = "tomli-w"
version = "1.0.0"
source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }
resolution-markers = [
"python_full_version < '3.9'",
]
sdist = { url = "https://mirror.nju.edu.cn/pypi/web/packages/49/05/6bf21838623186b91aedbda06248ad18f03487dc56fbc20e4db384abde6c/tomli_w-1.0.0.tar.gz", hash = "sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9" }
wheels = [
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/bb/01/1da9c66ecb20f31ed5aa5316a957e0b1a5e786a0d9689616ece4ceaf1321/tomli_w-1.0.0-py3-none-any.whl", hash = "sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463" },
]
[[package]]
name = "tomli-w"
version = "1.2.0"
source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }
resolution-markers = [
"python_full_version >= '3.10'",
"python_full_version == '3.9.*'",
]
sdist = { url = "https://mirror.nju.edu.cn/pypi/web/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021" }
wheels = [
{ url = "https://mirror.nju.edu.cn/pypi/web/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90" },
]
[[package]] [[package]]
name = "trimmcstruct" name = "trimmcstruct"
version = "0.0.5.9" version = "0.0.5.9"