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 withwyzie_) 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
BrowserWindowwith a partitioned session to capture Wyzie API keys from navigation events, storing them via ElectronsafeStorageinsrc/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-subtitleshandler insrc/ipc/subtitles.jsaggregates 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →