How Pyutube Merges Separate Video and Audio Streams: A Deep Dive into the Implementation

Pyutube merges separate video and audio streams by downloading each adaptive stream individually, then invoking FFmpeg through MoviePy's ffmpeg_merge_video_audio function inside the VideoService.merging method to combine them into a single MP4 file.

Pyutube is a Python-based CLI tool designed to download YouTube videos efficiently. Because YouTube serves high-quality video and audio as separate adaptive streams, Pyutube must download these components individually and combine them post-download. The merge process is orchestrated by DownloadService and executed by VideoService, utilizing FFmpeg via the MoviePy library to produce the final synchronized file.

Why Pyutube Downloads Streams Separately

YouTube's adaptive streaming technology splits video and audio into distinct files to optimize bandwidth and quality selection. Pyutube leverages this architecture by selecting the best available video stream and matching it with the appropriate audio stream via VideoService.get_selected_stream. This approach ensures users receive the highest possible quality for both components before the merge operation combines them into a single container.

The Merge Workflow Explained

The merging process follows a precise pipeline managed across multiple service classes. Here is the complete workflow from stream selection to final file cleanup.

Stream Selection and Preparation

The process begins in DownloadService.download_preparing, which initializes the video object and retrieves available streams. This method calls VideoService.get_selected_stream to identify the optimal video stream (video_file) and its corresponding audio companion (video_audio). These selections are then passed to DownloadService.download_video to begin the download phase.

File Persistence and Sanitization

Before merging can occur, both streams must be saved to disk with safe filenames. FileService.save_file writes the video and audio files to the target directory, sanitizing names using moviepy.video.io.ffmpeg_tools.safe_filename. The sanitized names, stored as video_safe_filename and audio_safe_filename, serve as prefixes for the actual saved files, which may contain additional suffixes to prevent conflicts.

The Merging Operation

Once both files are saved, DownloadService.download_video triggers the merge by calling self.video_service.merging(video_safe_filename, audio_safe_filename). The implementation in pyutube/services/VideoService.py (lines 57-84) executes the following steps:

  1. Prepare output directory – Creates a temporary <download_path>/output folder to house the merged file during processing.
  2. Resolve full paths – Scans the download directory to locate the actual video and audio files using the sanitized prefixes as search criteria.
  3. Invoke FFmpeg – Calls moviepy.video.io.ffmpeg_tools.ffmpeg_merge_video_audio to combine the streams into a single MP4 container with synchronized audio.
  4. Cleanup – Deletes the original separate video and audio files, moves the merged result from the output folder to the main download directory, and removes the empty temporary directory.

This workflow ensures that users receive a single, ready-to-play video file without leftover temporary components cluttering the filesystem.

Implementation Details in VideoService.merging

The core merge logic resides in the VideoService.merging method. This function handles the FFmpeg integration and file system operations required to produce the final output.


# Conceptual representation based on pyutube/services/VideoService.py lines 57-84

def merging(self, video_filename: str, audio_filename: str):
    # 1. Create output subdirectory

    output_dir = os.path.join(self.path, "output")
    os.makedirs(output_dir, exist_ok=True)
    
    # 2. Locate actual files using sanitized prefixes

    video_path = self._resolve_file_path(video_filename)
    audio_path = self._resolve_file_path(audio_filename)
    
    # 3. Merge using MoviePy's FFmpeg wrapper

    from moviepy.video.io.ffmpeg_tools import ffmpeg_merge_video_audio
    output_path = os.path.join(output_dir, f"{video_filename}.mp4")
    ffmpeg_merge_video_audio(video_path, audio_path, output_path)
    
    # 4. Cleanup and move to final location

    os.remove(video_path)
    os.remove(audio_path)
    final_path = os.path.join(self.path, os.path.basename(output_path))
    shutil.move(output_path, final_path)
    os.rmdir(output_dir)

The method relies on moviepy.video.io.ffmpeg_tools.ffmpeg_merge_video_audio to handle the codec-level merging, ensuring compatibility across different stream formats without requiring users to manage FFmpeg commands manually.

Practical Usage Examples

Command Line Interface

When using the CLI, Pyutube automatically handles the merge process after downloading separate streams:


# Download a YouTube video; Pyutube fetches separate video/audio 

# streams and merges them into a single MP4 file automatically.

pyutube download https://www.youtube.com/watch?v=abcdefg -f

Programmatic Download Service

For Python applications, instantiate DownloadService to execute the full pipeline:

from pyutube.services import DownloadService

# Initialize with target URL and download path

svc = DownloadService(
    url="https://www.youtube.com/watch?v=abcdefg",
    path="/tmp/yt_downloads",
    quality=None,          # Auto-select best quality

    is_audio=False,        # Download full video, not audio-only

    make_playlist_in_order=False,
)

# Execute download and automatic merge

svc.download()

# Final merged file appears in /tmp/yt_downloads/

Manual Merging of Existing Files

If you already have separate video and audio files downloaded, you can invoke the merge functionality directly:

from pyutube.services import VideoService

# Initialize VideoService (URL and quality required for initialization)

video_service = VideoService(
    url="", 
    quality="", 
    path="/tmp/yt_downloads"
)

# Merge existing files by their sanitized base names

video_service.merging("myvideo_1080p", "myvideo_audio")

# Result: myvideo_1080p.mp4 in /tmp/yt_downloads/

Summary

  • Pyutube downloads YouTube's adaptive streams separately because the platform provides video and audio as distinct files for quality optimization.
  • The VideoService.merging method in pyutube/services/VideoService.py (lines 57-84) handles the consolidation using MoviePy's FFmpeg wrapper.
  • DownloadService.download_video in pyutube/services/DownloadService.py (lines 86-99) orchestrates the workflow, triggering the merge after both streams are saved.
  • The process creates a temporary output directory, resolves actual file paths from sanitized prefixes, merges via ffmpeg_merge_video_audio, and cleans up temporary files automatically.

Frequently Asked Questions

Does Pyutube require FFmpeg to be installed separately?

Yes, Pyutube requires FFmpeg to be installed on your system because it uses moviepy.video.io.ffmpeg_tools.ffmpeg_merge_video_audio to combine streams. MoviePy acts as a Python wrapper that invokes FFmpeg commands, so the binary must be available in your system PATH for the merge operation to succeed.

What happens to the original video and audio files after merging?

The original separate files are deleted automatically during the cleanup phase of VideoService.merging. After FFmpeg successfully creates the merged file in the temporary output directory, the method removes the individual video and audio streams, moves the merged file to the main download folder, and deletes the empty output directory.

Can I use Pyutube to merge video and audio files I downloaded separately?

Yes, you can manually trigger the merge functionality by importing VideoService and calling the merging method directly with the sanitized filenames of your existing video and audio files. The method expects the files to exist in the path directory specified during VideoService initialization.

Why does Pyutube create a temporary "output" folder during merging?

Pyutube creates the output subdirectory as a staging area to prevent filename conflicts and ensure atomic file operations. By generating the merged file in an isolated folder first, the tool can verify the merge succeeded before deleting the original streams and moving the final file to its destination. This prevents data loss if the FFmpeg process fails midway.

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 →