Skip to content

An implementation of the VTube Studio API for Node and browser JS

License

Notifications You must be signed in to change notification settings

Hawkbat/VTubeStudioJS

Repository files navigation

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:

  • The ApiClient no longer attempts to import the ws package, due to complications with bundling for browser environments when the ws package is present in the package hierarchy. In NodeJS and other server environments, you must explicitly pass a WebSocket constructor via the webSocketFactory option. See the example above for reference.

Breaking Changes in 3.x.x

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

  • The constructor for ApiClient was changed to take an options object, instead of the factory methods or a taking a message bus.
  • The message bus abstractions (WebSocketBus and related functions) have been removed.
  • The ApiClient automatically handles instantiating WebSockets, connecting to VTube Studio, and making authentication calls. These no longer need to be performed manually by plugin code.
  • The object-oriented wrapper classes (Plugin, CurrentModel, Expression, etc.) have been fully removed. The automatic authentication flow provided by Plugin has been rolled into ApiClient.
  • Error codes related to message bus failures (MessageBusError, and MessageBusClosed) have been removed, as WebSocket errors are now handled silently by ApiClient.
  • The typings provided for API call methods that take no data payload now explicitly call for undefined as the data object instead of void, which would accept anything.

Breaking Changes in 2.x.x

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

  • The constructor for ApiClient was marked as private, and replaced with two factory methods: ApiClient.fromWebSocket(ws) and ApiClient.fromMessageBus(bus), to avoid unnecessarily exposing the end user to the library's message bus abstractions.
    • It is recommended that normal users should pass the websocket object to ApiClient.fromWebSocket(ws) directly instead of creating a WebSocketBus and passing that to new ApiClient(bus) or ApiClient.fromMessageBus(bus).
  • The object-oriented wrapper classes (Plugin, CurrentModel, Expression, etc.) have been deprecated, and will be removed in the next major version. The stateful nature of the wrappers implied that they were somehow synchronized with VTube Studio, but this was not the case. This became even more obvious with the introduction of highly dynamic concepts like Live2D items and expressions.
    • Users should switch to making calls to the API directly using the ApiClient class instead of the methods on Plugin. This unfortunately also includes handling the authentication workflow yourself for now. A basic example is provided above.
    • A future release may extend the ApiClient class to provide the automatic authentication handling that the Plugin class provided.
  • The MockApiServer class has been removed (as well as the related class EchoBus). This class was not intended to be used in production code and was a poor substitute for testing against VTube Studio itself.
  • Expression parameters were changed to use the new shape { name: string, value: number } instead of the deprecated { id: string, target: number }.
  • The ErrorCode enum values InternalClientError, MessageBusError, and MessageBusClosed were changed to -100, -101, and -102 to avoid conflicting with -1 (used in the VTube Studio API to indicate the absence of an error).
    • Code using the enum itself should continue to work as expected, but any code directly checking for the error code numbers will need to be manually fixed.

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

About

An implementation of the VTube Studio API for Node and browser JS

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published