Streambert Download Manager: Handling M3U8 Streams with External Tools
Streambert captures HLS playlist URLs through a custom Electron session, then delegates the actual downloading to an external binary (vid-dl-cli-only) that fetches fragments, merges them with ffmpeg, and reports progress back via IPC.
Streambert is an Electron-based streaming application that automates the detection and downloading of HLS (HTTP Live Streaming) content. The Streambert download manager leverages a sophisticated IPC architecture to bridge the renderer process UI with a standalone command-line downloader, enabling robust handling of .m3u8 playlists and external tool integration.
Intercepting M3U8 URLs in the Player Session
When a video page loads, Streambert creates a custom playerSession with specific webRequest filters to intercept media resources before they reach the player.
In src/index.js, the main process registers onBeforeRequest listeners for both blocked hosts and media URLs:
const MEDIA_URLS = [
"*://*/*.m3u8*",
"*://*/*.m3u8",
"*://*/*.vtt*",
"*://*/*.vtt",
];
playerSession.webRequest.onBeforeRequest(
{ urls: [...BLOCKED_HOSTS, ...MEDIA_URLS] },
(details, callback) => {
const { url } = details;
if (url.includes('.m3u8')) {
mw.webContents.send('m3u8-found', url);
} else if (url.includes('.vtt')) {
const { extractSubtitleLang } = require('./src/ipc/subtitles');
mw.webContents.send('subtitle-found', {
url,
lang: extractSubtitleLang(url),
});
}
callback({});
},
);
.m3u8detection: Triggers them3u8-foundevent sent to the renderer, enabling the download UI..vttdetection: Extracts language tags viaextractSubtitleLang()fromsrc/utils/subtitles.jsand emitssubtitle-found.
This interception happens transparently to the user, allowing the app to present download options immediately when HLS content is detected.
Validating the External Downloader Binary
Before executing any download, Streambert verifies that the user has configured a valid external tool. The check-downloader IPC handler in src/ipc/downloads.js scans the specified directory for an executable binary:
ipcMain.handle('check-downloader', (_, folderPath) => {
if (!folderPath) return { exists: false, reason: 'no_folder' };
const binary = entries.find(e => {
if (e === '_internal' || e.startsWith('.')) return false;
const stat = fs.statSync(path.join(folderPath, e));
return process.platform === 'win32' ? e.endsWith('.exe')
: !!(stat.mode & 0o111);
});
if (!binary) return { exists: false, reason: 'no_executable' };
return { exists: true, binaryPath: path.join(folderPath, binary) };
});
The validation checks for platform-appropriate executables (.exe on Windows, files with execute permissions on Unix systems) and excludes internal folders like _internal.
Spawning Downloads via IPC
The run-download handler orchestrates the actual download by spawning the external vid-dl-cli-only binary as a child process. Located in src/ipc/downloads.js, this handler manages process state, logging, and progress tracking:
ipcMain.handle('run-download', (_, {
binaryPath,
m3u8Url,
name,
downloadPath,
}) => {
const id = crypto.randomUUID();
const logPath = path.join(os.tmpdir(), `streambert_dl_${id}.log`);
const entry = { id, name, m3u8Url, downloadPath };
downloads.push(entry);
const args = [
'--cli',
m3u8Url,
'-f', 'mp4 (with Audio)',
'-r', 'best',
'-b', '320',
'-n', name,
'-d', downloadPath,
];
const proc = spawn(binaryPath, args, {
stdio: ['ignore','pipe','pipe']
});
activeProcs.set(id, proc);
// ... progress handling
});
Each download receives a unique UUID, creates a temporary log file in os.tmpdir(), and stores process references in activeProcs for lifecycle management.
Real-Time Progress Parsing
The download manager parses stdout from the external tool to provide granular progress updates. The parser in src/ipc/downloads.js handles multiple output formats:
Fragment-based HLS progress:
const fragMatch = trimmed.match(/\(frag\s+(\d+)\/(\d+)\)/);
if (fragMatch) {
const current = parseInt(fragMatch[1]);
const total = parseInt(fragMatch[2]);
update.progress = Math.min(99,
Math.round((current / total) * 100));
update.lastMessage = `Fragment ${current} / ${total}`;
}
Direct download statistics:
When encountering [download] prefixed lines, the parser extracts percentage, total size, and download speed using regex patterns.
FFmpeg integration:
The parser also monitors for Duration: and size= lines during the final muxing phase to accurately track the merge progress.
All updates are emitted to the renderer via download-progress events containing the download ID, percentage, speed, and status messages.
Post-Processing and Cleanup
Upon successful completion (exit code 0), the download manager performs several cleanup operations:
- Log removal: Deletes the temporary log file created in
os.tmpdir(). - File discovery: If the binary doesn't explicitly report the output path, Streambert scans the download directory for the most recently modified video file matching known extensions.
- Safe renaming: Sanitizes the media title and renames the file appropriately.
- Subtitle fetching: Processes any subtitle URLs passed from the UI via
downloadSubtitleFile, supporting HTTP(S) and localfile:schemes, saving.srt,.vtt, or.assfiles alongside the video.
If the external tool fails, the manager captures the last meaningful line from stderr and surfaces it to the user through the UI.
Session Persistence and Safety
Streambert maintains download state across sessions through downloads.json, which persists completed entries while filtering out active or error states. The killAllDownloads() function ensures graceful shutdown by terminating child processes and removing temporary artifacts (.part, .tmp files) when the application exits or when the user triggers a reset-app command.
Summary
- Streambert download manager uses a custom Electron
playerSessionto intercept.m3u8and.vttURLs before they reach the video player. - The
check-downloaderIPC handler validates the presence of the vid-dl-cli-only binary before allowing downloads to start. - Downloads run in isolated child processes spawned via
spawn()insrc/ipc/downloads.js, with real-time stdout parsing for fragment-level progress updates. - Post-processing includes automatic file discovery, safe renaming, subtitle downloads, and cleanup of temporary log files.
- State persistence and emergency cleanup ensure system hygiene even during unexpected shutdowns.
Frequently Asked Questions
How does Streambert detect M3U8 streams without browser extensions?
Streambert creates a custom Electron session (playerSession) with webRequest.onBeforeRequest filters targeting *.m3u8* and *.vtt* patterns. When the player loads a page, this API intercepts network requests before they complete, allowing the main process to emit m3u8-found events to the renderer without requiring external browser extensions or user scripts.
What external tool does Streambert require for downloading?
Streambert requires the vid-dl-cli-only binary, a standalone command-line downloader capable of processing HLS playlists, downloading fragments, and merging them with ffmpeg. The application validates this binary through the check-downloader IPC handler, which checks for executable permissions (or .exe extension on Windows) in the user-specified directory.
Can Streambert download subtitles separately from video?
Yes. When the player session detects .vtt URLs, it extracts language codes using extractSubtitleLang() from src/utils/subtitles.js and emits subtitle-found events. During download initiation, users can pass subtitle arrays to the run-download handler, which fetches them via downloadSubtitleFile and saves them with appropriate extensions alongside the video file.
How does the download manager handle partial failures or cancellations?
The download manager tracks active processes in an activeProcs Map using UUIDs. When a user cancels a download or the app shuts down, killAllDownloads() terminates the associated child process and removes temporary files including log files and partial downloads (.part files). Failed downloads capture the last stderr line for error reporting while preserving the attempt in the downloads store until explicitly cleared.
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 →