"""Event data models for VTube Studio API."""
# TODO:
# - make event configs fields optional
# - document fields
# - replace type str for path where appropriate
# - check for enums in API docs
from ast import Str
from typing import Optional, List, Dict, Any, Literal, Union
from enum import Enum
from pydantic import BaseModel, Field
from vtpy.data.common import (
BaseRequest,
BaseResponse,
BaseEvent,
ErrorData,
MessageType,
HotkeyAction,
)
__all__ = [
"EventType",
"EVENT_MODEL_MAP",
"WindowSize",
"AnimationEventType",
"ItemEventType",
"BaseEventSubscriptionRequest",
"EventSubscriptionResponseData",
"EventSubscriptionResponse",
"MouseButtonID",
"TestEventSubscriptionRequestConfig",
"TestEventSubscriptionRequestData",
"TestEventSubscriptionRequest",
"TestEventData",
"TestEvent",
"ModelLoadedEventSubscriptionRequestConfig",
"ModelLoadedEventSubscriptionRequestData",
"ModelLoadedEventSubscriptionRequest",
"ModelLoadedEventData",
"ModelLoadedEvent",
"TrackingStatusChangedEventSubscriptionRequestConfig",
"TrackingStatusChangedEventSubscriptionRequestData",
"TrackingStatusChangedEventSubscriptionRequest",
"TrackingStatusChangedEventData",
"TrackingStatusChangedEvent",
"BackgroundChangedEventSubscriptionRequestConfig",
"BackgroundChangedEventSubscriptionRequestData",
"BackgroundChangedEventSubscriptionRequest",
"BackgroundChangedEventData",
"BackgroundChangedEvent",
"ModelConfigChangedEventSubscriptionRequestConfig",
"ModelConfigChangedEventSubscriptionRequestData",
"ModelConfigChangedEventSubscriptionRequest",
"ModelConfigChangedEventData",
"ModelConfigChangedEvent",
"ModelMovedEventSubscriptionRequestConfig",
"ModelMovedEventSubscriptionRequestData",
"ModelMovedEventSubscriptionRequest",
"ModelPositionData",
"ModelMovedEventData",
"ModelMovedEvent",
"ModelOutlineEventSubscriptionRequestConfig",
"ModelOutlineEventSubscriptionRequestData",
"ModelOutlineEventSubscriptionRequest",
"ConvexHullPoint",
"ModelOutlineEventData",
"ModelOutlineEvent",
"HotkeyTriggeredEventSubscriptionRequestConfig",
"HotkeyTriggeredEventSubscriptionRequestData",
"HotkeyTriggeredEventSubscriptionRequest",
"HotkeyTriggeredEventData",
"HotkeyTriggeredEvent",
"ModelAnimationEventSubscriptionRequestConfig",
"ModelAnimationEventSubscriptionRequestData",
"ModelAnimationEventSubscriptionRequest",
"ModelAnimationEventData",
"ModelAnimationEvent",
"ItemEventSubscriptionRequestConfig",
"ItemEventSubscriptionRequestData",
"ItemEventSubscriptionRequest",
"ItemPosition",
"ItemEventData",
"ItemEvent",
"ModelClickedEventSubscriptionRequestConfig",
"ModelClickedEventSubscriptionRequestData",
"ModelClickedEventSubscriptionRequest",
"HitInfo",
"ArtMeshHit",
"ClickPosition",
"ModelClickedEventData",
"ModelClickedEvent",
"PostProcessingEventSubscriptionRequestConfig",
"PostProcessingEventSubscriptionRequestData",
"PostProcessingEventSubscriptionRequest",
"PostProcessingEventData",
"PostProcessingEvent",
"Live2DCubismEditorConnectedEventSubscriptionRequestConfig",
"Live2DCubismEditorConnectedEventSubscriptionRequestData",
"Live2DCubismEditorConnectedEventSubscriptionRequest",
"Live2DCubismEditorConnectedEventData",
"Live2DCubismEditorConnectedEvent",
]
# ============================================================================
# Event Types
# ============================================================================
[docs]
class EventType(Enum):
"""Event type identifiers."""
TestEvent = "TestEvent"
ModelLoadedEvent = "ModelLoadedEvent"
TrackingStatusChangedEvent = "TrackingStatusChangedEvent"
BackgroundChangedEvent = "BackgroundChangedEvent"
ModelConfigChangedEvent = "ModelConfigChangedEvent"
ModelMovedEvent = "ModelMovedEvent"
ModelOutlineEvent = "ModelOutlineEvent"
HotkeyTriggeredEvent = "HotkeyTriggeredEvent"
ModelAnimationEvent = "ModelAnimationEvent"
ItemEvent = "ItemEvent"
ModelClickedEvent = "ModelClickedEvent"
PostProcessingEvent = "PostProcessingEvent"
Live2DCubismEditorConnectedEvent = "Live2DCubismEditorConnectedEvent"
[docs]
class AnimationEventType(Enum):
Custom = "Custom"
Start = "Start"
End = "End"
[docs]
class ItemEventType(Enum):
Added = "Added"
Removed = "Removed"
DroppedPinned = "DroppedPinned"
DroppedUnpinned = "DroppedUnpinned"
Clicked = "Clicked"
Locked = "Locked"
Unlocked = "Unlocked"
# ============================================================================
# Helper Types
# ============================================================================
[docs]
class WindowSize(BaseModel):
x: float
y: float
# ============================================================================
# Event Sub
# ============================================================================
[docs]
class BaseEventSubscriptionRequest(BaseRequest):
"""Request to subscribe to events."""
messageType: Literal[MessageType.EventSubscriptionRequest] = (
MessageType.EventSubscriptionRequest
)
[docs]
class EventSubscriptionResponseData(BaseModel):
"""Data for event subscription response."""
subscribedEventCount: int
subscribedEvents: List[EventType]
[docs]
class EventSubscriptionResponse(BaseResponse):
"""Response from event subscription request."""
messageType: Literal[MessageType.EventSubscriptionResponse] = (
MessageType.EventSubscriptionResponse
)
data: Union[EventSubscriptionResponseData, ErrorData]
# ============================================================================
# Test Events
# ============================================================================
[docs]
class TestEventSubscriptionRequestConfig(BaseModel):
"""Config for test event subscription request."""
testMessageForEvent: Optional[str] = Field(
None, description="Test message returned in the event."
)
[docs]
class TestEventSubscriptionRequestData(BaseModel):
"""Config for test event subscription request."""
eventName: Literal[EventType.TestEvent] = EventType.TestEvent
subscribe: bool = True
config: TestEventSubscriptionRequestConfig
[docs]
class TestEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: TestEventSubscriptionRequestData
[docs]
class TestEventData(BaseModel):
"""Data for model loaded event."""
yourTestMessage: str = Field(description="Test message returned in the event.")
counter: int = Field(description="Counter of the event.")
[docs]
class TestEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.TestEvent] = EventType.TestEvent
data: TestEventData
# ============================================================================
# Model Events
# ============================================================================
[docs]
class ModelLoadedEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
modelID: Optional[List[str]] = Field(None, description="The ID of the model to listen for.")
[docs]
class ModelLoadedEventSubscriptionRequestData(BaseModel):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.ModelLoadedEvent] = EventType.ModelLoadedEvent
subscribe: bool = True
config: ModelLoadedEventSubscriptionRequestConfig
[docs]
class ModelLoadedEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: ModelLoadedEventSubscriptionRequestData
[docs]
class ModelLoadedEventData(BaseModel):
"""Data for model loaded event."""
modelLoaded: bool
modelName: str
modelID: str
[docs]
class ModelLoadedEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.ModelLoadedEvent] = EventType.ModelLoadedEvent
data: ModelLoadedEventData
# ============================================================================
# Tracking Events
# ============================================================================
[docs]
class TrackingStatusChangedEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
[docs]
class TrackingStatusChangedEventSubscriptionRequestData(BaseModel):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.TrackingStatusChangedEvent] = EventType.TrackingStatusChangedEvent
subscribe: bool = True
config: TrackingStatusChangedEventSubscriptionRequestConfig
[docs]
class TrackingStatusChangedEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: TrackingStatusChangedEventSubscriptionRequestData
[docs]
class TrackingStatusChangedEventData(BaseModel):
"""Data for model loaded event."""
faceFound: bool
leftHandFound: bool
rightHandFound: bool
[docs]
class TrackingStatusChangedEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.TrackingStatusChangedEvent] = (
EventType.TrackingStatusChangedEvent
)
data: TrackingStatusChangedEventData
# ============================================================================
# Background Events
# ============================================================================
[docs]
class BackgroundChangedEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
[docs]
class BackgroundChangedEventSubscriptionRequestData(BaseModel):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.BackgroundChangedEvent] = EventType.BackgroundChangedEvent
subscribe: bool = True
config: BackgroundChangedEventSubscriptionRequestConfig
[docs]
class BackgroundChangedEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: BackgroundChangedEventSubscriptionRequestData
[docs]
class BackgroundChangedEventData(BaseModel):
"""Data for model loaded event."""
backgroundName: str
[docs]
class BackgroundChangedEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.BackgroundChangedEvent] = EventType.BackgroundChangedEvent
data: BackgroundChangedEventData
# ============================================================================
# Model Config Events
# ============================================================================
[docs]
class ModelConfigChangedEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
[docs]
class ModelConfigChangedEventSubscriptionRequestData(BaseModel):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.ModelConfigChangedEvent] = EventType.ModelConfigChangedEvent
subscribe: bool = True
config: ModelConfigChangedEventSubscriptionRequestConfig
[docs]
class ModelConfigChangedEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: ModelConfigChangedEventSubscriptionRequestData
[docs]
class ModelConfigChangedEventData(BaseModel):
"""Data for model loaded event."""
modelID: str
modelName: str
hotkeyConfigChanged: bool
[docs]
class ModelConfigChangedEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.ModelConfigChangedEvent] = EventType.ModelConfigChangedEvent
data: ModelConfigChangedEventData
# ============================================================================
# Model Moved Events
# ============================================================================
[docs]
class ModelMovedEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
[docs]
class ModelMovedEventSubscriptionRequestData(BaseModel):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.ModelMovedEvent] = EventType.ModelMovedEvent
subscribe: bool = True
config: ModelMovedEventSubscriptionRequestConfig
[docs]
class ModelMovedEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: ModelMovedEventSubscriptionRequestData
[docs]
class ModelPositionData(BaseModel):
"""Data for model position."""
positionX: float
positionY: float
rotation: float
size: float
[docs]
class ModelMovedEventData(BaseModel):
"""Data for model loaded event."""
modelID: str
modelName: str
modelPosition: ModelPositionData
[docs]
class ModelMovedEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.ModelMovedEvent] = EventType.ModelMovedEvent
data: ModelMovedEventData
# ============================================================================
# Model Outline Events
# ============================================================================
[docs]
class ModelOutlineEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
draw: Optional[bool] = Field(None, description="Whether to draw the model outline.")
[docs]
class ModelOutlineEventSubscriptionRequestData(BaseModel):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.ModelOutlineEvent] = EventType.ModelOutlineEvent
subscribe: bool = True
config: ModelOutlineEventSubscriptionRequestConfig
[docs]
class ModelOutlineEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: ModelOutlineEventSubscriptionRequestData
[docs]
class ConvexHullPoint(BaseModel):
x: float
y: float
[docs]
class ModelOutlineEventData(BaseModel):
"""Data for model loaded event."""
modelID: str
modelName: str
convexHull: List[ConvexHullPoint]
convexHullCenter: ConvexHullPoint
windowSize: WindowSize
[docs]
class ModelOutlineEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.ModelOutlineEvent] = EventType.ModelOutlineEvent
data: ModelOutlineEventData
# ============================================================================
# Hotkey Events
# ============================================================================
[docs]
class HotkeyTriggeredEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
onlyForAction: Optional[HotkeyAction] = Field(
None, description="Only trigger events for this action."
)
ignoreHotkeysTriggeredByAPI: bool = Field(
False, description="Ignore hotkeys triggered by the API."
)
[docs]
class HotkeyTriggeredEventSubscriptionRequestData(BaseModel):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.HotkeyTriggeredEvent] = EventType.HotkeyTriggeredEvent
subscribe: bool = True
config: HotkeyTriggeredEventSubscriptionRequestConfig
[docs]
class HotkeyTriggeredEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: HotkeyTriggeredEventSubscriptionRequestData
[docs]
class HotkeyTriggeredEventData(BaseModel):
"""Data for model loaded event."""
hotkeyID: str
hotkeyName: str
hotkeyAction: HotkeyAction
hotkeyFile: str
hotkeyTriggeredByAPI: bool
modelID: str
modelName: str
isLive2DItem: bool
[docs]
class HotkeyTriggeredEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.HotkeyTriggeredEvent] = EventType.HotkeyTriggeredEvent
data: HotkeyTriggeredEventData
# ============================================================================
# Animation Events
# ============================================================================
[docs]
class ModelAnimationEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
ignoreLive2DItems: bool = Field(False, description="Ignore live2d items.")
ignoreIdleAnimations: bool = Field(False, description="Ignore idle animations.")
[docs]
class ModelAnimationEventSubscriptionRequestData(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.ModelAnimationEvent] = EventType.ModelAnimationEvent
subscribe: bool = True
config: ModelAnimationEventSubscriptionRequestConfig
[docs]
class ModelAnimationEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: ModelAnimationEventSubscriptionRequestData
[docs]
class ModelAnimationEventData(BaseModel):
"""Data for model loaded event."""
animationEventType: AnimationEventType
animationEventTime: float
animationEventData: str
animationName: str
animationLength: float
isIdleAnimation: bool
modelID: str
modelName: str
isLive2DItem: bool
[docs]
class ModelAnimationEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.ModelAnimationEvent] = EventType.ModelAnimationEvent
data: ModelAnimationEventData
# ============================================================================
# Item Events
# ============================================================================
[docs]
class ItemEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
itemInstanceIDs: Optional[List[str]] = Field(
None, description="The IDs of the items to listen for."
)
itemFileNames: Optional[List[str]] = Field(
None, description="The file names of the items to listen for."
)
[docs]
class ItemEventSubscriptionRequestData(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.ItemEvent] = EventType.ItemEvent
subscribe: bool = True
config: ItemEventSubscriptionRequestConfig
[docs]
class ItemEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: ItemEventSubscriptionRequestData
[docs]
class ItemPosition(BaseModel):
x: float
y: float
[docs]
class ItemEventData(BaseModel):
"""Data for model loaded event."""
itemEventType: ItemEventType
itemInstanceID: str
itemFileName: str
itemPosition: ItemPosition
[docs]
class ItemEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.ItemEvent] = EventType.ItemEvent
data: ItemEventData
# ============================================================================
# Model Clicked Events
# ============================================================================
[docs]
class ModelClickedEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
onlyClicksOnModel: Optional[bool] = Field(
True, description="Only trigger events for clicks on the model."
)
[docs]
class ModelClickedEventSubscriptionRequestData(BaseModel):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.ModelClickedEvent] = EventType.ModelClickedEvent
subscribe: bool = True
config: ModelClickedEventSubscriptionRequestConfig
[docs]
class ModelClickedEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: ModelClickedEventSubscriptionRequestData
[docs]
class HitInfo(BaseModel):
modelID: str
artMeshID: str
angle: float
size: float
vertexID1: int
vertexID2: int
vertexID3: int
vertexWeight1: float
vertexWeight2: float
vertexWeight3: float
[docs]
class ArtMeshHit(BaseModel):
artMeshOrder: int
isMasked: bool
hitInfo: HitInfo
[docs]
class ClickPosition(BaseModel):
x: float
y: float
[docs]
class ModelClickedEventData(BaseModel):
"""Data for model loaded event."""
modelLoaded: bool
loadedModelID: str
loadedModelName: str
modelWasClicked: bool
mouseButtonID: MouseButtonID
clickPosition: ClickPosition
windowSize: WindowSize
clickedArtMeshCount: int
artMeshHits: List[ArtMeshHit]
[docs]
class ModelClickedEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.ModelClickedEvent] = EventType.ModelClickedEvent
data: ModelClickedEventData
# ============================================================================
# Post Processing Events
# ============================================================================
[docs]
class PostProcessingEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
[docs]
class PostProcessingEventSubscriptionRequestData(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.PostProcessingEvent] = EventType.PostProcessingEvent
subscribe: bool = True
config: PostProcessingEventSubscriptionRequestConfig
[docs]
class PostProcessingEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: PostProcessingEventSubscriptionRequestData
[docs]
class PostProcessingEventData(BaseModel):
"""Data for model loaded event."""
currentState: bool
currentPreset: str
[docs]
class PostProcessingEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.PostProcessingEvent] = EventType.PostProcessingEvent
data: PostProcessingEventData
# ============================================================================
# Live2D Cubism Editor Events
# ============================================================================
[docs]
class Live2DCubismEditorConnectedEventSubscriptionRequestConfig(BaseModel):
"""Config for model loaded event subscription request."""
[docs]
class Live2DCubismEditorConnectedEventSubscriptionRequestData(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
eventName: Literal[EventType.Live2DCubismEditorConnectedEvent] = (
EventType.Live2DCubismEditorConnectedEvent
)
subscribe: bool = True
config: Live2DCubismEditorConnectedEventSubscriptionRequestConfig
[docs]
class Live2DCubismEditorConnectedEventSubscriptionRequest(BaseEventSubscriptionRequest):
"""Request to subscribe to model loaded events."""
data: Live2DCubismEditorConnectedEventSubscriptionRequestData
[docs]
class Live2DCubismEditorConnectedEventData(BaseModel):
"""Data for model loaded event."""
tryingToConnect: bool
connected: bool
shouldSendParameters: bool
[docs]
class Live2DCubismEditorConnectedEvent(BaseEvent):
"""Event fired when a model is loaded."""
messageType: Literal[EventType.Live2DCubismEditorConnectedEvent] = (
EventType.Live2DCubismEditorConnectedEvent
)
data: Live2DCubismEditorConnectedEventData
# Mapping of event types to their corresponding Pydantic models
EVENT_MODEL_MAP: Dict[EventType, type] = {
EventType.TestEvent: TestEvent,
EventType.ModelLoadedEvent: ModelLoadedEvent,
EventType.TrackingStatusChangedEvent: TrackingStatusChangedEvent,
EventType.BackgroundChangedEvent: BackgroundChangedEvent,
EventType.ModelConfigChangedEvent: ModelConfigChangedEvent,
EventType.ModelMovedEvent: ModelMovedEvent,
EventType.ModelOutlineEvent: ModelOutlineEvent,
EventType.HotkeyTriggeredEvent: HotkeyTriggeredEvent,
EventType.ModelAnimationEvent: ModelAnimationEvent,
EventType.ItemEvent: ItemEvent,
EventType.ModelClickedEvent: ModelClickedEvent,
EventType.PostProcessingEvent: PostProcessingEvent,
EventType.Live2DCubismEditorConnectedEvent: Live2DCubismEditorConnectedEvent,
}