Hawkbat / VTubeStudioJS

An implementation of the VTube Studio API for Node and browser JS
MIT License
45 stars 2 forks source link

VTubeStudioJS

npm GitHub

An implementation of the WebSocket-based VTube Studio API for Node and browser JavaScript.

See the generated API documentation for type definitions and comments for the various public classes and interfaces, or use an editor with TypeScript support to automatically get types and comments when inspecting objects and fields.

See the official VTube Studio API documentation for more details on the semantic meaning of individual fields and the behaviors of the various API calls.

Install

# NPM
npm i vtubestudio
# Yarn
yarn add vtubestudio

This package has no runtime dependencies. To avoid node_modules bloat, use npm install --only=prod or NODE_ENV=production to skip unnecessary dev dependencies.

Usage

Basic Usage

This library is platform-agnostic, allowing it to be used for both Node projects and for browsers via tools like webpack. Use it by importing and instantiating an ApiClient:

// ES Modules (NodeJS or browser with bundler)
import { ApiClient } from "vtubestudio";

// CommonJS/Require (NodeJS)
const vts = require("vtubestudio");
const ApiClient = vts.ApiClient;

// ES Modules (CDN)
import { ApiClient } from "https://unpkg.com/vtubestudio/lib/esm/vtubestudio.min.js?module";

// Global Variable (CDN)
// <script src="https://unpkg.com/vtubestudio/lib/iife/vtubestudio.min.js">
const ApiClient = VTubeStudio.ApiClient;

function setAuthToken(authenticationToken) {
  // store the authentication token somewhere
}

function getAuthToken() {
  // retrieve the stored authentication token
}

const options = {
  authTokenGetter: getAuthToken,
  authTokenSetter: setAuthToken,
  pluginName: "Your Plugin Name",
  pluginDeveloper: "Your User Name",

  // Optionally set the URL or port to connect to VTube Studio at; defaults are as below:

  //port: 8001,
  //url: "ws://localhost:${port}",
};

const apiClient = new ApiClient(options);

See IApiClientOptions in the docs for all available options.

The ApiClient automatically handles connecting to the VTube Studio API, including seamlessly reconnecting broken WebSocket connections, and authenticating your plugin with VTube Studio. To enable these features, you must provide the authTokenGetter and authTokenSetter functions above, to persist the authentication token generated by VTube Studio.

Barebones implementations, for reference:

// Browser

function setAuthToken(authenticationToken) {
  localStorage.setItem("VTS_AUTH_TOKEN", authenticationToken);
}

function getAuthToken() {
  return localStorage.getItem("VTS_AUTH_TOKEN");
}

// NodeJS

function setAuthToken(authenticationToken) {
  fs.writeFileSync("./auth-token.txt", authenticationToken, {
    encoding: "utf-8",
  });
}

function getAuthToken() {
  return fs.readFileSync("./auth-token.txt", "utf-8");
}

Avoiding Timeout Errors

API calls may throw an error if the API client is not prepared to send requests; it may not be able to connect to VTube Studio, it may be waiting for the user to accept the authentication prompt, or the Plugin API might be disabled in VTube Studio settings. For best results, wrap every call in a try-catch, and use the following patterns where applicable:

// Run commands when the client connects:
apiClient.on("connect", async () => {
  // The API client just finished authenticating with VTube Studio, or reconnecting if it lost connection.

  // Run any commands you need to persist across reconnections here, such as event subscriptions:
  apiClient.events.modelLoaded.subscribe((data) => {
    // ...
  });
  // ...
});

// Check if the client is connected before running commands:
if (apiClient.isConnected) {
  const response = await apiClient.currentModel();
  // ...
}

Making API Calls

The API client exposes the request/response message pairs provided by the VTube Studio API as single asynchronous functions. You can use them like so:

// async/await
async function loadModel() {
  try {
    const response = await apiClient.modelLoad({ modelID: "YourNewModelID" });
    console.log("Successfully loaded model:", response.modelID);
  } catch (e) {
    console.error("Failed to load model:", e.errorID, e.message);
  }
}
// Promise callbacks
apiClient
  .modelLoad({ modelID: "YourNewModelID" })
  .then((response) => {
    console.log("Successfully loaded model:", response.modelID);
  })
  .catch((e) => {
    console.error("Failed to load model:", e.errorID, e.message);
  });

See ApiClient in the docs for all available calls, and review the official VTube Studio API documentation for more details on what calls are available and what each field means. In general, you pass an object representing the data property of the request to the library method, and get an object back representing the data property of the response. If the request data object is empty, the request method instead takes no parameters, and if the response data object is empty, the request method returns Promise<void>.

Subscribing to API Events

To subscribe to VTube Studio events, such as the ModelLoadedEvent, call the functions in apiClient.events, like this:

apiClient.events.modelLoaded.subscribe((data) => {
  // this callback will fire every time a model is loaded or unloaded in VTube Studio
  console.log("Model loaded:" + data.modelName);
});

Setting API Call Options

An additional options object may be passed as the second parameter to control the execution of the API call. For example, to change the default timeout to 1 minute:

const stats = await apiClient.statistics(undefined, { timeout: 1 * 60 * 1000 });

WebSockets

The API client expects a WebSocket implementation to be available. In the browser, the native WebSocket will be used automatically. In NodeJS or other contexts, you must provide an external implementation, such as the one from the ws package. In this case, you can explicitly provide a webSocketFactory function, like so:

const WebSocket = require("ws");

const options = {
  // ...
  webSocketFactory: (url) => new WebSocket(url),
};

const apiClient = new ApiClient(options);

Examples

Examples are included in the repository's examples folder for a React app (created with create-react-app) and a plain NodeJS app.

Breaking Changes in 3.2.0

Version v3.2.0 contains a minor breaking change from previous versions:

Breaking Changes in 3.x.x

Version v3.x.x contains several breaking changes from previous versions:

Breaking Changes in 2.x.x

Version v2.x.x contains several breaking changes from previous versions:

Contributing

Pull requests welcome! Please adhere to the linting and default formatting provided by the TypeScript compiler, and ensure that your code successfully compiles before opening your PR.

After cloning the repository or fetching the latest code:

npm install

Before opening a pull request, ensure your code compiles:

npm run build

License

MIT © 2022 Joshua Thome