To ask for help with adding a channel simply fill out this form.
If you want to add a channel to the list yourself, here are the instructions on how to do it.
First select the site from SITES.md that you know has a guide for the channel you need. Then go to the folder with its config and open the file *.channels.xml
.
Make sure that the desired channel is not already in the list. If it is not, simply add its description to the end of the list as shown here:
<?xml version="1.0" encoding="UTF-8"?>
<channels>
...
<channel site="SITE" lang="LANGUAGE_CODE" xmltv_id="CHANNEL_ID" site_id="SITE_ID">CHANNEL_NAME</channel>
</channels>
Attribute | Description | Example |
---|---|---|
SITE | Site domain name. | example.com |
LANGUAGE_CODE | Language of the guide (ISO 639-1 code). | en |
CHANNEL_ID | ID of the channel. Full list of supported channels with corresponding ID could be found on iptv-org.github.io. | HBO.us@East |
SITE_ID | Unique ID of the channel used in the source. | hbo |
CHANNEL_NAME | Name of the channel used in the source. | HBO East |
After that just commit all changes and send a pull request.
If you start to get errors when downloading the guide or if nothing loads at all, please let us know via this form.
If you are not familiar with javascript programming, you can ask for help from other community members through this form. Otherwise, below are the instructions for you.
To start with, you need to create a new folder in the /sites folder and put at least 4 files in it:
example.com.config.js
This file describes what kind of request we need to send to get the guide for a particular channel on a certain date and how to parse the response.
module.exports = {
site: 'example.com',
url({ channel, date }) {
return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}`
},
parser(context) {
try {
return JSON.parse(context.content)
} catch {
return []
}
}
}
From each function in config.js
you can access a context
object containing the following data:
channel
: The object describing the current channel (xmltv_id, site_id, name, lang)date
: The 'dayjs' instance with the requested datecontent
: The response data as a Stringbuffer
: The response data as an ArrayBufferheaders
: The response headersrequest
: The request configcached
: A boolean to check whether this request was cached or not
List of properties that can be assigned to each program during parsing.
Property | Aliases | Type | Required |
---|---|---|---|
start | String | Number | Date() |
true | |
stop | String | Number | Date() |
true | |
title | titles | String | Object | String[] | Object[] |
true |
subTitle | subTitles, sub_title, sub_titles | String | Object | String[] | Object[] |
false |
description | desc, descriptions | String | Object | String[] | Object[] |
false |
date | String | Number | Date() |
false | |
category | categories | String | Object | String[] | Object[] |
false |
keyword | keywords | String | Object | String[] | Object[] |
false |
language | languages | String | Object | String[] | Object[] |
false |
origLanguage | origLanguages | String | Object | String[] | Object[] |
false |
length | String | Object | String[] | Object[] |
false | |
url | urls | String | Object | String[] | Object[] |
false |
country | countries | String | Object | String[] | Object[] |
false |
video | Object |
false | |
audio | Object |
false | |
season | String | Number |
false | |
episode | String | Number |
false | |
episodeNumber | episodeNum, episodeNumbers | Object |
false |
previouslyShown | String | Object | String[] | Object[] |
false | |
premiere | String | Object | String[] | Object[] |
false | |
lastChance | String | Object | String[] | Object[] |
false | |
new | Boolean |
false | |
subtitles | Object | Object[] |
false | |
rating | ratings | String | Object | String[] | Object[] |
false |
starRating | starRatings | String | Object | String[] | Object[] |
false |
review | reviews | String | Object | String[] | Object[] |
false |
director | directors | String | Object | String[] | Object[] |
false |
actor | actors | String | Object | String[] | Object[] |
false |
writer | writers | String | Object | String[] | Object[] |
false |
adapter | adapters | String | Object | String[] | Object[] |
false |
producer | producers | String | Object | String[] | Object[] |
false |
presenter | presenters | String | Object | String[] | Object[] |
false |
composer | composers | String | Object | String[] | Object[] |
false |
editor | editors | String | Object | String[] | Object[] |
false |
commentator | commentators | String | Object | String[] | Object[] |
false |
guest | guests | String | Object | String[] | Object[] |
false |
image | images | String | Object | String[] | Object[] |
false |
icon | icons | String | Object | String[] | Object[] |
false |
Example:
{
start: '2021-03-19T06:00:00.000Z',
stop: '2021-03-19T06:30:00.000Z',
title: 'Program 1',
subTitle: 'Sub-title & 1',
description: 'Description for Program 1',
date: '2022-05-06',
categories: ['Comedy', 'Drama'],
keywords: [
{ lang: 'en', value: 'physical-comedy' },
{ lang: 'en', value: 'romantic' }
],
language: 'English',
origLanguage: { lang: 'en', value: 'French' },
length: { units: 'minutes', value: '60' },
url: 'http://example.com/title.html',
country: 'US',
video: {
present: 'yes',
colour: 'no',
aspect: '16:9',
quality: 'HDTV'
},
audio: {
present: 'yes',
stereo: 'Dolby Digital'
},
season: 9,
episode: 239,
previouslyShown: [{ start: '20080711000000', channel: 'channel-two.tv' }],
premiere: 'First time on British TV',
lastChance: [{ lang: 'en', value: 'Last time on this channel' }],
new: true,
subtitles: [
{ type: 'teletext', language: 'English' },
{ type: 'onscreen', language: [{ lang: 'en', value: 'Spanish' }] }
],
rating: {
system: 'MPAA',
value: 'P&G',
icon: 'http://example.com/pg_symbol.png'
},
starRatings: [
{
system: 'TV Guide',
value: '4/5',
icon: [{ src: 'stars.png', width: 100, height: 100 }]
},
{
system: 'IMDB',
value: '8/10'
}
],
reviews: [
{
type: 'text',
source: 'Rotten Tomatoes',
reviewer: 'Joe Bloggs',
lang: 'en',
value: 'This is a fantastic show!'
},
{
type: 'text',
source: 'IDMB',
reviewer: 'Jane Doe',
lang: 'en',
value: 'I love this show!'
},
{
type: 'url',
source: 'Rotten Tomatoes',
reviewer: 'Joe Bloggs',
lang: 'en',
value: 'https://example.com/programme_one_review'
}
],
directors: [
{
value: 'Director 1',
url: { value: 'http://example.com/director1.html', system: 'TestSystem' },
image: [
'https://example.com/image1.jpg',
{
value: 'https://example.com/image2.jpg',
type: 'person',
size: '2',
system: 'TestSystem',
orient: 'P'
}
]
},
'Director 2'
],
actors: ['Actor 1', 'Actor 2'],
writer: 'Writer 1',
producers: 'Roger Dobkowitz',
presenters: 'Drew Carey',
images: [
{
type: 'poster',
size: '1',
orient: 'P',
system: 'tvdb',
value: 'https://tvdb.com/programme_one_poster_1.jpg'
},
{
type: 'poster',
size: '2',
orient: 'P',
system: 'tmdb',
value: 'https://tmdb.com/programme_one_poster_2.jpg'
},
{
type: 'backdrop',
size: '3',
orient: 'L',
system: 'tvdb',
value: 'https://tvdb.com/programme_one_backdrop_3.jpg'
}
],
icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777'
}
example.com.test.js
With this file we can test the previously created config and make sure it works as you expect.
const { parser, url } = require('./example.com.config.js')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const customParseFormat = require('dayjs/plugin/customParseFormat')
dayjs.extend(customParseFormat)
dayjs.extend(utc)
const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d')
const channel = { site_id: 'bbc1', xmltv_id: 'BBCOne.uk' }
it('can generate valid url', () => {
expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2025-01-12')
})
it('can parse response', () => {
const content =
'[{"title":"Program 1","start":"2025-01-12T00:00:00.000Z","stop":"2025-01-12T00:30:00.000Z"},{"title":"Program 2","start":"2025-01-12T00:30:00.000Z","stop":"2025-01-12T01:00:00.000Z"}]'
const results = parser({ content })
expect(results.length).toBe(2)
expect(results[0]).toMatchObject({
title: 'Program 1',
start: '2025-01-12T00:00:00.000Z',
stop: '2025-01-12T00:30:00.000Z'
})
expect(results[1]).toMatchObject({
title: 'Program 2',
start: '2025-01-12T00:30:00.000Z',
stop: '2025-01-12T01:00:00.000Z'
})
})
it('can handle empty guide', () => {
const result = parser({
date,
channel,
content: ''
})
expect(result).toMatchObject([])
})
Detailed documentation for the tests can be found here: https://jestjs.io/docs/using-matchers
example.com.channels.xml
This file contains a list of channels available at the source.
<?xml version="1.0" encoding="UTF-8" ?>
<channels>
<channel site="example.com" lang="en" xmltv_id="BBCOne.uk" site_id="bbc1">BBC One</channel>
</channels>
readme.md
This file contains instructions on how to use this config.
# example.com
https://example.com
### Download the guide
```sh
npm run grab --- --site=example.com
```
### Test
```sh
npm test --- example.com
```
The fastest way to create all these files is to use the command:
npm run sites:init --- example.com
After you finish working on the files you can make sure that everything works by running the config test:
npm test --- example.com
Then check that all channels have the correct xmltv-id
:
npm run channels:validate sites/example.com/example.com.channels.xml
And then try downloading the guide itself:
npm run grab --- example.com
If everything goes well just commit all changes and send us a pull request.
In order for the guides to be linked with playlists from iptv-org/iptv and also with our other projects, each channel must have the same ID in the description as in our iptv-org/database.
To check this, select one of the sites in the SITES.md, open its *.channels.xml
file and check that all channels have a valid xmltv_id
. A list of all channels with corresponding IDs can be found at iptv-org.github.io.
If the channel is not in our database yet, you can add it to it through this form.
If the *.channels.xml
file contains many channels without xmltv_id
, you can speed up the process by running the command in the Console (or Terminal if you have macOS):
npm run channels:edit path/to/channels.xml
This way, you can map channels by simply selecting the proper ID from the list:
? Select channel ID for "BBC One" (bbc1): (Use arrow keys)
❯ BBCOne.uk (BBC One, BBC1, BBC Television, BBC Television Service)
BBCOneHD.uk (BBC One HD)
Type...
Skip
Once complete, commit all changes and send a pull request.
.github/
ISSUE_TEMPLATE/
: issue templates for the repository.workflows
: contains GitHub actions workflows.CODE_OF_CONDUCT.md
: rules you shouldn't break if you don't want to get banned.
scripts/
: contains all scripts used in the repository.sites/
: contains configurations, channel lists and tests for all sites.tests/
: contains tests to check the scripts.CONTRIBUTING.md
: file you are currently reading.README.md
: project description displayed on the home page.SITES.md
: list of all supported sites and their current status.
These scripts are created to automate routine processes in the repository and make it a bit easier to maintain.
For scripts to work, you must have Node.js installed on your computer.
To run scripts use the npm run <script-name>
command.
act:check
: allows to test the check workflow locally. Depends on nektos/act.act:update
: allows to test the update workflow locally. Depends on nektos/act.api:load
: downloads the latest channels data from the iptv-org/api.api:generate
: generates a JSON file with all channels for the iptv-org/api repository.channels:lint
: сhecks the channel lists for syntax errors.channels:parse
: generates a list of channels based on the site configuration.channels:edit
: utility for quick channels mapping.channels:validate
: checks the description of channels for errors.sites:init
: creates a new site config from the template.sites:update
: updates the list of sites and their status in SITES.md.grab
: downloads a program from a specified source.serve
: starts the web server.lint
: сhecks the scripts for syntax errors.test
: runs a test of all the scripts described above.