For service operators

Make your service look right in PhiPTV.

Your customers bring their own login to PhiPTV. This page explains exactly what the app reads from an Xtream Codes panel or an M3U + XMLTV setup, how to organise it, and what PhiPTV does not do — so nobody promises a feature that is not there.

Scope of this page.

PhiPTV is a media player. It does not provide, host, list, recommend or endorse any channel, stream or service, and this guidance is not an endorsement of anyone who follows it. It is published so that operators can make a source their customers already have work well in the app. Operators are responsible for the rights to whatever they distribute.

Start here

Thirteen things that make the biggest difference

Each one is something PhiPTV reads and shows. Skip one and the customer sees a grey tile, an empty guide row or a mystery error.

  1. Fill every logo and poster

    stream_icon on live and VOD, cover on series, tvg-logo in M3U. An empty value shows a generated placeholder — PhiPTV never guesses a logo.

  2. Match guide ids exactly

    epg_channel_id (Xtream) or tvg-id (M3U) must equal the XMLTV channel="…" id byte for byte. There is no name fallback and no normalisation. No id, no guide row.

  3. Serve the guide at /xmltv.php

    Xtream users get it automatically, unless they set their own guide URL, which is tried first. Gzip is supported and there is no size cap. M3U users must paste a guide URL by hand, so publish it clearly.

  4. Put a ±HHMM offset on every timestamp

    Write it after a space, as in 20260901180000 +0100. A timestamp without one, or with the offset joined to the digits, is treated as UTC and your evening schedule lands in the afternoon.

  5. Order categories and streams the way you want them shown

    PhiPTV starts from the order your API returns and never sorts channels or titles alphabetically. Customers can hide, move and pin categories on their own TV; that is stored against the category name, so renaming a category resets it for them.

  6. Order streams inside each category the way they should be numbered

    PhiPTV numbers channels by their position in the list on screen; every category starts at 1, and the remote's digits dial that position. num is not shown. Keep stream_id stable: favourites, history and reminders are stored against it.

  7. Serve live streams as raw .ts and as .m3u8

    PhiPTV rewrites a live link ending in .m3u8 under /live/ to .ts and tunes that first. It switches to the .m3u8 for the rest of the session only after a 401, 403, 404, 410 or DNS failure on the .ts, which costs the viewer a reconnect.

  8. Flag adult content with is_adult

    It is the only signal the parental filter and Kids mode use. There is no keyword matching on names or categories; an unflagged adult category is visible to a filtered profile.

  9. Tag radio with stream_type: "radio_streams"

    Or radio="true" in M3U. Tagged stations appear in the Radio section and stay in their category in Live TV. Without a tag PhiPTV guesses from the group and station name.

  10. Fill the detail endpoints

    get_vod_info and get_series_info feed the detail pages: plot, cast, director, genre, release date, duration, backdrop, trailer, and per-episode stills. List endpoints alone give a bare card.

  11. Reject over-limit connections with 429 or 509

    Those, and 5xx errors, are retried quietly with backoff. A 401, 403, 404 or 410 marks the channel dead and prompts the customer to refresh their playlist.

  12. Mark the channels you archive

    Set tv_archive to 1 and tv_archive_duration to a whole number of days. PhiPTV then offers catch-up for those channels: replay from the guide, and start the programme on now over. See Catch-up.

  13. Do not filter by User-Agent

    The API and M3U file are fetched with a MAG250 string; catalogue downloads also send an X-MAC header and a mac=… cookie. Streams, movies and catch-up replays play with VLC/3.0.20 LibVLC/3.0.20. The XMLTV guide and logo or poster requests send the platform default (Dart/… (dart:io)) and no other headers.

Downloadable versions: the compatibility checklist, an M3U template and an XMLTV sample are on the Downloads page.

Xtream Codes API

What PhiPTV asks your panel, and which fields it keeps

Catalogue calls go to player_api.php with the customer's username and password; the guide comes from xmltv.php and catch-up from /timeshift/. Fields not listed here are ignored.

CallFields usedWhere it shows
Sign-in (no action)user_info.auth (a number; 0 means a wrong login), max_connections, username; server_info.time_now, server_info.timestamp_nowmax_connections sets the "Auto" stream limit; the server clock fields set the time in catch-up links. status, exp_date and active_cons are read but not shown. server_info.timezone is not read.
get_live_categories
get_vod_categories
get_series_categories
category_id, category_name — in array orderThe category rails and filters, in your order.
get_live_streamsstream_id, name, stream_icon, category_id, epg_channel_id, stream_type, is_adult, tv_archive, tv_archive_durationLive TV list, guide matching, radio section, parental filter, catch-up. num is stored but not shown.
get_vod_streamsstream_id, name, container_extension, stream_icon, rating, category_id, year, genre, is_adultMovies grid, genre chips, poster.
get_seriesseries_id, name, cover, category_id, year, genre, rating (0–10), last_modified, is_adultSeries grid; last_modified drives new-episode notifications.
get_vod_infoinfo: name, plot, cast, director, genre, releasedate, duration/duration_secs, backdrop_path, movie_image or cover_big, rating, country, tmdb_id, youtube_trailer; movie_data: stream_id, name, category_id, container_extension (description, actors and release_date are accepted as fallbacks; duration is used before duration_secs)Movie detail page and trailer.
get_series_infoinfo: name, cover, plot, cast, director, genre, releaseDate (or release_date), rating (0–10; rating_5based only as a fallback, shown as if out of 10), backdrop_path, youtube_trailer. episodes: an object keyed by season number; each episode with id, title, episode_num, container_extension, added, info.plot, info.duration_secs (or info.duration), info.releasedate, info.rating, info.movie_imageSeasons, episode list, episode stills; added dates new-episode alerts. The top-level seasons array is ignored.
xmltv.phpThe XMLTV guide, see belowProgramme guide, now-and-next, replay marks, reminders.

Values that must be in a specific shape

Small things that silently break when they are not.

  • is_adult — 1, true or "1". The string "true" does not count.
  • stream_type — exactly radio_streams for radio; anything else is treated as live TV.
  • rating — 0 to 10. Zero or above ten is shown as unrated.
  • auth — send 0 as a number for a wrong login; the string "0" shows a generic server error instead.
  • last_modified, exp_date — epoch seconds, not milliseconds.
  • tv_archive, tv_archive_duration — 1, true or "1"; the duration as a whole number of days.
  • episodes — an object keyed by season number, not an array.
  • Usernames and passwords — written into stream paths as typed; avoid / ? # % and spaces.
  • container_extension — used verbatim in the movie and episode URL; defaults to mp4 when missing.
  • backdrop_path — an array (first entry used) or a single string; both work.
  • youtube_trailer — a bare YouTube id or a full URL.
  • Timeouts — 30 seconds to connect and to wait for data on catalogue calls, 60 seconds while waiting for guide data. A slow live list fails the refresh; a slow movie or series list keeps the previous one.

How refresh works

PhiPTV downloads the whole catalogue; there is no delta API.

  • Automatic refresh once a day while PhiPTV is open: it checks five minutes after start and then hourly, and runs once the customer's hour has passed (04:00 by default) and the last refresh is more than 22 hours old.
  • If the fields PhiPTV keeps are unchanged (same items, same order, same category order), the database rewrite is skipped, so a stable catalogue is cheap for everyone.
  • A live list that comes back empty is rejected rather than wiping the customer's channels — a panel outage does not empty their TV. Empty movie or series lists keep the previous ones. Only after the customer changes the server or login may an empty answer clear them.
  • Stream URLs are built as /live/user/pass/id.m3u8, /movie/user/pass/id.ext and /series/user/pass/id.ext. allowed_output_formats is not consulted.

Catch-up (replay)

Xtream only. What makes the replay mark appear, and how the link is built.

  • A channel can be replayed when tv_archive is set and tv_archive_duration is a whole number of days above 0. In the guide, a programme that has ended and started within that many days carries a replay mark; OK plays it from the start. The guide reaches 12 hours back.
  • In fullscreen live on a TV, LEFT replays the programme on now from its start (the last hour when there is no guide), a minute behind live, with a Go live button.
  • The link is built from the channel's live link and the guide's start and stop, trying in order: /timeshift/USER/PASS/MINUTES/YYYY-MM-DD:HH-MM/ID.m3u8, the same ending .ts, then /streaming/timeshift.php?username=…&password=…&stream=ID&start=…&duration=MINUTES. The first that works is remembered for your server.
  • The start is in your server's local time, taken from server_info.time_now and timestamp_now; without them it is UTC. Accurate programme times matter.
  • Each shape is checked with a short request (MAG250 User-Agent, closed after the first kilobyte) and must answer 2xx with an HLS playlist, MPEG-TS, MP4 or FLV. The replay itself plays with the VLC User-Agent and leaves no Continue Watching entry.
M3U playlists

A correctly formed M3U, line by line

An M3U gives a customer Live TV and Radio. Movies and series are not detected in an M3U — offer the Xtream API to customers who want them.

The shape PhiPTV expects

Two lines per entry. That is the whole contract.

  • Line 1 of the file starts with #EXTM3U, or nothing is imported.
  • Each entry is one #EXTINF line followed by the URL line.
  • No #EXTVLCOPT, #KODIPROP or #EXTGRP line in between. An entry with an extra line in the middle is dropped. This is the single most common reason a channel is missing.
  • The channel name is the text after the last comma. tvg-name is not read.
  • Channel numbers are positions inside each category, starting at 1, so the order of lines within a group-title is the order customers dial; tvg-chno is ignored. Categories appear in the order each group-title first occurs.
  • Each entry is identified by its position in the file. Inserting, removing or reordering lines moves customers' favourites and history to other channels, so add new channels at the end.
  • Do not put " or ' inside an attribute value; the value stops at the first quote. Do not append |User-Agent=… to URLs.
#EXTM3U
#EXTINF:-1 tvg-id="news1.example" tvg-logo="https://cdn.example.com/logos/news1.png" group-title="News" tvg-country="GB" tvg-language="English",News Channel 1
http://stream.example.com/live/USER/PASS/1001.ts

#EXTINF:-1 tvg-id="sport1.example" tvg-logo="https://cdn.example.com/logos/sport1.png" group-title="Sport",Sport Channel 1 HD
http://stream.example.com/live/USER/PASS/2001.ts

#EXTINF:-1 tvg-logo="https://cdn.example.com/logos/radio1.png" group-title="Radio" radio="true",Radio Station 1
http://stream.example.com/radio/station1.mp3

Attributes that are read

Everything else on the line is ignored.

AttributeUsed for
tvg-idGuide matching. Must equal the XMLTV channel id exactly.
tvg-logoChannel logo. http or https; no headers are sent.
group-titleCategory. Spell it identically on every line.
radioradio="true" puts the entry in the Radio section; it also stays in its Live TV category.
tvg-country, tvg-languageStored; not used yet.
tvg-name, tvg-chno, catchup*, url-tvg, x-tvg-urlNot read (there is no catch-up for M3U).

Guide for M3U customers: the header's url-tvg is not read, so publish the XMLTV URL where customers can copy it. They paste it into the playlist's EPG field in PhiPTV, once.

XMLTV guide

Guide data that lands on the right channel at the right time

What is parsed

Only <programme> elements. <channel> blocks, their display names and icons, are ignored — names and logos come from the playlist.

  • start, stop and channel are all required.
  • The first <title>, <desc>, <category> and <icon> in each programme are used. lang attributes are not inspected, so put the language you want shown first.
  • Timestamps are YYYYMMDDHHMMSS ±HHMM with a space before the offset. Without an offset the time is treated as UTC.
  • Entities: &amp; &lt; &gt; &quot; &apos;. Numeric references such as &#233; are not decoded — write the character in UTF-8. CDATA sections are not unwrapped.
  • On import PhiPTV keeps programmes that end after 24 hours ago and start within the next 48 hours, for the channel ids in the customer's playlist. The guide shows 12 hours back and 24 hours ahead. Gzip bodies and gzip transport are both fine; there is no size cap.
  • The guide is refreshed every 12 hours by default (the customer can choose 4, 6, 12 or 24) while Live TV or the guide is open, an hour after a failed attempt, and on demand. It is fetched without a custom User-Agent, and its start and stop times also drive reminders and catch-up links.
<?xml version="1.0" encoding="UTF-8"?>
<tv>
  <programme start="20260901180000 +0100" stop="20260901183000 +0100" channel="news1.example">
    <title lang="en">Evening News</title>
    <desc lang="en">The day's headlines, weather and sport.</desc>
    <category lang="en">News</category>
    <icon src="https://cdn.example.com/stills/evening-news.jpg"/>
  </programme>
</tv>

channel="news1.example" must equal the stream's epg_channel_id or tvg-id exactly.

Streams & formats

What the player accepts

Playback runs on the platform's hardware decoders. On TV boxes, video is decoded in hardware only — a codec the box cannot decode in hardware will not play.

ItemSupportNotes
HLS (.m3u8)YesLive and VOD. Live joins at the live edge.
Raw MPEG-TS over HTTP (.ts)YesPreferred for live: fastest zap. Serve it alongside HLS.
DASH (.mpd)YesWithout DRM.
MP4, MKV, WebM, MP3, AACYesProgressive files for movies, episodes and radio.
Audio and subtitle tracksEmbedded onlyTag tracks with an ISO 639 language and a clear label. The label is shown first; und or empty languages fall back to it. PhiPTV remembers the choice per series and per movie.
External subtitle files (.srt, .vtt)No
RTSP, RTMP, Smooth StreamingNo
http (cleartext)Yeshttp and https both work; http↔https redirects are followed. https needs a publicly trusted certificate.
Custom headers from the playlistNoUser-Agent is fixed (see checklist). No referrer, cookie or token headers can be set per stream.
Stream ends earlyAuto-reopenIf a live session is cut by the server, PhiPTV reopens it up to three times before showing an error.
Connection limit reachedWarnedPhiPTV counts devices signed in to the same PhiPTV account that stream from the same server and username. It stops a new stream with a message (live TV: pressing again within 10 seconds goes ahead) before exceeding max_connections. Reject with 429/509 so the retry logic applies.
Catch-up (Xtream archive)YesSee Catch-up. Not available for M3U.
DRM-protected streamsNoNo Widevine, PlayReady or ClearKey settings are read.
Organising the catalogue

A layout that reads well on a TV

PhiPTV shows your structure as it is. These conventions make it look intentional.

Categories

Shown in the order you return them, spelled exactly as you return them. Customers can hide, move and pin categories; that is stored by category name.

  • Pick one prefix style and keep it: UK | Sport everywhere, never UK| Sport on some rows. Every spelling variant becomes a separate category.
  • Put the categories customers open most at the top of the array. Uncategorised or unlisted names fall to the bottom.
  • Keep names short enough for a rail label: about 24 characters.
  • Give radio its own category and tag the streams. Tagged stations appear in the Radio section and stay in their category in Live TV. Untagged channels whose group contains the word "radio", or whose name reads like a station (Radio, FM, راديو…) with no TV mark, are also listed under Radio.
  • Adult categories: flag every stream in them with is_adult; the category name alone hides nothing.

Channels

Listed in the order you return them; the number is the position in the list on screen.

  • Put the channels customers want on low numbers at the top of each category; digit entry dials positions within the category on screen.
  • Name the channel, then the quality: Sport Channel 1 HD. In M3U names, doubled suffixes like HD HD are collapsed; Xtream names are shown as sent.
  • One logo per channel, square or nearly square, around 256 px, PNG or JPG, on a host that does not check the User-Agent or Referer.
  • Give every channel an epg_channel_id, even if a few have no guide yet — a blank id leaves an empty guide row with nothing to focus and nothing to replay.

Movies and series

Posters, genres and detail pages come straight from your metadata.

  • Portrait posters in stream_icon / cover; a wide backdrop_path for the detail page.
  • genre as a comma-separated list; PhiPTV builds its genre chips from it. If it is empty the app falls back to keyword guessing from the title.
  • Series: one episodes map keyed by season number, episodes numbered with episode_num, each with its own still in info.movie_image.
  • Update last_modified when a new episode is added, and fill each episode's added — they drive the new-episode alert for customers who favourited the series, after their next catalogue refresh.
  • Ratings on a 0–10 scale. Durations in seconds (duration_secs) are safest.

Accounts and limits

What PhiPTV shows the customer about their account.

  • PhiPTV shows the customer their Xtream username. status, the expiry date and active_cons are not shown, and an expired account is only noticed when streams fail.
  • PhiPTV's stream-limit guard uses max_connections by default and warns the customer before another of their own devices pushes them over it.
  • Sessions you close from the server side are reopened automatically up to three times; a 429 or 509 is retried with backoff; 401/403/404/410 is treated as a dead link.
Be precise with customers

What PhiPTV does not do

So that nobody sells a feature the app does not have.

FeatureStatusDetail
Catch-up from an M3U (catchup, catchup-days, catchup-source)Not supportedCatch-up works with the Xtream archive only.
Stalker / MAG portalsNot supportedPhiPTV accepts an M3U URL or an Xtream Codes login only.
Movies & Series in an M3UNot detectedEvery M3U entry is a live channel or a radio station. Use the Xtream API for VOD.
External subtitle filesNot supportedOnly tracks embedded in the container.
Per-stream headers (#EXTVLCOPT, #KODIPROP)Not supportedWorse: such lines cause the entry to be dropped. Do not emit them.
RTSP, RTMP, Smooth StreamingNot supportedHLS, DASH, MPEG-TS and progressive files only.
XMLTV <channel> dataIgnoredDisplay names and icons come from the playlist, not the guide.
Multi-language guide entriesFirst onlyThe first title and description in each programme are shown regardless of lang.
DRM-protected streamsNot supportedNo licence settings are read.
tvg-chno, url-tvg, allowed_output_formats, server_info.timezoneIgnoredChannel numbers are positions in the list; formats are fixed as described above.
Something missing that your customers keep asking for?

This page is kept in step with what the app does. When a feature ships, it moves out of this table.

Content responsibility

PHIPTV is a media player only. It does not provide, host, bundle, or endorse any channels, streams, or other content.

You supply your own playlists and sources, and you are solely responsible for ensuring you have the legal right to access any content you add.

Shown and accepted on the first launch of the app, before anything else.