Skip to content

Repository files navigation

m3u8 Downloader

English | 简体中文 | 日本語

Download an HLS .m3u8 stream, clean and prepare its media segments locally, then package the result as an .mp4 with ffmpeg.

(Different to ffmpeg: Support m3u8 with fake image headers, and cleanup segments with invalid prefix.)

The repository now includes three ways to use the downloader:

  • A native SwiftUI macOS app in M3U8Downloader/.
  • A Python command-line downloader in download_m3u8.py.
  • A small local Flask web UI in web_app.py.

The downloader is intended for streams you are allowed to download. It does not bypass DRM or other access controls.

Native macOS App

MIT Licence macOS 14+ Apple Silicon Swift 6 Liquid Glass

M3U8Downloader is a SwiftUI app for macOS that wraps the downloader workflow in a desktop interface. It accepts either a remote playlist URL or a local .m3u8 file, downloads and cleans the HLS segments, and calls ffmpeg to package the final MP4.

M3U8Downloader macOS app

App Features

  • Remote URL and local .m3u8 file sources.
  • Output filename and save-folder selection.
  • Custom HTTP headers, one Name: Value header per line.
  • Preferred quality selection for master playlists: Auto / Best, 1080p, or 720p.
  • Segment worker, retry, timeout, and overwrite controls.
  • Download queue sidebar with search.
  • Per-download progress, segment counts, FFmpeg logs, retry, cancel, Show in Finder, and Open Video actions.
  • Preferences for ffmpeg path and the default download folder.

macOS Requirements

  • macOS 14 or newer.
  • Xcode with the macOS SDK.
  • ffmpeg installed locally.

Install ffmpeg with Homebrew if needed:

brew install ffmpeg

The app auto-detects common ffmpeg locations:

  • /opt/homebrew/bin/ffmpeg
  • /usr/local/bin/ffmpeg
  • /usr/bin/ffmpeg
  • ffmpeg from PATH

You can override the path in the app Preferences screen.

Build The App From Source

The Xcode project is checked in at:

M3U8Downloader/M3U8Downloader.xcodeproj

Build a Release app from the terminal:

./scripts/build-macos-app.sh

The script builds the M3U8Downloader scheme from source and leaves Xcode build products under .build/macos/:

.build/macos/DerivedData/Build/Products/Release/M3U8Downloader.app

Generated app bundles and build output are intentionally ignored by Git. The repository should contain source, project files, scripts, docs, and tests rather than checked-in .app bundles or local Xcode build directories.

You can also open the project in Xcode:

open M3U8Downloader/M3U8Downloader.xcodeproj

Then select the M3U8Downloader scheme and run it.

Package A macOS Release

Before the first notarized release, store your Apple notarization credentials in Keychain under a profile name. For example, create the develop profile:

xcrun notarytool store-credentials develop

Create a signed and notarized distributable DMG with that profile:

NOTARYTOOL_PROFILE=develop ./scripts/package-macos-release.sh

The artifact is written to dist/ using the current Git version, for example:

dist/M3U8Downloader-<version>-macOS.dmg

The mounted DMG contains M3U8Downloader.app and an Applications link, so users can install the app by dragging it onto Applications.

To produce a ZIP instead:

NOTARYTOOL_PROFILE=develop ./scripts/package-macos-release.sh --format zip

Set VERSION to override the artifact name:

NOTARYTOOL_PROFILE=develop \
  VERSION=1.0.0 \
  ./scripts/package-macos-release.sh

Release packages are signed with the first valid Developer ID Application identity found in your keychain. You can provide an explicit identity when needed:

CODESIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)" \
  NOTARYTOOL_PROFILE=develop \
  VERSION=1.0.0 \
  ./scripts/package-macos-release.sh

The packaging script submits the ZIP or DMG with notarytool --wait. After Apple accepts it, the script staples and validates the ticket. ZIP artifacts are rebuilt from the stapled app before the final artifact path is printed. Any submission, stapling, or validation failure stops the release.

For a signed local package that should not be submitted to Apple, set SKIP_NOTARIZATION=1. For an unsigned local package, set SKIP_CODESIGN=1; unsigned packages also skip notarization.

Run The App

After building, launch the app from the build output:

open ./.build/macos/DerivedData/Build/Products/Release/M3U8Downloader.app

To download a stream:

  1. Choose Remote URL or Local .m3u8 File.
  2. Enter the playlist URL or browse to a local playlist.
  3. Choose an output filename and save folder.
  4. Expand Advanced Settings when you need headers, quality, worker, retry, or timeout options.
  5. Click Download Stream.

Completed jobs can be opened in Finder or played directly from the job detail screen.

How to get .M3U8 url

In Chrome browser, press F12, click the tab to the Network page, enter "m3u8" in the Filter box. Press F5 to refresh the page, if the video in the web page uses an HLS source, you can capture the video stream address there. Right click and select "Copy -> Copy as cURL" to get the full URL with headers.

Copy

Regenerate The Xcode Project

The app also includes an XcodeGen spec:

M3U8Downloader/project.yml

If you update the project structure and want to regenerate the .xcodeproj, install XcodeGen and run:

cd M3U8Downloader
xcodegen generate

Python CLI

The CLI downloads remote playlists segment-by-segment, strips a small fake image header from segments when present, writes a cleaned local playlist, then uses ffmpeg to combine the local playlist into the final MP4.

CLI Requirements

  • Python 3.
  • ffmpeg available on your PATH.

CLI Usage

Download a remote playlist:

python3 download_m3u8.py "https://cdn3.turboviplay.com/data1/685f4c3d5bc66/685f4c3d5bc66.m3u8"

By default, MP4s are saved in your user Downloads folder, such as /Users/ericchan/Downloads/, using the playlist filename.

Save with a custom output filename:

python3 download_m3u8.py "https://cdn3.turboviplay.com/data1/685f4c3d5bc66/685f4c3d5bc66.m3u8" -o sample.mp4

Download from a local .m3u8 playlist file:

python3 download_m3u8.py ./video.m3u8 -o video.mp4

Pass headers when a stream requires them:

python3 download_m3u8.py "https://example.com/video.m3u8" \
  --header "Referer: https://example.com" \
  --header "User-Agent: Mozilla/5.0" \
  -o video.mp4

Avoid overwriting an existing file:

python3 download_m3u8.py "https://example.com/video.m3u8" -o video.mp4 --no-overwrite

Prefer a 720p or 1080p stream when the master playlist offers one:

python3 download_m3u8.py "https://example.com/video.m3u8" --quality 1080p -o video.mp4

Tune segment downloading:

python3 download_m3u8.py "https://example.com/video.m3u8" \
  --segment-workers 12 \
  --retries 4 \
  --timeout 20 \
  -o video.mp4

Segments are downloaded concurrently and timeout failures are retried.

Local Web UI

Install the UI dependency:

python3 -m pip install -r requirements.txt

Start the local server:

python3 web_app.py

Open http://127.0.0.1:5000 in your browser. The UI lets you queue downloads from either a remote m3u8 URL or an uploaded local .m3u8 file, set the same options as the CLI, watch live logs, see the local saved path, and download a completed MP4 through the browser.

The web app is intended for local use and binds to 127.0.0.1.

Tests

Use the repository virtual environment for tests:

./.venv/bin/python3 -m pytest

License

m3u8Downloader is released under the MIT License. See LICENSE.

About

M3U8 downloader with a SwiftUI macOS app, Python CLI, and local web UI

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages