No history yet

IINA Plugin Architecture

IINA's Two-Layer Heart

To understand IINA's plugins, you first have to understand IINA itself. The app isn't built from scratch; it's a sophisticated macOS interface layered on top of a powerful, open-source media engine called mpv . Think of it like a high-performance car. IINA is the sleek, user-friendly dashboard, steering wheel, and comfortable seats—all written in Swift for a native macOS experience. Under the hood, mpv is the raw engine, a command-line powerhouse written in C that handles the complex work of decoding and rendering video and audio.

This dual-layer architecture is key. IINA provides the polish and platform integration, while mpv delivers the playback muscle. Plugins, therefore, don't just interact with IINA's Swift code. They need a way to reach down and control the underlying mpv engine, which is where things get interesting.

Anatomy of a Plugin

An IINA plugin, which has the .iinaplugin extension, isn't a single file. It's a special kind of folder called a bundle. On macOS, bundles are directories that the system treats as a single package. If you right-click a .iinaplugin file and choose "Show Package Contents," you'll see its internal file structure.

Inside, you'll find two essential components: a manifest file and a script.

  • Info.json: This is the plugin's metadata file. It tells IINA everything it needs to know to load and identify your plugin.
  • scripts/main.js: This is the JavaScript file containing the plugin's core logic.

The Info.json manifest is a simple JSON file that acts as the plugin's identity card. It defines crucial properties that IINA reads when it first discovers the plugin.

{
  "name": "My Awesome Plugin",
  "identifier": "com.yourname.myplugin",
  "version": "1.0.0",
  "author": "Your Name",
  "description": "A brief description of what this plugin does.",
  "main": "scripts/main.js"
}

The identifier must be a unique string, typically in reverse domain name notation, to prevent conflicts with other plugins. The main key is particularly important; it points IINA to the entry point of your plugin's code, which in this case is the main.js file inside the scripts folder.

Bridging Two Worlds

So, how does IINA's Swift-based application run your JavaScript code and let it talk to the C-based mpv core? It uses an internal JavaScript engine. When IINA loads your plugin, it creates a sandboxed environment using a framework like JavaScriptCore and executes the main.js file specified in your manifest.

This JavaScript environment doesn't have direct access to mpv. Instead, IINA provides a specialized Plugin API. This API acts as the crucial bridge. Your JavaScript code calls functions on IINA's API, and IINA translates those calls into commands that the mpv engine understands. It also listens for events from mpv—like a file being loaded or playback pausing—and forwards them to your JavaScript code.

The plugin's lifecycle is managed entirely by IINA. When the app starts, it scans for plugins and loads their manifests. It initializes each plugin's script environment. When the user interacts with IINA or when playback events occur, IINA's bridge ensures the correct data flows between the Swift UI, your JavaScript logic, and the mpv engine. When IINA quits, it cleanly terminates the JavaScript environment, ending the plugin's lifecycle.

This architecture allows for safe and powerful extensions. Your plugin code is isolated from IINA's core logic, but the API bridge gives it deep control over the playback experience.