Streambert Single Instance Lock: Managing Multiple Windows in Electron

Streambert uses Electron's app.requestSingleInstanceLock() API in src/index.js to enforce a single-instance lock while simultaneously supporting multiple auxiliary windows including picture-in-picture and pop-out player windows.

Streambert is an Electron-based streaming application developed under the truelockmc/streambert repository that requires strict single-instance enforcement despite supporting multiple window types. Understanding how Streambert implements a single instance lock while managing multiple windows is essential for developers working with Electron's process model and shared resource management.

Implementing the Single-Instance Lock in src/index.js

The core implementation resides in the main entry point at src/index.js, where the application leverages Electron's native API to control instance spawning and prevent duplicate backend processes.

Acquiring the Lock on Startup

Immediately after defining IPC handlers, the code attempts to acquire the single-instance lock:

const gotTheLock = app.requestSingleInstanceLock();

This call returns a boolean indicating whether the current process successfully claimed the lock. According to the Streambert source code, this occurs at lines 82-84 in src/index.js.

Terminating Duplicate Instances

If another Streambert instance is already running, the lock acquisition fails and the duplicate process exits immediately to prevent database corruption:

if (!gotTheLock) {
  app.quit();
}

This logic at lines 85-86 ensures that only one process can access the SQLite storage files and download queues managed by src/ipc/downloads.js and src/ipc/storage.js.

Handling Second-Instance Events

When the lock succeeds, the primary instance registers a second-instance event listener. If a user attempts to launch Streambert again—perhaps by clicking the application icon—the existing window is restored rather than creating a new process:

app.on("second-instance", () => {
  if (mainWindow) {
    if (mainWindow.isMinimized()) mainWindow.restore();
    mainWindow.focus();
  }
});

This handler at lines 89-93 ensures that keyboard shortcuts, media controls, and the system tray icon always target the same window hierarchy.

Supporting Multiple Windows Within the Single Instance

Despite enforcing one process, Streambert creates several auxiliary windows that share the same backend state and Electron session objects.

Window Creation Workflow

The main window initializes through createWindow() after the Electron ready event fires. This function establishes the primary browser window, while additional windows for picture-in-picture (PIP) and pop-out players are created lazily via IPC handlers such as open-pip-window and open-popout-window defined later in the same file.

Shared Backend Resources

All windows access common resources through IPC modules. The single-instance lock prevents duplicate download queues and conflicting database connections to the SQLite file that stores user settings, subtitle caches, and persistent storage. Running two instances would create file-level race conditions in these shared modules.

Session and Memory Management

Electron's session objects for video playback—specifically persist:player and persist:trailer—are created once per process. The lock ensures these sessions aren't duplicated across instances, optimizing memory consumption and maintaining consistent media state across the main window, PIP views, and pop-out players.

Graceful Shutdown and Lock Release

When the user closes all windows, the window-all-closed listener triggers app.quit(), releasing the system lock and allowing future launches to acquire it:

app.on("window-all-closed", () => app.quit());

This implementation at lines 99-100 completes the application lifecycle, ensuring that the requestSingleInstanceLock() can succeed on the next application startup.

Summary

  • Single-instance enforcement occurs in src/index.js using app.requestSingleInstanceLock(), with duplicate processes immediately calling app.quit() at lines 85-86.
  • Window restoration happens via the second-instance event, which focuses the existing mainWindow using mainWindow.restore() and mainWindow.focus() rather than spawning new applications.
  • Multiple auxiliary windows (PIP, pop-out) operate within the single process, sharing SQLite databases via src/ipc/storage.js and IPC handlers without resource conflicts.
  • Graceful shutdown through window-all-closed at lines 99-100 ensures the lock releases properly when all windows close.

Frequently Asked Questions

How does Streambert prevent multiple instances from running?

Streambert calls app.requestSingleInstanceLock() in src/index.js immediately during startup. If the return value gotTheLock is false, the application executes app.quit() to terminate the duplicate process before any windows render, preventing conflicts in the SQLite database and download managers.

What happens when I try to open Streambert while it's already running?

The operating system routes the request to the existing instance, triggering the second-instance event. The handler checks if mainWindow exists, restores it if minimized using mainWindow.restore(), and brings it to focus with mainWindow.focus(), ensuring users always interact with the single running instance.

Can Streambert open multiple windows simultaneously?

Yes. While only one application instance runs, Streambert supports multiple window types including the main browser window, picture-in-picture views, and pop-out players. These share the same Electron process and backend resources through IPC handlers defined in src/index.js and preload scripts like src/preload.js and src/popout-preload.js.

Where is the single-instance logic located in the codebase?

The primary implementation resides in src/index.js at lines 82-93, containing the lock acquisition, duplicate instance termination, and second-instance event handling. Supporting IPC modules for shared resources are located in src/ipc/downloads.js and src/ipc/storage.js, while window titlebar UI components are defined in src/components/WindowTitlebar.jsx.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →