Skip to content

Latest commit

 

History

History
437 lines (360 loc) · 18 KB

CONTRIBUTING.md

File metadata and controls

437 lines (360 loc) · 18 KB

Contributing Guide

How to?

How to add a channel to the guide?

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.

How to report broken guide?

If you start to get errors when downloading the guide or if nothing loads at all, please let us know via this form.

How to add a new source to the repository?

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 []
    }
  }
}

Context Object

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 date
  • content: The response data as a String
  • buffer: The response data as an ArrayBuffer
  • headers: The response headers
  • request: The request config
  • cached: A boolean to check whether this request was cached or not

Program Properties

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.

How to map the channels?

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.

Project Structure

  • .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.

Scripts

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.