756 lines
19 KiB (Stored with Git LFS)
Markdown
756 lines
19 KiB (Stored with Git LFS)
Markdown
# StreamDock Plugin SDK 공식 문서 백업
|
|
|
|
> **출처**: https://sdk.key123.vip/
|
|
> **GitHub**: https://github.com/MiraboxSpace/StreamDock-Plugin-SDK
|
|
> **백업일**: 2025-01-27
|
|
|
|
---
|
|
|
|
## 📋 목차
|
|
|
|
1. [SDK 개요](#sdk-개요)
|
|
2. [⚠️ 중요: 자주 발생하는 오류 및 해결법](#중요-자주-발생하는-오류-및-해결법)
|
|
3. [지원 언어 및 SDK](#지원-언어-및-sdk)
|
|
4. [플러그인이 수신하는 이벤트](#플러그인이-수신하는-이벤트)
|
|
5. [플러그인이 전송하는 이벤트](#플러그인이-전송하는-이벤트)
|
|
6. [메시지 구조](#메시지-구조)
|
|
7. [버전 요구사항](#버전-요구사항)
|
|
|
|
---
|
|
|
|
## SDK 개요
|
|
|
|
### Stream Dock란?
|
|
Stream Dock는 **여러 개의 전용 프로그래밍 가능한 버튼을 제공하는 USB 주변기기**입니다.
|
|
|
|
**하드웨어 모델:**
|
|
- 293
|
|
- 293s
|
|
- 293N3
|
|
- 293N4
|
|
|
|
### 에코시스템 구성
|
|
1. **하드웨어 장치**: 물리적인 StreamDock 버튼 장치
|
|
2. **데스크톱 소프트웨어**: 구성 및 관리 소프트웨어
|
|
3. **플러그인 시스템**: 확장 가능한 플러그인 아키텍처
|
|
|
|
### SDK 설계 원칙
|
|
|
|
1. **강력함 (Powerful)**: 키 이벤트 트리거를 통해 코드 실행
|
|
2. **단순함 (Simple)**: JSON 기반 통신 프로토콜 사용
|
|
3. **언어 독립적 (Language-Agnostic)**: WebSocket을 지원하는 모든 언어 사용 가능 (JavaScript 권장)
|
|
4. **안전함 (Secure)**: 각 플러그인을 별도 프로세스로 격리
|
|
5. **크로스 플랫폼 (Cross-Platform)**: macOS와 Windows 호환
|
|
|
|
### 주요 기능
|
|
- 프로그래밍 가능한 버튼에 사용자 정의 액션 할당
|
|
- 아이콘 및 레이블로 버튼 외형 커스터마이징
|
|
- Property Inspector 인터페이스를 통한 액션 구성
|
|
- 서드파티 플러그인 및 마켓플레이스 통합
|
|
|
|
### 기술 기반
|
|
**WebSocket 통신**을 사용하여 플러그인-호스트 간 상호작용을 수행합니다.
|
|
|
|
---
|
|
|
|
## ⚠️ 중요: 자주 발생하는 오류 및 해결법
|
|
|
|
### 🐛 문제: keyUp 이벤트에서 action UUID를 전달하지 않아 잘못된 액션이 실행됨
|
|
|
|
**증상**:
|
|
- 올바른 버튼을 클릭했는데 다른 액션이 실행됨
|
|
- 설정(Settings)이 비어있어서 기본값으로 폴백됨
|
|
- `didReceiveSettings`에서 받은 설정에 `actionType` 같은 커스텀 필드가 없음
|
|
|
|
**원인**:
|
|
```javascript
|
|
case 'keyUp':
|
|
handleButtonClick(jsonObj.context); // ❌ action UUID를 전달하지 않음!
|
|
break;
|
|
```
|
|
|
|
StreamDock SDK는 버튼의 설정을 **영구 저장**하지만, 설정이 비어있거나 초기화되지 않은 경우:
|
|
1. `willAppear` 이벤트에서 설정을 초기화해도 `setSettings`을 호출하지 않으면 저장되지 않음
|
|
2. `keyUp` 이벤트에서 `getCurrentSettings(context)`로 가져오면 빈 객체 반환
|
|
3. 기본값으로 폴백되어 의도하지 않은 액션 실행
|
|
|
|
**해결 방법**:
|
|
|
|
#### 방법 1: keyUp에서 action UUID 전달 (권장)
|
|
```javascript
|
|
case 'keyUp':
|
|
console.log('🔘 버튼 클릭됨!');
|
|
console.log('🔍 Action UUID:', jsonObj.action);
|
|
handleButtonClick(jsonObj.context, jsonObj.action); // ✅ action UUID 전달
|
|
break;
|
|
|
|
// 핸들러에서 action UUID로 actionType 결정
|
|
function handleButtonClick(context, actionUUID) {
|
|
const settings = getCurrentSettings(context);
|
|
let actionType = settings.actionType;
|
|
|
|
// actionType이 없으면 action UUID로 판단
|
|
if (!actionType && actionUUID) {
|
|
if (actionUUID === 'com.example.myaction') {
|
|
actionType = 'myaction';
|
|
} else if (actionUUID === 'com.example.otheraction') {
|
|
actionType = 'otheraction';
|
|
} else {
|
|
actionType = 'default';
|
|
}
|
|
|
|
// 설정에 저장
|
|
settings.actionType = actionType;
|
|
setCurrentSettings(context, settings);
|
|
}
|
|
|
|
// actionType에 따라 처리
|
|
switch (actionType) {
|
|
case 'myaction':
|
|
handleMyAction();
|
|
break;
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 방법 2: willAppear에서 setSettings 호출
|
|
```javascript
|
|
case 'willAppear':
|
|
let settings = jsonObj.payload?.settings || {};
|
|
|
|
// action UUID로 actionType 설정
|
|
if (jsonObj.action === 'com.example.myaction') {
|
|
settings.actionType = 'myaction';
|
|
}
|
|
|
|
buttonContexts.set(jsonObj.context, settings);
|
|
|
|
// ✅ StreamDock SDK에 영구 저장
|
|
if (websocket) {
|
|
const setSettingsMessage = {
|
|
event: 'setSettings',
|
|
context: jsonObj.context,
|
|
payload: settings
|
|
};
|
|
websocket.send(JSON.stringify(setSettingsMessage));
|
|
}
|
|
break;
|
|
```
|
|
|
|
#### 방법 3: didReceiveSettings에서 보정
|
|
```javascript
|
|
case 'didReceiveSettings':
|
|
if (jsonObj.payload && jsonObj.context) {
|
|
const newSettings = jsonObj.payload.settings || {};
|
|
|
|
// actionType이 없으면 action UUID로 보정
|
|
if (!newSettings.actionType && jsonObj.action) {
|
|
if (jsonObj.action === 'com.example.myaction') {
|
|
newSettings.actionType = 'myaction';
|
|
|
|
// StreamDock SDK에 저장
|
|
if (websocket) {
|
|
const setSettingsMessage = {
|
|
event: 'setSettings',
|
|
context: jsonObj.context,
|
|
payload: newSettings
|
|
};
|
|
websocket.send(JSON.stringify(setSettingsMessage));
|
|
}
|
|
}
|
|
}
|
|
|
|
buttonContexts.set(jsonObj.context, newSettings);
|
|
}
|
|
break;
|
|
```
|
|
|
|
**핵심 교훈**:
|
|
1. **keyUp 이벤트에는 action UUID가 포함되어 있습니다** - 이를 활용하세요!
|
|
2. **Settings는 자동으로 저장되지 않습니다** - `setSettings` 이벤트를 명시적으로 보내야 합니다
|
|
3. **여러 액션 타입을 처리할 때는 action UUID를 직접 확인**하는 것이 가장 안전합니다
|
|
|
|
**실제 발생 사례**:
|
|
- OptiTrack Marker Toggle 버튼을 클릭했는데 Camera Switch 메시지가 전송됨
|
|
- Settings가 `{}`로 비어있어서 기본값인 `camera`로 폴백
|
|
- action UUID (`com.mirabox.streamingle.optitrack_marker_toggle`)를 확인하지 않아서 발생
|
|
|
|
**디버깅 팁**:
|
|
```javascript
|
|
case 'keyUp':
|
|
console.log('============================================');
|
|
console.log('버튼 클릭!');
|
|
console.log('Context:', jsonObj.context);
|
|
console.log('Action UUID:', jsonObj.action); // ⭐ 반드시 로그로 확인
|
|
console.log('Settings:', getCurrentSettings(jsonObj.context));
|
|
console.log('============================================');
|
|
handleButtonClick(jsonObj.context, jsonObj.action);
|
|
break;
|
|
```
|
|
|
|
---
|
|
|
|
## 지원 언어 및 SDK
|
|
|
|
StreamDock Plugin SDK는 다양한 프로그래밍 언어를 지원합니다:
|
|
|
|
### 1. JavaScript SDK (SDJavaScriptSDK)
|
|
- 순수 JavaScript 구현
|
|
- 브라우저 기반 플러그인
|
|
- 가장 단순한 구조
|
|
|
|
### 2. Node.js SDK V2 (SDNodeJsSDKV2) ⭐ 권장
|
|
- Node.js 20.8.1 내장 (StreamDock 3.10.188.226+)
|
|
- log4js 로깅 시스템
|
|
- 파일 기반 로깅 지원
|
|
- WebSocket 재연결 로직
|
|
|
|
### 3. Node.js SDK (SDNodeJsSDK) - Legacy
|
|
- 구 버전 Node.js SDK
|
|
- V2 사용 권장
|
|
|
|
### 4. Vue.js SDK (SDVueSDK)
|
|
- Vue.js 프레임워크 기반
|
|
- 반응형 UI 컴포넌트
|
|
- TypeScript 지원
|
|
|
|
### 5. C++ SDK (StreamDockCPPSDK)
|
|
- 네이티브 C++ 구현
|
|
- 고성능 플러그인
|
|
- Visual Studio 2022 지원
|
|
|
|
### 6. Qt SDK (SDQtSDK)
|
|
- Qt 프레임워크 기반
|
|
- 크로스 플랫폼 UI
|
|
- C++ 기반
|
|
|
|
### 7. Python SDK (SDPythonSDK)
|
|
- 순수 Python 구현
|
|
- PyInstaller로 exe 패키징
|
|
- 타입 힌팅 지원
|
|
|
|
---
|
|
|
|
## 플러그인이 수신하는 이벤트
|
|
|
|
### 공통 이벤트 (Plugin & Property Inspector)
|
|
|
|
#### `didReceiveSettings`
|
|
설정이 변경될 때 트리거됩니다.
|
|
|
|
**파라미터:**
|
|
- `action`: 액션 식별자
|
|
- `context`: 컨텍스트 값
|
|
- `device`: 장치 정보
|
|
- `payload`: 설정 데이터 및 좌표 포함
|
|
|
|
**예시:**
|
|
```json
|
|
{
|
|
"event": "didReceiveSettings",
|
|
"action": "com.example.plugin.action",
|
|
"context": "unique_context_id",
|
|
"device": "device_id",
|
|
"payload": {
|
|
"settings": {
|
|
"key": "value"
|
|
},
|
|
"coordinates": {
|
|
"column": 0,
|
|
"row": 0
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `didReceiveGlobalSettings`
|
|
전역 설정이 변경될 때 트리거됩니다.
|
|
|
|
**파라미터:**
|
|
- `payload`: 전역 설정 데이터
|
|
|
|
---
|
|
|
|
### 플러그인 전용 이벤트
|
|
|
|
#### 입력 이벤트
|
|
|
|
##### `keyDown`
|
|
사용자가 키를 누를 때 발생합니다.
|
|
|
|
**파라미터:**
|
|
- `action`: 액션 식별자
|
|
- `context`: 컨텍스트 값
|
|
- `device`: 장치 ID
|
|
- `payload`:
|
|
- `settings`: 액션 설정
|
|
- `coordinates`: 키 좌표
|
|
- `state`: 현재 상태 (0 또는 1)
|
|
- `isInMultiAction`: Multi-Action 여부
|
|
|
|
**예시:**
|
|
```json
|
|
{
|
|
"event": "keyDown",
|
|
"action": "com.example.plugin.action",
|
|
"context": "context_id",
|
|
"device": "device_id",
|
|
"payload": {
|
|
"settings": {},
|
|
"coordinates": {"column": 2, "row": 1},
|
|
"state": 0,
|
|
"isInMultiAction": false
|
|
}
|
|
}
|
|
```
|
|
|
|
##### `keyUp`
|
|
사용자가 키를 뗄 때 발생합니다. (파라미터는 `keyDown`과 동일)
|
|
|
|
##### `dialDown`
|
|
다이얼(엔코더)을 누를 때 발생합니다.
|
|
|
|
##### `dialUp`
|
|
다이얼(엔코더)을 뗄 때 발생합니다.
|
|
|
|
##### `dialRotate`
|
|
다이얼(엔코더)을 회전할 때 발생합니다.
|
|
|
|
**파라미터:**
|
|
- `payload.ticks`: 회전 틱 수 (양수=시계방향, 음수=반시계방향)
|
|
- `payload.pressed`: 다이얼이 눌려진 상태인지 여부
|
|
|
|
**예시:**
|
|
```json
|
|
{
|
|
"event": "dialRotate",
|
|
"action": "com.example.plugin.action",
|
|
"context": "context_id",
|
|
"device": "device_id",
|
|
"payload": {
|
|
"ticks": 3,
|
|
"pressed": false,
|
|
"settings": {}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### 라이프사이클 이벤트
|
|
|
|
##### `willAppear`
|
|
액션 인스턴스가 화면에 나타날 때 발생합니다.
|
|
|
|
**발생 시점:**
|
|
- 시작 시
|
|
- 프로필 전환 시
|
|
- 키 할당 시
|
|
|
|
**파라미터:**
|
|
- `action`: 액션 식별자
|
|
- `context`: 컨텍스트 값
|
|
- `device`: 장치 ID
|
|
- `payload`:
|
|
- `settings`: 저장된 설정
|
|
- `coordinates`: 버튼 위치
|
|
- `state`: 현재 상태
|
|
- `isInMultiAction`: Multi-Action 여부
|
|
|
|
**예시:**
|
|
```json
|
|
{
|
|
"event": "willAppear",
|
|
"action": "com.mirabox.streamingle.optitrack_marker_toggle",
|
|
"context": "button_context_123",
|
|
"device": "streamdock_device_1",
|
|
"payload": {
|
|
"settings": {
|
|
"currentState": 0
|
|
},
|
|
"coordinates": {"column": 0, "row": 0},
|
|
"state": 0,
|
|
"isInMultiAction": false
|
|
}
|
|
}
|
|
```
|
|
|
|
##### `willDisappear`
|
|
프로필 전환 또는 액션 삭제 시 트리거됩니다.
|
|
|
|
##### `titleParametersDidChange`
|
|
사용자가 제목 또는 제목 파라미터를 수정할 때 발생합니다.
|
|
|
|
**포함 정보:**
|
|
- 폰트 설정
|
|
- 정렬 방식
|
|
- 제목 텍스트
|
|
|
|
---
|
|
|
|
#### 시스템 이벤트
|
|
|
|
##### `deviceDidConnect`
|
|
장치가 연결될 때 발생합니다.
|
|
|
|
**파라미터:**
|
|
- `device`: 장치 ID
|
|
- `deviceInfo`: 장치 타입 및 크기 정보
|
|
|
|
##### `deviceDidDisconnect`
|
|
장치 연결이 해제될 때 발생합니다.
|
|
|
|
##### `applicationDidLaunch`
|
|
지정된 애플리케이션이 실행될 때 발생합니다.
|
|
|
|
**식별자:**
|
|
- macOS: Bundle ID
|
|
- Windows: .exe 파일명
|
|
|
|
##### `applicationDidTerminate`
|
|
지정된 애플리케이션이 종료될 때 발생합니다.
|
|
|
|
##### `systemDidWakeUp`
|
|
컴퓨터가 절전 모드에서 깨어날 때 발생합니다.
|
|
|
|
**주의**: 여러 번 발생할 수 있음
|
|
|
|
---
|
|
|
|
#### Property Inspector 이벤트
|
|
|
|
##### `propertyInspectorDidAppear`
|
|
Property Inspector가 표시될 때 발생합니다.
|
|
|
|
##### `propertyInspectorDidDisappear`
|
|
Property Inspector가 숨겨질 때 발생합니다.
|
|
|
|
##### `sendToPlugin`
|
|
Property Inspector에서 플러그인으로 데이터를 전송합니다.
|
|
|
|
**예시 (Property Inspector → Plugin):**
|
|
```javascript
|
|
// Property Inspector
|
|
sendToPlugin('update_settings', { value: 123 });
|
|
|
|
// Plugin
|
|
websocket.onmessage = (event) => {
|
|
const jsonObj = JSON.parse(event.data);
|
|
if (jsonObj.event === 'sendToPlugin') {
|
|
const payload = jsonObj.payload;
|
|
console.log('Received:', payload);
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 플러그인이 전송하는 이벤트
|
|
|
|
### 공통 이벤트 (Plugin & Property Inspector)
|
|
|
|
#### `setSettings`
|
|
액션 인스턴스의 데이터를 영구적으로 저장합니다.
|
|
|
|
**구조:**
|
|
```json
|
|
{
|
|
"event": "setSettings",
|
|
"context": "context_id",
|
|
"payload": {
|
|
"key1": "value1",
|
|
"key2": 123
|
|
}
|
|
}
|
|
```
|
|
|
|
**사용 예:**
|
|
```javascript
|
|
function saveSettings(context, settings) {
|
|
const message = {
|
|
event: 'setSettings',
|
|
context: context,
|
|
payload: settings
|
|
};
|
|
websocket.send(JSON.stringify(message));
|
|
}
|
|
```
|
|
|
|
#### `getSettings`
|
|
저장된 설정을 요청합니다. `didReceiveSettings` 이벤트로 응답을 받습니다.
|
|
|
|
**구조:**
|
|
```json
|
|
{
|
|
"event": "getSettings",
|
|
"context": "context_id"
|
|
}
|
|
```
|
|
|
|
#### `setGlobalSettings`
|
|
플러그인 전역 설정을 저장합니다. 모든 액션에서 접근 가능합니다.
|
|
|
|
**구조:**
|
|
```json
|
|
{
|
|
"event": "setGlobalSettings",
|
|
"context": "plugin_uuid",
|
|
"payload": {
|
|
"globalKey": "globalValue"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `getGlobalSettings`
|
|
전역 설정을 요청합니다. `didReceiveGlobalSettings`로 응답을 받습니다.
|
|
|
|
#### `openUrl`
|
|
기본 브라우저에서 URL을 엽니다.
|
|
|
|
**구조:**
|
|
```json
|
|
{
|
|
"event": "openUrl",
|
|
"payload": {
|
|
"url": "https://example.com"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `logMessage`
|
|
디버그 메시지를 로그 창에 작성합니다.
|
|
|
|
**구조:**
|
|
```json
|
|
{
|
|
"event": "logMessage",
|
|
"payload": {
|
|
"message": "Debug message here"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 플러그인 전용 이벤트
|
|
|
|
#### `setTitle`
|
|
액션 인스턴스의 제목을 동적으로 업데이트합니다.
|
|
|
|
**파라미터:**
|
|
- `context`: 컨텍스트 ID
|
|
- `payload.title`: 제목 텍스트 (줄바꿈: `\n`)
|
|
- `payload.target`: 대상 (0=하드웨어+소프트웨어, 1=하드웨어만, 2=소프트웨어만)
|
|
- `payload.state`: 상태 (다중 상태 액션용)
|
|
|
|
**예시:**
|
|
```javascript
|
|
function setButtonTitle(context, title) {
|
|
const message = {
|
|
event: 'setTitle',
|
|
context: context,
|
|
payload: {
|
|
title: title,
|
|
target: 0
|
|
}
|
|
};
|
|
websocket.send(JSON.stringify(message));
|
|
}
|
|
|
|
// 사용
|
|
setButtonTitle(context, '마커\nON');
|
|
```
|
|
|
|
#### `setImage`
|
|
액션의 이미지를 변경합니다.
|
|
|
|
**지원 형식:**
|
|
- Base64 인코딩 이미지
|
|
- SVG 형식
|
|
|
|
**파라미터:**
|
|
- `context`: 컨텍스트 ID
|
|
- `payload.image`: Base64 또는 SVG 문자열
|
|
- `payload.target`: 대상
|
|
- `payload.state`: 상태
|
|
|
|
**예시:**
|
|
```javascript
|
|
function setButtonImage(context, imageBase64) {
|
|
const message = {
|
|
event: 'setImage',
|
|
context: context,
|
|
payload: {
|
|
image: imageBase64,
|
|
target: 0
|
|
}
|
|
};
|
|
websocket.send(JSON.stringify(message));
|
|
}
|
|
```
|
|
|
|
#### `showAlert`
|
|
액션에 임시 경고 아이콘을 표시합니다.
|
|
|
|
**구조:**
|
|
```json
|
|
{
|
|
"event": "showAlert",
|
|
"context": "context_id"
|
|
}
|
|
```
|
|
|
|
#### `showOk`
|
|
액션에 임시 체크마크 확인 아이콘을 표시합니다.
|
|
|
|
**구조:**
|
|
```json
|
|
{
|
|
"event": "showOk",
|
|
"context": "context_id"
|
|
}
|
|
```
|
|
|
|
#### `setState`
|
|
다중 상태 액션의 상태를 변경합니다.
|
|
|
|
**파라미터:**
|
|
- `context`: 컨텍스트 ID
|
|
- `payload.state`: 상태 번호 (0, 1, 2, ...)
|
|
|
|
**예시:**
|
|
```javascript
|
|
function setButtonState(context, state) {
|
|
const message = {
|
|
event: 'setState',
|
|
context: context,
|
|
payload: {
|
|
state: state
|
|
}
|
|
};
|
|
websocket.send(JSON.stringify(message));
|
|
}
|
|
|
|
// 사용: ON(0) → OFF(1) 토글
|
|
const newState = currentState === 0 ? 1 : 0;
|
|
setButtonState(context, newState);
|
|
```
|
|
|
|
#### `sendToPropertyInspector`
|
|
플러그인에서 Property Inspector로 데이터를 전송합니다.
|
|
|
|
**예시 (Plugin → Property Inspector):**
|
|
```javascript
|
|
// Plugin
|
|
function sendToPI(context, action, payload) {
|
|
const message = {
|
|
event: 'sendToPropertyInspector',
|
|
action: action,
|
|
context: context,
|
|
payload: payload
|
|
};
|
|
websocket.send(JSON.stringify(message));
|
|
}
|
|
|
|
// Property Inspector
|
|
window.connectElgatoStreamDeckSocket = (inPort, inUUID, inEvent, inInfo) => {
|
|
websocket.onmessage = (evt) => {
|
|
const jsonObj = JSON.parse(evt.data);
|
|
if (jsonObj.event === 'sendToPropertyInspector') {
|
|
console.log('Received from plugin:', jsonObj.payload);
|
|
}
|
|
};
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 메시지 구조
|
|
|
|
### 표준 메시지 형식
|
|
|
|
모든 WebSocket 메시지는 다음 표준 JSON 구조를 따릅니다:
|
|
|
|
```json
|
|
{
|
|
"event": "이벤트_이름",
|
|
"action": "com.developer.plugin.action_id",
|
|
"context": "unique_context_id",
|
|
"device": "device_id",
|
|
"payload": {
|
|
// 이벤트별 데이터
|
|
}
|
|
}
|
|
```
|
|
|
|
### 필드 설명
|
|
|
|
- **event**: 이벤트 타입 (필수)
|
|
- **action**: 액션 UUID (일부 이벤트에서 선택)
|
|
- **context**: 고유 컨텍스트 식별자 (필수)
|
|
- **device**: 장치 ID (장치 관련 이벤트)
|
|
- **payload**: 이벤트별 데이터 객체
|
|
|
|
---
|
|
|
|
## 버전 요구사항
|
|
|
|
### StreamDock 소프트웨어 버전
|
|
|
|
#### Windows
|
|
- **최소 버전**: `3.10.188.226` 이상
|
|
- **Node.js 내장**: 버전 20.8.1
|
|
- **manifest.json 설정**:
|
|
```json
|
|
{
|
|
"Nodejs": {
|
|
"Version": "20"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### macOS
|
|
- **최소 버전**: `3.10.189.0313` (Node.js 내장 예정)
|
|
- 현재는 외부 Node.js 설치 필요
|
|
|
|
### SDK 버전 호환성
|
|
|
|
| SDK | 최소 SW 버전 | Node.js 버전 | 권장 사용 |
|
|
|-----|-------------|-------------|----------|
|
|
| SDNodeJsSDKV2 | 3.10.188.226 | 20.8.1 | ✅ 권장 |
|
|
| SDNodeJsSDK | 모든 버전 | 외부 설치 | ⚠️ Legacy |
|
|
| SDJavaScriptSDK | 모든 버전 | N/A | ✅ 간단한 플러그인 |
|
|
| SDVueSDK | 3.10.188.226 | 20.8.1 | ✅ 복잡한 UI |
|
|
| StreamDockCPPSDK | 모든 버전 | N/A | ✅ 고성능 |
|
|
| SDPythonSDK | 모든 버전 | N/A | ✅ Python 개발자 |
|
|
|
|
---
|
|
|
|
## 참고 자료
|
|
|
|
### 공식 문서
|
|
- **메인**: https://sdk.key123.vip/
|
|
- **Getting Started**: https://sdk.key123.vip/en/guide/overview.html
|
|
- **수신 이벤트**: https://sdk.key123.vip/en/guide/events-received.html
|
|
- **전송 이벤트**: https://sdk.key123.vip/en/guide/events-sent.html
|
|
- **Manifest 문서**: https://sdk.key123.vip/en/guide/manifest.html
|
|
|
|
### GitHub
|
|
- **SDK 리포지토리**: https://github.com/MiraboxSpace/StreamDock-Plugin-SDK
|
|
- **플러그인 예제**: https://github.com/MiraboxSpace/StreamDock-Plugins
|
|
|
|
### 커뮤니티
|
|
- **Email**: service@key123.vip
|
|
- **Discord**: 공식 Discord 서버
|
|
- **플랫폼**: https://space.key123.vip/ (플러그인 마켓플레이스)
|
|
|
|
### 라이선스
|
|
- **MIT License** - 오픈소스
|
|
|
|
---
|
|
|
|
**문서 버전**: 1.0
|
|
**최종 업데이트**: 2025-01-27
|
|
**백업 목적**: AI 환각 방지 및 오프라인 참조
|