This is the reference for playlist authors and providers. The app reads the common IPTV flavor of M3U (as used by
other IPTV players and Kodi) plus a few tags of its own. A marker like (since 1.7.1.3) is the first app version that
reads an attribute; attributes without a marker have been supported for years.
Contents:
#EXTM3U: playlist header#EXTINF: channel attributes#EXTGRP#EXTHTTP, #EXTVLCOPT#KODIPROP#EXTATTRFROMURL#EXTM3U url-tvg="http://example.com/epg.xml.gz"
#EXTINF:-1 tvg-id="music1" tvg-logo="http://example.com/logo/music1.png" group-title="Music",Music 1
http://example.com/channel/01.m3u8
#EXTINF:-1 tvg-id="sport.news" group-title="Sports",Sports News
http://example.com/channel/02.m3u8
A fuller sample with archive, headers and DRM:
#EXTM3U url-tvg="http://example.com/epg.xml.gz" catchup="shift" catchup-days="3" refresh="6"
#EXTINF:-1 tvg-id="news1" ch-number="1" tvg-logo="news1.png" group-title="News",News One HD
#EXTVLCOPT:http-user-agent=MyPlayer/1.0
#EXTVLCOPT:http-referrer=http://example.com/
http://example.com/live/news1/index.m3u8
#EXTINF:-1 tvg-id="movies" group-title="Movies" catchup-enable="0",Movies (no archive)
#EXTHTTP:{"Authorization":"Bearer abc123"}
http://example.com/live/movies/index.m3u8
#EXTINF:-1 tvg-id="premium" group-title="Premium",Premium (DRM)
#KODIPROP:inputstream.adaptive.license_type=clearkey
#KODIPROP:inputstream.adaptive.license_key=0123456789abcdef0123456789abcdef:fedcba9876543210fedcba9876543210
http://example.com/live/premium/manifest.mpd
#EXTM3U.#EXTINF, #EXTGRP, #EXTVLCOPT, #KODIPROP, #EXTHTTP, #EXTATTRFROMURL) applies to the next
stream URL; after the URL everything starts over. Other # lines are ignored.name="value", separated by spaces. Names are case-insensitive. Put values in double quotes
(single quotes are not understood).http://, https://, udp://, rtp://, rtsp://, rtmp:// or acestream://
(acestream:// since 1.6.7.6). Lines with other schemes are skipped.#EXTM3U: playlist headerThe first line of the playlist. Its attributes apply to the whole playlist:
url-tvg : address of the XMLTV TV guide. Alias: x-tvg-url. Several addresses can be separated by , or ;.
See TV guide (XMLTV).url-logo : prefix for channel logos. A tvg-logo without :// is appended to it
(url-logo="http://example.com/logos/" + tvg-logo="news1.png").catchup, catchup-type, catchup-days, catchup-time, catchup-source : archive for all channels. See
Archive (catch-up) in playlists.max-conn : how many streams one account may play at the same time (for example for picture-in-picture or
multi-view). Number.billed-till : when the account expires, in unix time (seconds). Shown to the user with the provider’s details.billed-msg : a text about the account (balance or any other note), shown to the user.refresh : reload the playlist on a timer (since 1.6.6.1). The unit depends on the number: 1–24 hours,
25–299 minutes, 300 and more seconds. The shortest period is 10 minutes. refresh="3" reloads every 3 hours.refresh_at : reload the playlist once at this moment (since 1.7.2.1), UTC time in the form
2024-12-31T23:59:59Z (exactly this form: date, T, time, Z).vod_library : address of a media library (movies and series) that comes with this playlist (since 1.7.0.1), in
OTV, M3U (see below) or XML format. Several
addresses separated by , (since 1.7.1.1). A playlist with a vod_library and no channels becomes a media library
only provider.#EXTINF: channel attributes#EXTINF:-1 tag1="value1" tag2="value2",Channel name. The duration (-1 or 0) is ignored.
Identity, category and logo:
tvg-id : the channel’s id in the XMLTV guide (<channel id="...">). The most reliable way to link the guide.tvg-name : the channel’s name as the guide has it, when it differs from the displayed name. Also matched against
<display-name> of the guide.ch-id : the channel’s id in the app (without it the name is used), and its guide id when there is no tvg-id.ch-number : the channel number, used to switch channels with number keys.tvg-logo : the channel logo: a full URL, or a file name appended to url-logo. Alias: logo.group-title : the category. Several categories separated by ; put the channel in each of them
(group-title="News;HD").group-logo : the category icon, a full URL (since 1.6.6.7). Taken from the first channel of the category that has
it.type : what the entry is. Alias: tvg-type (since 1.7.2.2).
radio : a radio station (since 1.7.2.1).movie, series : not a channel but a movie or an episode; the entry goes to the media library (see
below). A separate media library (vod_library) is the better way.playlist : the URL is another playlist to include (see Including other playlists).
Also content-type="playlist".Restrictions:
adult="1" : an adult channel, hidden behind the app’s restrict code. Putting all adult channels in one category
is easier for users to manage.parent-code : any value except 0000 marks the channel restricted, like adult="1". The value itself is not used
as a code: the user enters the app’s own restrict code.adult="1" or parent-code becomes an adult category, unless its name already
matches another category type.TV guide:
tvg-shift : shift the guide of this channel, in whole hours from -12 to 12 (tvg-shift="-2", "+3"). A value
outside that range is read as seconds (tvg-shift="5400" = 1.5 hours).Archive (catch-up), per channel: catchup, catchup-type, catchup-days, catchup-time, catchup-source,
catchup-enable, tvg-rec, timeshift. See Archive (catch-up) in playlists.
Playback:
codec : the player to use for this channel by default (since 1.6.9.3). Several values separated by , in order of
preference: the first one available on the device is used. The user’s own choice for the channel wins.
auto : the provider’s defaulthard : hardware codecs (ExoPlayer)exo1 : the old ExoPlayer v1system : the system video viewsoft_vlc : software VLC codecs (skipped where VLC is not available)external : open in an external playeraudio-track : select this audio track at start, by its number counted from 1 (audio-track="2"). The user’s own
choice for the channel wins; otherwise the track matching the app language is chosen.video-track : select this video track at start, by number (since 1.7.1.4) or by resolution such as 1920x1080
(since 1.7.3.2).bl-audio-track : audio tracks never to use, numbers separated by , (bl-audio-track="1,3") (since 1.7.4.1).bl-video-track : video tracks never to use, numbers and resolutions separated by ,
(bl-video-track="1,1920x1080") (since 1.7.4.1). When every track is blacklisted, the blacklist is ignored.seek_preview : address of a thumbnail for the moment the user seeks to, with
time variables such as {utc} (since 1.7.5.2). See
Preview images while rewinding.Media library entries (when the playlist is a media library, see below):
tvg-season, tvg-episode : season and episode numbers (since 1.7.2.2).tvg-serie : an id that groups entries into one series (since 1.7.4.1).serie-title : the series name (since 1.7.4.1).tvg-imdb, tvg-tmdb : IMDb or TMDB id of the movie or series (since 1.7.4.1).The first line after the tags that starts with a supported scheme (see How the file is read) is the stream address.
HTTP headers can be appended to it after |, as name=value pairs separated by &. This form is not recommended:
values are cut at & and =; use #EXTHTTP instead.
http://example.com/channel/01.m3u8|User-Agent=MyPlayer/1.0&Referer=http://example.com/
User-Agent and Referer (or Referrer) set those headers; any other name is sent as a header as it is.#EXTHTTP is the better way: values are not cut at & or =. Do not mix the two on one channel, the | headers
replace those of #EXTHTTP.Login and password in the address (http://user:pass@example.com/...) are sent as HTTP basic authorization.
The address may use ${token}, ${login}, ${password} and the other
template variables: they are filled in at playback from the
provider’s settings in the app.
#EXTGRP: Category Name#EXTGRP:News sets the category of the next channel, the same as group-title.
An entry with type="playlist" (or content-type="playlist") and an http(s) URL includes that playlist in place:
#EXTINF:-1 type="playlist",Sports
http://example.com/sports.m3u
#EXTM3U. Its header attributes are ignored: the main playlist’s header applies.The guide is given by url-tvg in the header (or epg in OTC). Users can add more
guides themselves, see How to set up the TV guide.
.zip are ignored., or ;.tvg-id = <channel id="..."> (if no exact match, then ignoring letter case), otherwise by
the channel name or tvg-name = <display-name> (ignoring letter case).<channel>: display-name, icon (the first address when several are separated by |; data:image
is accepted).<programme>: title, sub-title, desc (in the user’s language when there are several lang),
category, actor, director, date (the year), icon, country, episode-num (onscreen preferred;
themoviedb.org and imdb.com systems give the TMDB / IMDb id), rating/value (age rating), star-rating/value.tvg-shift moves one channel’s guide in time; users can shift the whole guide in the provider’s properties.A channel’s requests can carry extra headers in these ways (pick one per channel):
#EXTHTTP with a JSON object (best),#EXTVLCOPT for User-Agent, Referer and Origin,#KODIPROP:inputstream.adaptive.stream_headers / manifest_headers (see DRM),| suffix of the stream URL.Headers set by the user in the provider’s properties are sent too; a channel’s own header of the same name wins.
The User-Agent sent is the first one found of: the app-wide HTTP User-Agent setting, a User-Agent in the
channel’s headers (#EXTHTTP, stream_headers), the channel’s http-user-agent / |User-Agent=, the provider’s
User-Agent in its properties, the app’s own.
#EXTHTTP:{..http tags..}Headers as a JSON object (since 1.6.6.9):
#EXTHTTP:{"User-Agent":"MyPlayer/1.0","Referer":"http://example.com/","X-Token":"abc"}
They go with every request of the stream (playlist, segments, keys) and with the DRM license request.
#EXTVLCOPT:parameter="value"VLC-compatible options, one per line:
http-user-agent : the User-Agent header.http-referrer : the Referer header.http-origin : the Origin header (since 1.7.5.2). An Origin given in #EXTHTTP wins.#EXTVLCOPT:http-user-agent=MyPlayer/1.0
#EXTVLCOPT:http-referrer=http://example.com/
Other VLC options are ignored.
#KODIPROP:parameter=valueKodi-compatible properties (the syntax of Kodi’s inputstream.adaptive), one per line. Whether a DRM system works also depends on the device; see DRM-protected channels.
inputstream.adaptive.license_type : the DRM system. Values containing widevine (com.widevine.alpha),
clearkey (clearkey, org.w3.clearkey) or playready (com.microsoft.playready). Always give it: without a
type the app assumes Widevine, also for static keys.inputstream.adaptive.license_key : one of:
http:// or https://) for Widevine, PlayReady or ClearKey. A ClearKey server
(since 1.7.5.2) answers with the keys as a JWK set; cenc and cbcs streams both work.kid:keykid1:key1,kid2:key2 (since 1.7.5.2) or {"kid1":"key1","kid2":"key2"}{"keys":[{"kty":"oct","kid":"...","k":"..."}],"type":"temporary"}url|headers|body|response (since 1.7.5.2), for a server that needs the request wrapped:
Header1=value1&Header2=value2 (URL-encoded values are decoded)R{SSM} (raw), b{SSM} (base64), B{SSM} (base64, URL-encoded) or
D{SSM} (decimal bytes), for example {"token":"abc","challenge":"b{SSM}"}B (base64), Jkey / JBkey (field key of a JSON,
plain / base64), BJkey / BJBkey (the JSON itself base64-encoded), HB (after the HTTP headers)|) uses the license server declared in the stream’s manifest with these
headers and wrappings{SID}, {KID}, {PSSH} variables, GET requestsurl|Header=value&Header2=value2 : extra headers for the license request only (since 1.6.4.1).inputstream.adaptive.drm_legacy (since 1.7.5.2, Kodi 21.5 syntax) : type and license in one value:
com.widevine.alpha|https://license/url|headers, or org.w3.clearkey|kid1:key1,kid2:key2, or
org.w3.clearkey|data:application/json;base64,<JWK set in base64>.inputstream.adaptive.license_data : base64 of the stream’s default key id (since 1.7.0.1), for streams whose
manifest does not declare it.inputstream.adaptive.stream_headers : extra headers, name1=value1&name2=value2 (since 1.6.4.1). Sent with all
requests of the stream, unless manifest_headers is given.inputstream.adaptive.manifest_headers : headers for the manifest request only (mpd / m3u8 / ism)
(since 1.7.5.2). When it is present (even empty), stream_headers go only with segment and key requests, as in
Kodi 21.inputstream.adaptive.manifest_type : the stream format when the URL does not tell it: mpd / dash, hls,
ism (Smooth Streaming).#EXTINF:-1 group-title="Premium",Premium HD
#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha
#KODIPROP:inputstream.adaptive.license_key=https://license.example.com/wv|X-Token=abc
#KODIPROP:inputstream.adaptive.manifest_type=mpd
http://example.com/live/premium/manifest.mpd
#EXTATTRFROMURL:http://some/url/for/data.m3uSince 1.6.9.2. Keeps sensitive or short-lived data (DRM keys, tokens, the stream address itself) out of the playlist: the app requests this URL each time the user starts the channel or media library entry, and applies what it gets.
#EXTINF line, any tags, and the stream URL), or one channel in
OTC JSON (without base64).catchup-source, seek_preview, adult. The name, category, logo and guide id stay as in the playlist.{fromUnix} in the URL is replaced by the start of the archived programme being opened, in unix time (since
1.7.1.6); empty for live.Playlist:
#EXTINF:-1 tvg-id="premium" group-title="Premium",Premium HD
#EXTATTRFROMURL:https://example.com/api/attrs?ch=premium&start={fromUnix}
http://example.com/placeholder
Response:
#EXTINF:-1,Premium HD
#KODIPROP:inputstream.adaptive.license_type=clearkey
#KODIPROP:inputstream.adaptive.license_key=0123456789abcdef0123456789abcdef:fedcba9876543210fedcba9876543210
http://example.com/live/premium/manifest.mpd?token=xyz
Two ways:
vod_library in the header (recommended). Every entry of that M3U is a
library item:
group-title is the folder;tvg-season / tvg-episode or tvg-serie are gathered into series: by tvg-serie when given,
otherwise by the same name; serie-title names the series;tvg-logo is the poster, tvg-imdb / tvg-tmdb link the title to IMDb / TMDB;type="playlist" is a folder whose content (another M3U) is loaded when the user opens it;codec, headers, DRM, #EXTATTRFROMURL, …) work as for channels.vod_library): an entry is taken out of the channel list and put
in the media library when it has type="movie" or type="series". Mark series episodes with tvg-season /
tvg-episode.#EXTM3U
#EXTINF:-1 tvg-logo="http://example.com/posters/show.jpg" tvg-serie="show1" serie-title="Some Show" tvg-season="1" tvg-episode="1" group-title="Drama",Some Show S01E01
http://example.com/vod/show1/s01e01.mp4
#EXTINF:-1 tvg-logo="http://example.com/posters/movie.jpg" tvg-imdb="tt0000001" group-title="Action",Some Movie (2020)
http://example.com/vod/movie.mp4