Streambert Download Progress Tracking and Completion Notification

Streambert tracks video downloads by spawning the vid-dl-cli-only binary in the main process, parsing its real-time stdout/stderr output into structured progress objects, and broadcasting completion status to the renderer via Electron's download-progress IPC channel.

Streambert is an Electron-based streaming application that manages video downloads through external CLI tools. Understanding how Streambert implements download progress tracking and completion notification requires examining the IPC architecture that bridges binary execution in the main process with the React-based user interface.

Architecture Overview

The download tracking system spans three architectural layers. The main process handles binary execution and output parsing in [src/ipc/downloads.js](https://github.com/truelockmc/streambert/blob/main/src/ipc/downloads.js). The preload script ([preload.js](https://github.com/truelockmc/streambert/blob/main/preload.js)) creates a secure bridge between main and renderer processes. Finally, React components like [DownloadModal.jsx](https://github.com/truelockmc/streambert/blob/main/src/components/DownloadModal.jsx) consume these events to update the user interface.

Initiating Downloads

The run-download IPC Handler

When a user starts a download, the renderer invokes the run-download channel through the exposed window.ipc API. The handler in src/ipc/downloads.js generates a unique UUID for the download entry, creates a temporary log file, and spawns the vid-dl-cli-only binary using child_process.spawn.

// Renderer process invocation
const result = await window.ipc.invoke('run-download', {
  binaryPath: '/usr/local/bin/vid-dl-cli-only',
  m3u8Url: playlistUrl,
  name: 'Movie Title',
  downloadPath: '/home/user/Videos',
  mediaId: '12345',
  mediaType: 'movie',
  season: null,
  episode: null,
  tmdbId: '67890',
  subtitles: [{ url: subtitleUrl, lang: 'en', name: 'English' }]
});

The IPC handler tracks the active process in an internal array, associates it with the generated UUID, and begins monitoring the streams.

Real-Time Progress Parsing

Binary Output Parsing

The vid-dl-cli-only binary writes status information to stdout and stderr in human-readable formats such as (frag 12/200), [download] 45% of 1.2 GiB at 3.5MiB/s, and size= 35.4MiB time=00:02:30.12. The downloads.js handler buffers this output, splits it into lines, and applies regular expressions to extract quantitative metrics.

Each parsed line updates a partial progress object containing:

  • progress: Integer percentage (0-99) calculated from fragment counts or download percentage
  • totalFragments and completedFragments: HLS segment counters
  • size: Human-readable file size (e.g., "1.2 GiB")
  • speed: Transfer rate (e.g., "3.5 MiB/s")
  • lastMessage: Human-readable status line for UI display
  • status: String value "downloading" while active

Emitting Progress Updates

After processing each line, the handler calls sendProgress(), which broadcasts the update to all renderer processes via webContents.send():

function sendProgress(update) {
  const mw = _getMainWindow();
  if (mw && !mw.isDestroyed()) {
    mw.webContents.send('download-progress', update);
  }
}

This function ensures the main window receives the payload even if multiple downloads run concurrently.

Completion Notification Flow

Process Exit Handling

When the binary process closes, the exit code determines the final status. In the process close handler within src/ipc/downloads.js, the code evaluates the exit status:

const status = code === 0 ? 'completed' : 'error';
downloads[idx].status = status;
downloads[idx].completedAt = Date.now();

if (code === 0) {
  downloads[idx].progress = 100;
  fs.unlinkSync(logPath); // Clean up temporary log
}

A zero exit code triggers the completion sequence, setting the progress to 100% and recording the completion timestamp. Non-zero codes mark the entry as an error state.

Finalization and Cleanup

For successful downloads, the completion handler performs several finalization steps:

  1. File Resolution: Locates the generated video file by scanning the destination directory if the "Destination" line was missed in output
  2. Renaming: Sanitizes the filename to match the user-supplied title
  3. Size Calculation: Determines the final on-disk file size
  4. Subtitle Processing: Downloads requested subtitles via the downloadSubtitleFile helper function
  5. Store Update: Persists the completed entry to the downloads store

The final broadcast payload sent via sendProgress() includes:

{
  "id": "a1b2c3d4-...",
  "status": "completed",
  "progress": 100,
  "filePath": "/home/user/Videos/Movie Title.mp4",
  "size": "1.35 GiB",
  "lastMessage": "Completed"
}

UI Integration

Subscribing to Progress Events

The [preload.js](https://github.com/truelockmc/streambert/blob/main/preload.js) script exposes the ipcRenderer to the renderer process, forwarding the download-progress event through a safe API bridge. Components subscribe using window.ipc.on() and unsubscribe on unmount to prevent memory leaks.

// In DownloadModal.jsx or any component
useEffect(() => {
  const onProgress = (_, update) => {
    setDownload(prev => ({ ...prev, [update.id]: update }));
  };
  
  window.ipc.on('download-progress', onProgress);
  
  return () => {
    window.ipc.removeListener('download-progress', onProgress);
  };
}, []);

Displaying Status in Components

The DownloadModal component renders a progress bar using the progress field and conditionally displays completion UI when status === 'completed'. The DownloadsPage component maintains the list of all downloads by invoking window.ipc.invoke('get-downloads') on mount and merging real-time updates from the progress channel.

{Object.values(downloads).map(item => (
  <div key={item.id} className="download-item">
    <h4>{item.name}</h4>
    <ProgressBar value={item.progress} />
    <p>{item.lastMessage}</p>
    
    {item.status === 'completed' && (
      <button onClick={() => window.ipc.invoke('show-in-folder', item.filePath)}>
        Open Folder
      </button>
    )}
    
    {item.status === 'error' && (
      <span className="error-text">Error: {item.lastMessage}</span>
    )}
  </div>
))}

Persisting Download State

Download metadata persists to a JSON file (downloads.json) in the Electron user data directory. The loadDownloads() function initializes the store on application startup, deduplicating entries and sorting by recent activity. The saveDownloads() function writes only completed or errored entries to disk, ensuring interrupted downloads do not leave stale data.

The UI retrieves the current list via the get-downloads IPC channel, while ongoing downloads receive live updates through the download-progress broadcast system, maintaining synchronization between the persistent store and the active interface.

Summary

  • Streambert's download tracking relies entirely on the IPC layer in src/ipc/downloads.js to manage binary execution and output parsing.
  • Progress parsing uses regular expressions against the vid-dl-cli-only binary's stdout/stderr, converting text lines into structured data objects.
  • Completion detection occurs when the child process exits with code 0, triggering finalization steps including file renaming, subtitle downloads, and cleanup.
  • Renderer updates flow through a single unified event (download-progress) consumed by components like DownloadModal.jsx and DownloadsPage.jsx.
  • State persistence uses a local JSON store updated only when downloads reach terminal states (completed or error).

Frequently Asked Questions

How does Streambert parse download progress from the binary output?

Streambert parses progress by buffering the vid-dl-cli-only binary's stdout and stderr streams in src/ipc/downloads.js. The handler splits incoming data by lines and matches them against regular expressions to extract fragment counts, percentages, file sizes, and transfer speeds. Each match updates a progress object that is immediately broadcast to the renderer via the download-progress IPC event.

What triggers the completion notification in Streambert?

Completion triggers when the spawned child process exits with code 0, detected in the process close handler within src/ipc/downloads.js. The handler sets the download status to "completed", updates the progress to 100%, records the completion timestamp, and performs finalization tasks such as file renaming and subtitle downloading before broadcasting the final state to the UI.

Where does Streambert store download progress data?

Streambert stores completed download metadata in a JSON file named downloads.json located in the Electron user data directory. The loadDownloads() and saveDownloads() functions in the IPC handler manage this persistence. Active downloads exist only in memory and the IPC broadcast channel until they reach a terminal state (completed or error).

Can I track download progress in a custom Streambert component?

Yes, any React component can track progress by subscribing to the download-progress event exposed through window.ipc in the preload script. Import useEffect from React, register a listener with window.ipc.on('download-progress', handler), and remove it in the cleanup function to avoid memory leaks. The handler receives the update payload containing progress percentage, status, and file metadata.

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 →