A command-line utility to download contents from M3U8 playlists.
The most basic usage is as follows:
$ kai -u "http://example.com/playlist.m3u8"
This will:
- Select the video stream with the highest resolution and download it.
- If there are any audio or subtitle streams attached to the video, they will be downloaded as well.
- The output file will be saved as
media.mkv
in the current working directory.
By default, Kai will mux media streams using a built-in implementation of FFmpeg. If you want to force the usage of the FFmpeg CLI tool installed on your system, pass the --prefer-ffmpeg-cli
flag:
$ kai \
--url <url> \
--prefer-ffmpeg-cli
You can also send custom HTTP headers with the -H
/--header
flag:
$ kai \
--url <url> \
--header "User-Agent: ..." \
--header "Authorization: ..."
You can see a list of all available flags with kai -h
.
You can load any M3U8 playlist with the -u
/--url
flag. It supports loading playlists from HTTP URLs and local files.
Loading from HTTP URLs:
$ kai -u "http://example.com/playlist.m3u8"
Loading from local files:
$ kai -u "/path/to/playlist.m3u8"
You can also set the base URL of the M3U8 playlist with the --base-url
flag:
$ kai --base-url "https://example.com" -u "./master.m3u8"
This can be useful in cases where you have already downloaded an .m3u8
file from somewhere else and want to parse it with Kai without explicitly loading from the original URL.
There are two ways of listing the streams of an M3U8 playlist: -S
/--list-streams
and --dump
.
The first one will show a human-readable listing:
Command:
$ kai -S -u "https://kartatz.github.io/hls-example/master.m3u8"
Output:
Playlist #0
Metadata:
Type: Master Playlist
Media #0
Metadata:
Type: Audio
Identification: audio_aac
Name: eng
Language: eng
Options: (default = yes, autoselect = yes, forced = no)
Duration: 00:10:00
Segments: 61 (~9 seconds)
Variant Stream #0
Metadata:
Bandwidth: 140800
Codecs: avc1.64001f,mp4a.40.2
Resolution: 880x480
Frame rate: 30
Duration: 00:10:00
Audio: eng (ID = "audio_aac")
Segments: 60 (~10 seconds)
Size: ~10.07 MB
Variant Stream #1
Metadata:
Bandwidth: 281600
Codecs: avc1.64001f,mp4a.40.2
Resolution: 1280x720
Frame rate: 30
Duration: 00:10:00
Audio: eng (ID = "audio_aac")
Segments: 60 (~10 seconds)
Size: ~20.14 MB
The other one is most useful when parsing M3U8 playlists with third-party programs, as it outputs a JSON tree:
Command:
$ kai --dump -u "https://kartatz.github.io/hls-example/master.m3u8"
Output:
{
"type": {
"id": 1,
"name": "Master Playlist"
},
"streams": [
{
"type": {
"id": 13,
"name": "Media Stream"
},
"mtype": {
"id": 2,
"name": "AUDIO"
},
"group-id": "audio_aac",
"language": "eng",
"assoc-language": null,
"name": "eng",
"default": true,
"autoselect": true,
"forced": false,
"instream-id": null,
"characteristics": null,
"channels": null,
"duration": 600,
"average-duration": 9,
"segments": 61,
"uri": "https://kartatz.github.io/hls-example/audio.m3u8",
"media": [ /* ... */ ]
},
{
"type": {
"id": 14,
"name": "Variant Stream"
},
"bandwidth": 140800,
"average-bandwidth": null,
"program-id": null,
"codecs": "avc1.64001f,mp4a.40.2",
"resolution": {
"width": 880,
"height": 480
},
"frame-rate": 30,
"duration": 600,
"average-duration": 10,
"audio": {
"name": "eng",
"group-id": "audio_aac"
},
"video": null,
"subtitles": null,
"closed-captions": null,
"segments": 60,
"size": 10560000,
"uri": "https://kartatz.github.io/hls-example/video-sd.m3u8",
"media": [ /* ... */ ]
},
{
"type": {
"id": 14,
"name": "Variant Stream"
},
"bandwidth": 281600,
"average-bandwidth": null,
"program-id": null,
"codecs": "avc1.64001f,mp4a.40.2",
"resolution": {
"width": 1280,
"height": 720
},
"frame-rate": 30,
"duration": 600,
"average-duration": 10,
"audio": {
"name": "eng",
"group-id": "audio_aac"
},
"video": null,
"subtitles": null,
"closed-captions": null,
"segments": 60,
"size": 21120000,
"uri": "https://kartatz.github.io/hls-example/video-hd.m3u8",
"media": [ /* ... */ ]
}
]
}
There are two flags that can be used for selecting specific streams to download: --select-stream
and --select-media
.
The --select-stream
flag can be used to select streams based on their position in the master playlist and also by resolution. It is mainly intended for selecting video streams.
The --select-media
flag is used to select anything that is not a video, like audio and subtitles. It can be used to select streams based on their position in the master playlist.
Here is an example playlist:
Playlist #0
Metadata:
Type: Master Playlist
Media #0
Metadata:
Type: Audio
Identification: audio_aac
Name: eng
Language: eng
Options: (default = yes, autoselect = yes, forced = no)
Duration: 00:10:00
Segments: 61 (~9 seconds)
Variant Stream #0
Metadata:
Bandwidth: 140800
Codecs: avc1.64001f,mp4a.40.2
Resolution: 880x480
Frame rate: 30
Duration: 00:10:00
Audio: eng (ID = "audio_aac")
Segments: 60 (~10 seconds)
Size: ~10.07 MB
Variant Stream #1
Metadata:
Bandwidth: 281600
Codecs: avc1.64001f,mp4a.40.2
Resolution: 1280x720
Frame rate: 30
Duration: 00:10:00
Audio: eng (ID = "audio_aac")
Segments: 60 (~10 seconds)
Size: ~20.14 MB
This playlist contains 2 video streams. The first one (Variant Stream #0
) is in 480p, while the other one (Variant Stream #1
) is in 720p.
By default, when not explicitly selected, Kai chooses to download the variant stream with the highest bandwidth, which most of the time will be the video stream with the highest resolution.
But what if you want to download the 480p variant or any other resolution?
You can use --select-stream
by providing it the stream position, resolution, or wordings like worst
, medium
, and best
.
# Select by position
$ kai --select-stream "0" -u <url>
# Select by resolution
$ kai --select-stream "480p" -u <url>
# You can also use "worst", "medium" or "best"
$ kai --select-stream "worst" -u <url>
You can select audio and subtitle streams with the --select-media
flag. Unlike --select-stream
, you can specify this argument as many times as you want.
It accepts a numeric value (which represents the position the media stream appears in the master playlist) and also a wildcard character (*
).
# Select by position
$ kai --select-media "0" -u <url>
# Select all streams (audio, subtitles, etc)
$ kai --select-media "*" -u <url>
Kai uses a built-in implementation of FFmpeg to mux media streams. However, sometimes you might want to avoid using it.
The internal implementation is more limited compared to the FFmpeg CLI tool; It supports only a limited set of codecs and may fail to mux media streams in some edge cases.
To work around this, you can force Kai to use the FFmpeg CLI tool installed on your system with the --prefer-ffmpeg-cli
flag.
$ kai --prefer-ffmpeg-cli -u <url>
Since M3U8 playlists deliver content through fragmented media streams (also called media segments), downloading an entire stream can take a lot of time, especially very long ones, which may contain thousands of media segments.
By default, Kai downloads media segments concurrently (i.e., multiple segments are downloaded simultaneously). You can control this behavior with the -c
/--concurrency
flag.
$ kai -c <number> -u <url>
This flag accepts a numeric value that represents how many media segments should be downloaded simultaneously. It defaults to the number of CPU cores available on your machine.
Kai supports setting a custom proxy for network requests with the -x
/--proxy
flag.
# SOCKS5 proxy
$ kai -x socks5://<hostname>:<port> -u <url>
# SOCKS4 proxy
$ kai -x socks4://<hostname>:<port> -u <url>
# HTTP(S) proxy
$ kai -x http://<hostname>:<port> -u <url>
The easiest way to install Kai is by downloading a pre-compiled binary from the releases page.
Below is a listing of some of the supported platforms and their download links: