Streambert Wyzie API Key Redemption and Subtitle Downloading: Complete Technical Guide

Streambert integrates a built-in Wyzie API key redemption flow that captures free subtitle-service credentials via a secure BrowserWindow, encrypts them using Electron's safeStorage, and aggregates search results from both Wyzie and SubDL endpoints.

The Streambert media player (truelockmc/streambert) streamlines subtitle acquisition by automating Wyzie API key redemption directly within the Electron application. This guide examines how the application securely stores these credentials in encrypted form and implements a dual-provider search system that automatically falls back to a public Wyzie endpoint when no key is present. Developers can use these patterns to implement similar OAuth-like redemption flows or troubleshoot subtitle-fetching issues in their own Electron apps.

How Streambert Handles Wyzie API Key Redemption

The redemption process creates an isolated, non-persistent browser session to prevent credential leakage while monitoring navigation to capture the API key from URL parameters.

Opening the Secure Redemption Window

The renderer initiates the flow by calling window.electron.wyzieOpenRedeem(), exposed in preload.js (lines 121–124). This triggers the main process handler registered in index.js:

// index.js – Wyzie redemption window creation
ipcMain.handle("wyzie-open-redeem", async () => {
  return new Promise((resolve) => {
    const { BrowserWindow: BW, session: electronSession } = require("electron");
    const redeemSession = electronSession.fromPartition("partition:wyzie-redeem");
    // ...
    const win = new BW({ 
      // non-persistent session ensures no cached credentials leak
      webPreferences:{ session: redeemSession } 
    });
    // ...
  });
});

The implementation creates a dedicated BrowserWindow using a partitioned session (partition:wyzie-redeem) to ensure complete isolation from the main application context.

Extracting the Key from Navigation Events

Once the window loads https://sub.wyzie.io/redeem, the main process strips CSP headers to ensure proper rendering and monitors navigation via will-navigate, did-navigate, and did-navigate-in-page events. When the URL matches the pattern https://sub.wyzie.io/notice?key=wyzie-…, the handler extracts the key parameter:

// index.js – URL monitoring logic (lines 20-30)
const checkUrl = (url) => {
  const u = new URL(url);
  if (u.hostname==="sub.wyzie.io" && u.pathname==="/notice") {
     const key = u.searchParams.get("key");
     if (key && key.startsWith("wyzie-")) finish({ok:true, key});
     return true;
  }
  return false;
};

The promise resolves with { ok:true, key }, closing the redemption window and returning control to the renderer.

Encrypting and Storing the Credential

The renderer receives the key and immediately stores it via window.electron.secureSet("wyzieApiKey", res.key), referencing the STORAGE_KEYS.WYZIE_API_KEY constant defined in src/utils/storage.js. This leverages Electron’s safeStorage module to encrypt the credential at rest, ensuring the key never touches unencrypted localStorage or plain text files.

Validating Wyzie API Keys Before Use

Before executing subtitle queries, Streambert can verify key validity through a dedicated IPC handler in index.js (lines 44–56):

ipcMain.handle("wyzie-validate-key", async (_, key) => {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 10_000);
  const res = await fetch(
    `https://sub.wyzie.io/search?id=550&format=srt&key=${encodeURIComponent(key)}`,
    { signal: controller.signal }
  ).finally(() => clearTimeout(timer));
  if (res.status===401 || res.status===403) return {ok:false, error:"Invalid or expired key"};
  return {ok:true};
});

The validation performs a lightweight search against a static TMDB ID (550) with a 10-second timeout. HTTP status codes 401 or 403 trigger an "Invalid or expired key" response, prompting the UI to request redemption.

Aggregating Subtitles from Wyzie and SubDL

The IPC subtitle module (src/ipc/subtitles.js) implements a unified search-subtitles handler that aggregates results from both providers into a normalized format.

The IPC Search Handler Architecture

The handler accepts TMDB identifiers, language preferences, and both API keys as parameters (lines 94–112):

ipcMain.handle(
  "search-subtitles",
  async (_, { tmdbId, mediaType, season, episode, languages, subdlApiKey, wyzieApiKey }) => {
    // SubDL helper logic omitted for brevity
    
    async function searchWyzie() {
      const params = new URLSearchParams({ id: String(tmdbId), format: "srt" });
      if (languages) params.set("language", languages);
      if (wyzieApiKey) params.set("key", wyzieApiKey);
      
      const baseUrl = wyzieApiKey 
        ? "https://sub.wyzie.io/search" 
        : "https://subs.wyzie.ru/search";
        
      const res = await fetchWithTimeout(`${baseUrl}?${params}`, {}, 12_000);
      // ...
    }
    // ...
  }
);

Endpoint Selection Logic

When a Wyzie API key is present, requests route to the authenticated endpoint https://sub.wyzie.io/search. Without a key, the system falls back to the public endpoint https://subs.wyzie.ru/search. SubDL queries execute only when a subdlApiKey is supplied, creating a flexible three-tier fallthrough: authenticated Wyzie → public Wyzie → SubDL.

Normalizing Results for the UI

Each subtitle entry normalizes to a common shape containing file_id, file_name, language, and source flags (via_wyzie or via_subdl). The UI renders source badges using color logic defined in src/utils/subtitles.js (sourceBadgeStyle), allowing users to distinguish between providers visually.

Fetching and Saving Subtitle Files

Once the user selects a subtitle, Streambert handles provider-specific retrieval methods before saving files adjacent to the video.

Direct URLs vs ZIP Extraction

The get-subtitle-url handler (lines 72–80) differentiates between providers:

  • Wyzie entries: Already contain a direct download URL. The handler decodes the file_id (prefixed with wyzie_) and returns the URL immediately.
  • SubDL entries: Require downloading a ZIP archive, extracting the first subtitle file, and exposing it via a temporary file:// URI.
// src/ipc/subtitles.js – get-url handler excerpt
if (String(fileId).startsWith("wyzie_")) {
  const url = decodeURIComponent(String(fileId).split("_").slice(2).join("_"));
  return { ok:true, url, file_name:"", via_wyzie:true };
}

The Download Workflow

The downloadSubtitlesForFile function (lines 33–80) iterates over selected subtitle objects, fetches each file (handling ZIP extraction for SubDL), deduplicates language files to prevent overwriting, and writes them using the naming pattern <basename>.<lang>.srt:

// src/ipc/subtitles.js – download-for-file excerpt
for (const sub of selectedSubs) {
  // fetch logic varies by provider
  const destPath = path.join(dir, `${baseName}.${langCode}${suffix}.${ext}`);
  fs.writeFileSync(destPath, fileData);
  results.push({ 
    lang: langCode, 
    path: destPath, 
    source: sub.via_subdl ? "subdl" : "wyzie" 
  });
}

The function updates the download registry with absolute paths, allowing the renderer to display download status and open containing folders.

Summary

  • Secure Redemption: Streambert uses an isolated BrowserWindow with a partitioned session to capture Wyzie API keys from navigation events, storing them via Electron safeStorage in src/utils/storage.js.
  • Validation Layer: The main process validates keys against the Wyzie search endpoint with a 10-second timeout before allowing queries.
  • Dual-Provider Search: The search-subtitles handler in src/ipc/subtitles.js aggregates results from authenticated Wyzie, public Wyzie, and SubDL endpoints based on available credentials.
  • Provider Abstraction: Wyzie provides direct URLs while SubDL requires ZIP extraction, both normalized to a common interface for the UI.
  • Local File Integration: Selected subtitles download directly to the video file’s directory with standardized naming conventions.

Frequently Asked Questions

How is the Wyzie API key stored securely?

Streambert encrypts the key using Electron’s safeStorage module via the secureSet IPC method exposed in preload.js. The credential is stored under the wyzieApiKey registry entry defined in src/utils/storage.js, ensuring it never persists in plain text or browser localStorage.

What happens if my Wyzie API key expires?

When executing searches, the validation handler in index.js detects HTTP 401 or 403 responses from sub.wyzie.io/search and returns {ok:false, error:"Invalid or expired key"}. The UI can then prompt the user to re-run the redemption flow via window.electron.wyzieOpenRedeem() to obtain a fresh key.

Can I use Streambert subtitles without a Wyzie key?

Yes. The subtitle search logic in src/ipc/subtitles.js automatically falls back to the public endpoint https://subs.wyzie.ru/search when wyzieApiKey is null or undefined. Additionally, if you provide a SubDL API key, the application will search that provider regardless of Wyzie authentication status.

How does Streambert prioritize between SubDL and Wyzie results?

The search-subtitles handler executes both searches concurrently (when keys are available) and aggregates results into a single normalized array. The UI displays badges indicating the source (via_wyzie or via_subdl) via src/utils/subtitles.js, but does not inherently prioritize one over the other; the user selects from the combined list based on language and filename preferences.

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 →