mirror of
https://github.com/iptv-org/epg.git
synced 2025-05-09 08:30:06 -04:00
commit
c191199dd4
642 changed files with 36943 additions and 33981 deletions
11
.eslintrc.js
11
.eslintrc.js
|
@ -1,11 +0,0 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: false,
|
||||
node: true,
|
||||
es6: true
|
||||
},
|
||||
extends: 'eslint:recommended',
|
||||
parserOptions: {
|
||||
ecmaVersion: 12
|
||||
}
|
||||
}
|
39
.eslintrc.json
Normal file
39
.eslintrc.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es2021": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"no-case-declarations": "off",
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true
|
||||
}
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
]
|
||||
}
|
||||
}
|
13
.github/CODE_OF_CONDUCT.md
vendored
Normal file
13
.github/CODE_OF_CONDUCT.md
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
||||
|
||||
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at https://www.contributor-covenant.org/version/1/0/0/code-of-conduct.html
|
33
.github/ISSUE_TEMPLATE/-----epg-request.yml
vendored
33
.github/ISSUE_TEMPLATE/-----epg-request.yml
vendored
|
@ -1,33 +0,0 @@
|
|||
name: 🗓 EPG Request
|
||||
description: Request to add a channel to existing guide
|
||||
labels: ['🗓 epg request']
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please fill out the issue template as much as you can so we could efficiently process your request.
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Guide
|
||||
description: Link to the file in which you want to add these channels
|
||||
placeholder: 'https://iptv-org.github.io/epg/guides/en/example.co.uk.xml'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Channels
|
||||
description: List all channels that need to be added
|
||||
placeholder: |
|
||||
- Channel1
|
||||
- Channel2
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Notes
|
||||
description: Anything else we should know?
|
|
@ -1,6 +1,6 @@
|
|||
name: ➕ Source Request
|
||||
name: 📝 Source Request
|
||||
description: Request to add a new source
|
||||
labels: ['➕ source request']
|
||||
labels: ['source request']
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
|
|
2
.github/ISSUE_TEMPLATE/---broken-guide.yml
vendored
2
.github/ISSUE_TEMPLATE/---broken-guide.yml
vendored
|
@ -11,7 +11,7 @@ body:
|
|||
- type: input
|
||||
attributes:
|
||||
label: Site
|
||||
description: The name of the site listed in the [sites](https://github.com/iptv-org/epg/tree/master/sites) folder
|
||||
description: The name of the site
|
||||
placeholder: 'guidatv.sky.it'
|
||||
validations:
|
||||
required: true
|
||||
|
|
15
.github/ISSUE_TEMPLATE/--feature-request.yml
vendored
15
.github/ISSUE_TEMPLATE/--feature-request.yml
vendored
|
@ -1,15 +0,0 @@
|
|||
name: 💡 Feature Request
|
||||
description: For any ideas or feature requests
|
||||
labels: ['💡 feature request']
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please describe your idea in as much detail as possible so that we can efficiently process your request.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
validations:
|
||||
required: true
|
10
.github/ISSUE_TEMPLATE/--question.yml
vendored
10
.github/ISSUE_TEMPLATE/--question.yml
vendored
|
@ -1,10 +0,0 @@
|
|||
name: ❓ Ask a Question
|
||||
description: Any questions about this repository
|
||||
labels: ['❓ question']
|
||||
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
validations:
|
||||
required: true
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: 💡 Feature request
|
||||
url: https://github.com/orgs/iptv-org/discussions/categories/ideas
|
||||
about: For any ideas or feature requests
|
||||
- name: ❓ Ask a question
|
||||
url: https://github.com/orgs/iptv-org/discussions/categories/q-a
|
||||
about: Ask questions about this project
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,6 +1,5 @@
|
|||
/node_modules/
|
||||
/logs/
|
||||
/temp/
|
||||
/channels.xml
|
||||
/guide.xml
|
||||
/guide.xml.gz
|
||||
/.jenkins/
|
||||
/guides/
|
||||
/guide.xml.gz
|
|
@ -1,3 +0,0 @@
|
|||
*
|
||||
!*.js
|
||||
!*/
|
|
@ -6,7 +6,5 @@ module.exports = {
|
|||
singleQuote: true,
|
||||
printWidth: 100,
|
||||
trailingComma: 'none',
|
||||
bracketSpacing: true,
|
||||
singleAttributePerLine: false,
|
||||
arrowParens: 'avoid'
|
||||
}
|
||||
|
|
103
CONTRIBUTING.md
103
CONTRIBUTING.md
|
@ -1,21 +1,28 @@
|
|||
# Contributing Guide
|
||||
|
||||
### How do I add a program guide for the channel?
|
||||
- [How to?](#how-to)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Scripts](#scripts)
|
||||
|
||||
First, open the [/sites](/sites) folder and select the source that you know has the guide for the channel you want.
|
||||
## How to?
|
||||
|
||||
### How to add a channel to the guide?
|
||||
|
||||
Open the [/sites](/sites) folder and select the source that you know has the guide for the channel you want.
|
||||
|
||||
Then in the selected folder open the file `*.channels.xml` and add to it:
|
||||
|
||||
```xml
|
||||
<channel lang="LANGUAGE_CODE" xmltv_id="CHANNEL_ID" site_id="SITE_ID">CHANNEL_NAME</channel>
|
||||
<channel site="SITE" lang="LANGUAGE_CODE" xmltv_id="CHANNEL_ID" site_id="SITE_ID">CHANNEL_NAME</channel>
|
||||
```
|
||||
|
||||
| Attribute | Description | Example |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |
|
||||
| LANGUAGE_CODE | Language of the guide ([ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) code). | `en` |
|
||||
| CHANNEL_ID | Channel ID from [iptv-org/database](https://github.com/iptv-org/database). A complete list of supported channels can also be found at https://iptv-org.github.io/. | `BBCOne.uk` |
|
||||
| SITE_ID | Unique ID of the channel used in the source. | `bbc1` |
|
||||
| CHANNEL_NAME | Name of the channel used in the source. | `BBC 1` |
|
||||
| Attribute | Description | Example |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- |
|
||||
| SITE | Site domain name. | `example.com` |
|
||||
| LANGUAGE_CODE | Language of the guide ([ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) code). | `en` |
|
||||
| CHANNEL_ID | Channel ID from [iptv-org/database](https://github.com/iptv-org/database). A complete list of supported channels can also be found at https://iptv-org.github.io/. | `BBCOne.uk` |
|
||||
| SITE_ID | Unique ID of the channel used in the source. | `bbc1` |
|
||||
| CHANNEL_NAME | Name of the channel used in the source. | `BBC 1` |
|
||||
|
||||
After that just commit all changes and send a pull request.
|
||||
|
||||
|
@ -31,13 +38,13 @@ This file describes what kind of request we need to send to get the guide for a
|
|||
|
||||
```js
|
||||
module.exports = {
|
||||
site: 'example.com',
|
||||
url: function ({ channel, date }) {
|
||||
return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}`
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
return JSON.parse(content)
|
||||
}
|
||||
site: 'example.com',
|
||||
url: function ({ channel, date }) {
|
||||
return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}`
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
return JSON.parse(content)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -61,26 +68,26 @@ const date = dayjs.utc('2022-11-18', 'YYYY-MM-DD').startOf('d')
|
|||
const channel = { site_id: 'bbc1', xmltv_id: 'BBCOne.uk', lang: 'en' }
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2022-11-18')
|
||||
expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2022-11-18')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `[{"start":"2022-11-18T01:30:00.000Z","stop":"2022-11-18T02:00:00.000Z","title":"Program 1"}]`
|
||||
const results = parser({ content })
|
||||
const content = `[{"start":"2022-11-18T01:30:00.000Z","stop":"2022-11-18T02:00:00.000Z","title":"Program 1"}]`
|
||||
const results = parser({ content })
|
||||
|
||||
expect(results).toMatchObject([
|
||||
{
|
||||
start: '2022-11-18T01:30:00.000Z',
|
||||
stop: '2022-11-18T02:00:00.000Z',
|
||||
title: 'Program 1'
|
||||
}
|
||||
])
|
||||
expect(results).toMatchObject([
|
||||
{
|
||||
start: '2022-11-18T01:30:00.000Z',
|
||||
stop: '2022-11-18T02:00:00.000Z',
|
||||
title: 'Program 1'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const results = parser({ content: '' })
|
||||
const results = parser({ content: '' })
|
||||
|
||||
expect(results).toMatchObject([])
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
```
|
||||
|
||||
|
@ -102,11 +109,9 @@ This file contains a list of channels available at the source.
|
|||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<site site="example.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="BBCOne.uk" site_id="bbc1">BBC 1</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="example.com" lang="en" xmltv_id="BBCOne.uk" site_id="bbc1">BBC 1</channel>
|
||||
</channels>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
@ -114,9 +119,39 @@ This file contains a list of channels available at the source.
|
|||
After creating all the files we can make sure that the guide loads correctly and has no errors using the command:
|
||||
|
||||
```sh
|
||||
npx epg-grabber --config=sites/example.com/example.com.config.js --channels=sites/example.com/example.com.channels.xml --output=guide.xml --days=2
|
||||
npm run grab -- --site=example.com
|
||||
```
|
||||
|
||||
If the download is successful, the `guide.xml` file with the ready to use program should appear in the root directory.
|
||||
|
||||
After that, all that remains is to commit all the changes and send a pull request.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `.github/`
|
||||
- `ISSUE_TEMPLATE/`: issue templates for the repository.
|
||||
- `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](https://nodejs.org/en) installed on your computer.
|
||||
|
||||
To run scripts use the `npm run <script-name>` command.
|
||||
|
||||
- `api:load`: downloads the latest channels data from the [iptv-org/api](https://github.com/iptv-org/api).
|
||||
- `channels:lint`: сhecks the channel lists for syntax errors.
|
||||
- `channels:parse`: generates a list of channels based on the site configuration.
|
||||
- `channels:editor`: utility for quick channels markup.
|
||||
- `channels:validate`: checks the description of channels for errors.
|
||||
- `grab`: downloads a program from a specified source.
|
||||
- `serve`: starts the [web server](https://github.com/vercel/serve).
|
||||
- `lint`: сhecks the scripts for syntax errors.
|
||||
- `test`: runs a test of all the scripts described above.
|
||||
|
|
108
README.md
108
README.md
|
@ -1,99 +1,137 @@
|
|||
# EPG
|
||||
|
||||
Utilities for downloading the EPG (Electronic Program Guide) for thousands of TV channels from hundreds of sources.
|
||||
|
||||
__IMPORTANT:__ We are no longer able to provide pre-made guides due to the disabling of GitHub Actions (Read more: https://github.com/orgs/iptv-org/discussions/12#discussioncomment-5219050). This repository now contains only utilities and configurations for downloading guides yourself.
|
||||
Tools for downloading the EPG (Electronic Program Guide) for thousands of TV channels from hundreds of sources.
|
||||
|
||||
## Table of contents
|
||||
|
||||
- 🚀 [How to use?](#how-to-use)
|
||||
- ✨ [Installation](#installation)
|
||||
- 🚀 [Usage](#usage)
|
||||
- 💫 [Update](#update)
|
||||
- 📺 [Playlists](#playlists)
|
||||
- 🗄 [Database](#database)
|
||||
- 👨💻 [API](#api)
|
||||
- 📚 [Resources](#resources)
|
||||
- 💬 [Discussions](#discussions)
|
||||
- 🛠 [Contribution](#contribution)
|
||||
- © [License](#license)
|
||||
- 📄 [License](#license)
|
||||
|
||||
## How to use?
|
||||
## Installation
|
||||
|
||||
To download the guide from one of the supported sites you must have [Node.js](https://nodejs.org/en) installed on your computer first.
|
||||
First, you need to install [Node.js](https://nodejs.org/en) on your computer. You will also need to install [Git](https://git-scm.com/downloads) to follow these instructions.
|
||||
|
||||
You will also need to install [Git](https://git-scm.com/downloads) to follow these instructions.
|
||||
|
||||
After installing them, you need to open the [Console](https://en.wikipedia.org/wiki/Windows_Console) (or [Terminal](https://en.wikipedia.org/wiki/Terminal_(macOS)) if you have macOS) and type the following command:
|
||||
After that open the [Console](https://en.wikipedia.org/wiki/Windows_Console) (or [Terminal](<https://en.wikipedia.org/wiki/Terminal_(macOS)>) if you have macOS) and type the following command:
|
||||
|
||||
```sh
|
||||
git clone --depth 1 -b master https://github.com/iptv-org/epg.git
|
||||
```
|
||||
|
||||
Then also through the Console navigate to the just downloaded `epg` folder:
|
||||
Then navigate to the downloaded `epg` folder:
|
||||
|
||||
```sh
|
||||
cd epg
|
||||
```
|
||||
|
||||
And install all the necessary dependencies:
|
||||
And install all the dependencies:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
Now choose one of the sources (their complete list can be found in the [/sites](https://github.com/iptv-org/epg/tree/master/sites) folder) and start downloading the guide using the command:
|
||||
## Usage
|
||||
|
||||
To start the download of the guide, select one of the [supported sites](SITES.md) and paste its name into the command below:
|
||||
|
||||
```sh
|
||||
npm run grab -- --site=example.com
|
||||
```
|
||||
|
||||
To download a guide in a specific language pass its [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) code to the `--lang` argument:
|
||||
And once the download is complete, the guide will be saved to the `guide.xml` file.
|
||||
|
||||
```sh
|
||||
npm run grab -- --site=example.com --lang=fr
|
||||
Usage: npm run grab -- [options]
|
||||
|
||||
Options:
|
||||
-s, --site <name> Name of the site to parse
|
||||
-c, --channels <path> Path to *.channels.xml file (required if the "--site" attribute is
|
||||
not specified)
|
||||
-o, --output <path> Path to output file (default: "guide.xml")
|
||||
-l, --lang <code> Filter channels by language (ISO 639-2 code)
|
||||
-t, --timeout <milliseconds> Override the default timeout for each request
|
||||
--days <days> Override the number of days for which the program will be loaded
|
||||
(defaults to the value from the site config)
|
||||
--maxConnections <number> Limit on the number of concurrent requests (default: 1)
|
||||
--cron <expression> Schedule a script run (example: "0 0 * * *")
|
||||
--gzip Create a compressed version of the guide as well (default: false)
|
||||
```
|
||||
|
||||
To override the number of days for which the program will be loaded use the `--days` argument (the default is the value specified in the site config):
|
||||
### Access the guide by URL
|
||||
|
||||
You can make the guide available via URL by running your own server:
|
||||
|
||||
```sh
|
||||
npm run grab -- --site=example.com --days=3
|
||||
npm run serve
|
||||
```
|
||||
|
||||
To also create a compressed version of the guide, add the `--gzip` flag:
|
||||
After that, the guide will be available at the link:
|
||||
|
||||
```
|
||||
http://localhost:3000/guide.xml
|
||||
```
|
||||
|
||||
In addition it will be available to other devices on the same local network at the address:
|
||||
|
||||
```
|
||||
http://<your_local_ip_address>:3000/guide.xml
|
||||
```
|
||||
|
||||
### Parallel downloading
|
||||
|
||||
By default, the guide for each channel is downloaded one by one, but you can change this behavior by increasing the number of simultaneous requests using the `--maxConnections` attribute:
|
||||
|
||||
```sh
|
||||
npm run grab -- --site=example.com --gzip
|
||||
npm run grab -- --site=example.com --maxConnections=10
|
||||
```
|
||||
|
||||
After the download is completed in the current directory will appear a new folder `guides`, which will store all XML files:
|
||||
But be aware that under heavy load, some sites may start return an error or completely block your access.
|
||||
|
||||
### Use custom channel list
|
||||
|
||||
Create an XML file and copy the descriptions of all the channels you need from the [/sites](sites) into it:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channels>
|
||||
<channel site="arirang.com" lang="en" xmltv_id="ArirangTV.kr" site_id="CH_K">Arirang TV</channel>
|
||||
...
|
||||
</channels>
|
||||
```
|
||||
|
||||
And then specify the path to that file via the `--channels` attribute:
|
||||
|
||||
```sh
|
||||
guides
|
||||
└── fr
|
||||
└── example.com.xml
|
||||
└── example.com.xml.gz
|
||||
npm run grab -- --channels=path/to/custom.channels.xml
|
||||
```
|
||||
|
||||
### Run on schedule
|
||||
|
||||
If you want to download the guide automatically on a schedule, you need to pass a valid [cron expression](https://crontab.guru/) to the script using the `--cron` attribute:
|
||||
|
||||
```sh
|
||||
npm run grab -- --site=example.com --cron="0 0 * * *"
|
||||
```
|
||||
|
||||
Also you can make these guides available via URL by running your own server:
|
||||
## Update
|
||||
|
||||
If you have downloaded the repository code according to the instructions above, then to update it will be enough to run the command:
|
||||
|
||||
```sh
|
||||
npm run serve
|
||||
git pull
|
||||
```
|
||||
|
||||
After that all the downloaded guides will be available at a link like this:
|
||||
And then update all the dependencies:
|
||||
|
||||
```
|
||||
http://localhost:3000/guides/fr/example.com.xml
|
||||
```
|
||||
|
||||
In addition, they will be available on your local network at:
|
||||
|
||||
```
|
||||
http://<your_local_ip_address>:3000/guides/fr/example.com.xml
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
## Playlists
|
||||
|
|
188
SITES.md
Normal file
188
SITES.md
Normal file
|
@ -0,0 +1,188 @@
|
|||
# Sites
|
||||
|
||||
| Site | Status | Notes |
|
||||
| -------------------------- | ------ | ---------------------------------------------------------------------------------------- |
|
||||
| 9tv.co.il | 🟢 | |
|
||||
| abc.net.au | 🟢 | |
|
||||
| allente.se | 🟢 | |
|
||||
| andorradifusio.ad | 🟢 | |
|
||||
| arianaafgtv.com | 🟢 | |
|
||||
| arianatelevision.com | 🟢 | |
|
||||
| arirang.com | 🟢 | |
|
||||
| artonline.tv | 🟢 | |
|
||||
| astro.com.my | 🟢 | |
|
||||
| bein.com | 🟢 | |
|
||||
| beinsports.com | 🟡 | https://github.com/iptv-org/epg/issues/2160, https://github.com/iptv-org/epg/issues/2168 |
|
||||
| berrymedia.co.kr | 🟢 | |
|
||||
| bt.com | 🟢 | |
|
||||
| cablego.com.pe | 🟢 | |
|
||||
| cableplus.com.uy | 🟢 | |
|
||||
| canalplus-caraibes.com | 🟢 | |
|
||||
| canalplus-haiti.com | 🟢 | |
|
||||
| canalplus-reunion.com | 🟢 | |
|
||||
| canalplus.com | 🟢 | |
|
||||
| cgates.lt | 🟢 | |
|
||||
| chaines-tv.orange.fr | 🟢 | |
|
||||
| clickthecity.com | 🟢 | |
|
||||
| compulms.com | 🟢 | |
|
||||
| comteco.com.bo | 🟢 | |
|
||||
| cosmote.gr | 🟢 | |
|
||||
| delta.nl | 🟢 | |
|
||||
| digiturk.com.tr | 🟢 | |
|
||||
| directv.com | 🟢 | |
|
||||
| directv.com.ar | 🟢 | |
|
||||
| directv.com.uy | 🟢 | |
|
||||
| dishtv.in | 🟢 | |
|
||||
| dsmart.com.tr | 🟢 | |
|
||||
| dstv.com | 🟢 | |
|
||||
| elcinema.com | 🟢 | |
|
||||
| ena.skylifetv.co.kr | 🟢 | |
|
||||
| entertainment.ie | 🟢 | |
|
||||
| epg.i-cable.com | 🟢 | |
|
||||
| firstmedia.com | 🟢 | |
|
||||
| flixed.io | 🟢 | |
|
||||
| foxsports.com.au | 🟢 | |
|
||||
| foxtel.com.au | 🟢 | |
|
||||
| frikanalen.no | 🟢 | |
|
||||
| gatotv.com | 🟢 | |
|
||||
| getafteritmedia.com | 🟢 | |
|
||||
| guidatv.sky.it | 🟢 | |
|
||||
| guide.dstv.com | 🟢 | |
|
||||
| hd-plus.de | 🟡 | https://github.com/iptv-org/epg/issues/2173 |
|
||||
| horizon.tv | 🟢 | |
|
||||
| i.mjh.nz | 🟢 | |
|
||||
| i24news.tv | 🟢 | |
|
||||
| indihometv.com | 🟢 | |
|
||||
| ionplustv.com | 🟢 | |
|
||||
| ipko.com | 🟢 | |
|
||||
| kan.org.il | 🟢 | |
|
||||
| knr.gl | 🟢 | |
|
||||
| kplus.vn | 🟢 | |
|
||||
| kvf.fo | 🟢 | |
|
||||
| m.tv.sms.cz | 🟢 | |
|
||||
| magentatv.at | 🟡 | https://github.com/iptv-org/epg/issues/2133#issuecomment-1640598226 |
|
||||
| magentatv.de | 🟡 | https://github.com/iptv-org/epg/issues/2133 |
|
||||
| magticom.ge | 🟢 | |
|
||||
| mako.co.il | 🟢 | |
|
||||
| maxtv.hrvatskitelekom.hr | 🟢 | |
|
||||
| maxtvgo.mk | 🟢 | |
|
||||
| mbc.net | 🟢 | |
|
||||
| mediagenie.co.kr | 🟢 | |
|
||||
| mediaklikk.hu | 🟢 | |
|
||||
| mediaset.it | 🟢 | |
|
||||
| melita.com | 🟢 | |
|
||||
| meo.pt | 🟢 | |
|
||||
| mewatch.sg | 🟢 | |
|
||||
| mi.tv | 🟢 | |
|
||||
| mncvision.id | 🟢 | |
|
||||
| moji.id | 🟢 | |
|
||||
| mon-programme-tv.be | 🟢 | |
|
||||
| movistarplus.es | 🟢 | |
|
||||
| mtel.ba | 🟢 | |
|
||||
| mts.rs | 🟢 | |
|
||||
| mujtvprogram.cz | 🟢 | |
|
||||
| musor.tv | 🟢 | |
|
||||
| myafn.dodmedia.osd.mil | 🟢 | |
|
||||
| mysky.com.ph | 🟢 | |
|
||||
| mytvsuper.com | 🟢 | |
|
||||
| nhk.or.jp | 🟢 | |
|
||||
| nhkworldpremium.com | 🟢 | |
|
||||
| nos.pt | 🟢 | |
|
||||
| novacyprus.com | 🟢 | |
|
||||
| novasports.gr | 🟢 | |
|
||||
| nowplayer.now.com | 🟢 | |
|
||||
| nuevosiglo.com.uy | 🟢 | |
|
||||
| ontvtonight.com | 🟢 | |
|
||||
| osn.com | 🟡 | https://github.com/iptv-org/epg/issues/2157 |
|
||||
| pbsguam.org | 🟢 | |
|
||||
| plex.tv | 🟢 | |
|
||||
| programacion-tv.elpais.com | 🟢 | |
|
||||
| programacion.tcc.com.uy | 🟢 | |
|
||||
| programetv.ro | 🟢 | |
|
||||
| programme-tv.net | 🟢 | |
|
||||
| programme-tv.vini.pf | 🟢 | |
|
||||
| programme.tvb.com | 🟢 | |
|
||||
| programtv.onet.pl | 🟢 | |
|
||||
| proximusmwc.be | 🟢 | |
|
||||
| raiplay.it | 🟢 | |
|
||||
| reportv.com.ar | 🟢 | |
|
||||
| rev.bs | 🟢 | |
|
||||
| rotana.net | 🟢 | |
|
||||
| rtb.gov.bn | 🟢 | |
|
||||
| rthk.hk | 🟢 | |
|
||||
| rtmklik.rtm.gov.my | 🟢 | |
|
||||
| rtp.pt | 🟢 | |
|
||||
| ruv.is | 🟢 | |
|
||||
| sat.tv | 🟢 | |
|
||||
| siba.com.co | 🟢 | |
|
||||
| singtel.com | 🟢 | |
|
||||
| sjonvarp.is | 🟢 | |
|
||||
| sky.co.nz | 🟢 | |
|
||||
| sky.com | 🟢 | |
|
||||
| sky.de | 🟢 | |
|
||||
| sportsnet.ca | 🟢 | |
|
||||
| starhubtvplus.com | 🟢 | |
|
||||
| startimestv.com | 🟢 | |
|
||||
| startv.com | 🟢 | |
|
||||
| streamingtvguides.com | 🟢 | |
|
||||
| superguidatv.it | 🟢 | |
|
||||
| taiwanplus.com | 🟢 | |
|
||||
| tapdmv.com | 🟢 | |
|
||||
| telecablesat.fr | 🟢 | |
|
||||
| telenet.tv | 🟢 | |
|
||||
| teliatv.ee | 🟢 | |
|
||||
| telkku.com | 🟢 | |
|
||||
| telkussa.fi | 🟢 | |
|
||||
| telsu.fi | 🟢 | |
|
||||
| tivu.tv | 🟢 | |
|
||||
| toonamiaftermath.com | 🟢 | |
|
||||
| transvision.co.id | 🟢 | |
|
||||
| turksatkablo.com.tr | 🟢 | |
|
||||
| tv.blue.ch | 🟢 | |
|
||||
| tv.cctv.com | 🟢 | |
|
||||
| tv.dir.bg | 🟢 | |
|
||||
| tv.lv | 🟢 | |
|
||||
| tv.mail.ru | 🟢 | |
|
||||
| tv.movistar.com.pe | 🟢 | |
|
||||
| tv.nu | 🟢 | |
|
||||
| tv.post.lu | 🟢 | |
|
||||
| tv.trueid.net | 🟡 | https://github.com/iptv-org/epg/issues/2164 |
|
||||
| tv.vera.com.uy | 🟢 | |
|
||||
| tv.yandex.ru | 🟡 | https://github.com/iptv-org/epg/issues/2170 |
|
||||
| tv.yettel.hu | 🟢 | |
|
||||
| tv2go.t-2.net | 🟢 | |
|
||||
| tv24.co.uk | 🟢 | |
|
||||
| tv24.se | 🟢 | |
|
||||
| tva.tv | 🟢 | |
|
||||
| tvarenasport.com | 🟢 | |
|
||||
| tvarenasport.hr | 🟢 | |
|
||||
| tvcubana.icrt.cu | 🟢 | |
|
||||
| tvgids.nl | 🟢 | |
|
||||
| tvguide.com | 🟢 | |
|
||||
| tvguide.myjcom.jp | 🟢 | |
|
||||
| tvhebdo.com | 🟢 | |
|
||||
| tvheute.at | 🟢 | |
|
||||
| tvim.tv | 🟢 | |
|
||||
| tving.com | 🟢 | |
|
||||
| tvmi.mt | 🟢 | |
|
||||
| tvmusor.hu | 🟢 | |
|
||||
| tvpassport.com | 🟡 | https://github.com/iptv-org/epg/issues/2175 |
|
||||
| tvplus.com.tr | 🟢 | |
|
||||
| tvprofil.com | 🟢 | |
|
||||
| tvtv.us | 🟡 | https://github.com/iptv-org/epg/issues/2176 |
|
||||
| unifi.com.my | 🟢 | |
|
||||
| vidio.com | 🟢 | |
|
||||
| virginmedia.com | 🟢 | |
|
||||
| virginmediatelevision.ie | 🟢 | |
|
||||
| visionplus.id | 🟢 | |
|
||||
| vivacom.bg | 🟢 | |
|
||||
| vtm.be | 🟢 | |
|
||||
| walesi.com.fj | 🟢 | |
|
||||
| watchyour.tv | 🟢 | |
|
||||
| wavve.com | 🟢 | |
|
||||
| worldfishingnetwork.com | 🟢 | |
|
||||
| xumo.tv | 🟢 | |
|
||||
| zap.co.ao | 🟢 | |
|
||||
| ziggogo.tv | 🟢 | |
|
||||
| znbc.co.zm | 🟢 | |
|
||||
| zuragt.mn | 🟢 | |
|
4137
package-lock.json
generated
4137
package-lock.json
generated
File diff suppressed because it is too large
Load diff
61
package.json
61
package.json
|
@ -1,51 +1,59 @@
|
|||
{
|
||||
"name": "epg",
|
||||
"scripts": {
|
||||
"channels:validate": "node scripts/commands/channels/validate.js",
|
||||
"channels:lint": "node scripts/commands/channels/lint.js",
|
||||
"channels:parse": "node scripts/commands/channels/parse.js",
|
||||
"channels:editor": "node scripts/commands/channels/editor.js",
|
||||
"api:load": "./scripts/commands/api/load.sh",
|
||||
"lint": "npx eslint ./scripts/**/*.js",
|
||||
"test": "TZ=Pacific/Nauru npx jest --runInBand",
|
||||
"test:commands": "npx jest --runInBand -- commands",
|
||||
"test:sites": "TZ=Pacific/Nauru npx jest --runInBand -- sites",
|
||||
"check": "npm run api:load && npm run channels:lint sites/**/*.js && npm run channels:validate sites/**/*.xml",
|
||||
"grab": "node scripts/commands/epg/grab.js",
|
||||
"api:load": "ts-node scripts/commands/api/load.ts",
|
||||
"channels:lint": "ts-node scripts/commands/channels/lint.ts",
|
||||
"channels:parse": "ts-node scripts/commands/channels/parse.ts",
|
||||
"channels:editor": "ts-node scripts/commands/channels/editor.ts",
|
||||
"channels:validate": "ts-node scripts/commands/channels/validate.ts",
|
||||
"grab": "ts-node scripts/commands/epg/grab.ts",
|
||||
"serve": "npx serve",
|
||||
"lint": "npx eslint ./scripts/**/*.ts ./sites/**/*.js ./tests/**/*.ts ./tests/**/*.js",
|
||||
"test": "TZ=Pacific/Nauru npx jest --runInBand",
|
||||
"postinstall": "npm run api:load"
|
||||
},
|
||||
"private": true,
|
||||
"author": "Arhey",
|
||||
"license": "MIT",
|
||||
"jest": {
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/.jenkins/"
|
||||
],
|
||||
"testRegex": "(sites|tests)/(.*?/)?.*test.js$",
|
||||
"setupFilesAfterEnv": [
|
||||
"@alex_neo/jest-expect-message"
|
||||
]
|
||||
"transform": {
|
||||
"^.+\\.(ts|js)$": "ts-jest"
|
||||
},
|
||||
"testRegex": "(tests|sites)/(.*?/)?.*test.(js|ts)$"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||
"@freearhey/core": "^0.2.1",
|
||||
"@octokit/core": "^4.1.0",
|
||||
"axios": "^0.21.1",
|
||||
"@types/cli-progress": "^3.11.3",
|
||||
"@types/fs-extra": "^11.0.2",
|
||||
"@types/inquirer": "^9.0.3",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/lodash": "^4.14.199",
|
||||
"@types/node-cleanup": "^2.1.2",
|
||||
"@types/numeral": "^2.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||
"axios": "^1.5.1",
|
||||
"axios-cookiejar-support": "^4.0.7",
|
||||
"chalk": "^4.1.2",
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"cli-progress": "^3.12.0",
|
||||
"commander": "^8.2.0",
|
||||
"consola": "^3.2.3",
|
||||
"cron": "^2.3.1",
|
||||
"cron": "^2.4.3",
|
||||
"csv-parser": "^3.0.0",
|
||||
"cwait": "^1.1.2",
|
||||
"dayjs": "^1.11.7",
|
||||
"epg-grabber": "^0.32.0",
|
||||
"epg-grabber": "^0.34.0",
|
||||
"epg-parser": "^0.2.0",
|
||||
"eslint": "^8.17.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
"glob": "^7.2.0",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"inquirer": "^8.2.0",
|
||||
"jest": "^29.5.0",
|
||||
"inquirer": "^8.2.6",
|
||||
"jest": "^29.7.0",
|
||||
"langs": "^2.0.0",
|
||||
"libxmljs2": "^0.32.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
@ -55,6 +63,7 @@
|
|||
"nedb-promises": "^6.0.3",
|
||||
"node-cleanup": "^2.1.2",
|
||||
"node-gzip": "^1.1.2",
|
||||
"numeral": "^2.0.6",
|
||||
"parse-duration": "^1.0.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"serve": "^14.2.0",
|
||||
|
@ -62,11 +71,11 @@
|
|||
"srcset": "^4.0.0",
|
||||
"table2array": "^0.0.2",
|
||||
"tabletojson": "^2.0.7",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"transliteration": "^2.2.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"unzipit": "^1.4.0",
|
||||
"wildcard-match": "^5.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.17.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
mkdir -p scripts/tmp/data
|
||||
curl -L -o scripts/tmp/data/channels.json https://iptv-org.github.io/api/channels.json
|
||||
curl -L -o scripts/tmp/data/countries.json https://iptv-org.github.io/api/countries.json
|
||||
curl -L -o scripts/tmp/data/regions.json https://iptv-org.github.io/api/regions.json
|
||||
curl -L -o scripts/tmp/data/subdivisions.json https://iptv-org.github.io/api/subdivisions.json
|
18
scripts/commands/api/load.ts
Normal file
18
scripts/commands/api/load.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Logger } from '@freearhey/core'
|
||||
import { ApiClient } from '../../core'
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger()
|
||||
const client = new ApiClient({ logger })
|
||||
|
||||
const requests = [
|
||||
client.download('channels.json'),
|
||||
client.download('countries.json'),
|
||||
client.download('regions.json'),
|
||||
client.download('subdivisions.json')
|
||||
]
|
||||
|
||||
await Promise.all(requests)
|
||||
}
|
||||
|
||||
main()
|
1
scripts/commands/channels/.gitignore
vendored
Normal file
1
scripts/commands/channels/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/replace.ts
|
|
@ -1,160 +0,0 @@
|
|||
const { api, parser, xml, file, logger } = require('../../core')
|
||||
const { transliterate } = require('transliteration')
|
||||
const nodeCleanup = require('node-cleanup')
|
||||
const { program } = require('commander')
|
||||
const inquirer = require('inquirer')
|
||||
|
||||
program
|
||||
.argument('<filepath>', 'Path to *.channels.xml file to edit')
|
||||
.option('-c, --country <name>', 'Source country', 'us')
|
||||
.parse(process.argv)
|
||||
|
||||
const filepath = program.args[0]
|
||||
const options = program.opts()
|
||||
const defaultCountry = options.country
|
||||
const newLabel = ` [new]`
|
||||
|
||||
let site
|
||||
let channels = []
|
||||
|
||||
async function main() {
|
||||
if (!(await file.exists(filepath))) {
|
||||
throw new Error(`File "${filepath}" does not exists`)
|
||||
return
|
||||
}
|
||||
|
||||
let result = await parser.parseChannels(filepath)
|
||||
site = result.site
|
||||
channels = result.channels
|
||||
channels = channels.map(c => {
|
||||
c.xmltv_id = c.xmltv_id
|
||||
return c
|
||||
})
|
||||
await api.channels.load()
|
||||
const buffer = []
|
||||
for (const channel of channels) {
|
||||
if (channel.xmltv_id) {
|
||||
if (channel.xmltv_id !== '-') {
|
||||
buffer.push(`${channel.xmltv_id}/${channel.lang}`)
|
||||
}
|
||||
continue
|
||||
}
|
||||
let choices = await getOptions(channel)
|
||||
const question = {
|
||||
name: 'option',
|
||||
message: `Choose an option:`,
|
||||
type: 'list',
|
||||
choices,
|
||||
pageSize: 10
|
||||
}
|
||||
await inquirer.prompt(question).then(async selected => {
|
||||
switch (selected.option) {
|
||||
case 'Overwrite':
|
||||
const input = await getInput(channel)
|
||||
channel.xmltv_id = input.xmltv_id
|
||||
break
|
||||
case 'Skip':
|
||||
channel.xmltv_id = '-'
|
||||
break
|
||||
default:
|
||||
const [name, xmltv_id] = selected.option
|
||||
.replace(/ \[.*\]/, '')
|
||||
.split('|')
|
||||
.map(i => i.trim().replace(newLabel, ''))
|
||||
channel.xmltv_id = xmltv_id
|
||||
break
|
||||
}
|
||||
|
||||
const found = buffer.includes(`${channel.xmltv_id}/${channel.lang}`)
|
||||
if (found) {
|
||||
const question = {
|
||||
name: 'option',
|
||||
message: `"${channel.xmltv_id}" already on the list. Choose an option:`,
|
||||
type: 'list',
|
||||
choices: ['Skip', 'Add', 'Delete'],
|
||||
pageSize: 5
|
||||
}
|
||||
await inquirer.prompt(question).then(async selected => {
|
||||
switch (selected.option) {
|
||||
case 'Skip':
|
||||
channel.xmltv_id = '-'
|
||||
break
|
||||
case 'Delete':
|
||||
channel.delete = true
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (channel.xmltv_id !== '-') {
|
||||
buffer.push(`${channel.xmltv_id}/${channel.lang}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
function save() {
|
||||
if (!file.existsSync(filepath)) return
|
||||
|
||||
channels = channels.filter(c => !c.delete)
|
||||
|
||||
const output = xml.create(channels, site)
|
||||
|
||||
file.writeSync(filepath, output)
|
||||
|
||||
logger.info(`\nFile '${filepath}' successfully saved`)
|
||||
}
|
||||
|
||||
nodeCleanup(() => {
|
||||
save()
|
||||
})
|
||||
|
||||
async function getInput(channel) {
|
||||
const name = channel.name.trim()
|
||||
const input = await inquirer.prompt([
|
||||
{
|
||||
name: 'xmltv_id',
|
||||
message: ' ID:',
|
||||
type: 'input',
|
||||
default: generateCode(name, defaultCountry)
|
||||
}
|
||||
])
|
||||
|
||||
return { name, xmltv_id: input['xmltv_id'] }
|
||||
}
|
||||
|
||||
async function getOptions(channel) {
|
||||
const channels = await api.channels.all()
|
||||
const channelId = generateCode(channel.name, defaultCountry)
|
||||
const similar = await getSimilar(channels, channelId)
|
||||
let variants = []
|
||||
variants.push(`${channel.name.trim()} | ${channelId}${newLabel}`)
|
||||
similar.forEach(i => {
|
||||
let alt_names = i.alt_names.length ? ` (${i.alt_names.join(',')})` : ''
|
||||
let closed = i.closed ? `[closed:${i.closed}]` : ``
|
||||
let replaced_by = i.replaced_by ? `[replaced_by:${i.replaced_by}]` : ''
|
||||
variants.push(`${i.name}${alt_names} | ${i.id} ${closed}${replaced_by}[api]`)
|
||||
})
|
||||
variants.push(`Overwrite`)
|
||||
variants.push(`Skip`)
|
||||
|
||||
return variants
|
||||
}
|
||||
|
||||
async function getSimilar(list, channelId) {
|
||||
const normChannelId = channelId.split('.')[0].slice(0, 8).toLowerCase()
|
||||
return list.filter(i => i.id.split('.')[0].toLowerCase().startsWith(normChannelId))
|
||||
}
|
||||
|
||||
function generateCode(name, country) {
|
||||
const id = transliterate(name)
|
||||
.replace(/\+/gi, 'Plus')
|
||||
.replace(/^\&/gi, 'And')
|
||||
.replace(/[^a-z\d]+/gi, '')
|
||||
|
||||
return `${id}.${country}`
|
||||
}
|
181
scripts/commands/channels/editor.ts
Normal file
181
scripts/commands/channels/editor.ts
Normal file
|
@ -0,0 +1,181 @@
|
|||
import { DATA_DIR } from '../../constants'
|
||||
import { Storage, Collection, Dictionary, Logger } from '@freearhey/core'
|
||||
import { ChannelsParser, XML, ApiChannel } from '../../core'
|
||||
import { Channel } from 'epg-grabber'
|
||||
import { transliterate } from 'transliteration'
|
||||
import nodeCleanup from 'node-cleanup'
|
||||
import { program } from 'commander'
|
||||
import inquirer, { QuestionCollection } from 'inquirer'
|
||||
|
||||
program
|
||||
.argument('<filepath>', 'Path to *.channels.xml file to edit')
|
||||
.option('-c, --country <name>', 'Default country (ISO 3166 code)', 'US')
|
||||
.parse(process.argv)
|
||||
|
||||
const filepath = program.args[0]
|
||||
const programOptions = program.opts()
|
||||
const defaultCountry = programOptions.country.toLowerCase()
|
||||
const newLabel = ` [new]`
|
||||
|
||||
let site: string
|
||||
let options = new Collection()
|
||||
|
||||
async function main() {
|
||||
const storage = new Storage()
|
||||
|
||||
if (!(await storage.exists(filepath))) {
|
||||
throw new Error(`File "${filepath}" does not exists`)
|
||||
}
|
||||
|
||||
const parser = new ChannelsParser({ storage })
|
||||
|
||||
const parsedChannels = await parser.parse(filepath)
|
||||
options = parsedChannels.map((channel: Channel) => {
|
||||
return {
|
||||
channel,
|
||||
delete: false
|
||||
}
|
||||
})
|
||||
|
||||
const dataStorage = new Storage(DATA_DIR)
|
||||
const channelsContent = await dataStorage.json('channels.json')
|
||||
const channels = new Collection(channelsContent).map(data => new ApiChannel(data))
|
||||
|
||||
const buffer = new Dictionary()
|
||||
options.forEach(async (option: { channel: Channel; delete: boolean }) => {
|
||||
const channel = option.channel
|
||||
if (channel.xmltv_id) {
|
||||
if (channel.xmltv_id !== '-') {
|
||||
buffer.set(`${channel.xmltv_id}/${channel.lang}`, true)
|
||||
}
|
||||
return
|
||||
}
|
||||
let choices = getOptions(channels, channel)
|
||||
const question: QuestionCollection = {
|
||||
name: 'option',
|
||||
message: `Choose an option:`,
|
||||
type: 'list',
|
||||
choices,
|
||||
pageSize: 10
|
||||
}
|
||||
|
||||
await inquirer.prompt(question).then(async selected => {
|
||||
switch (selected.option) {
|
||||
case 'Overwrite':
|
||||
const input = await getInput(channel)
|
||||
channel.xmltv_id = input.xmltv_id
|
||||
break
|
||||
case 'Skip':
|
||||
channel.xmltv_id = '-'
|
||||
break
|
||||
default:
|
||||
const [, xmltv_id] = selected.option
|
||||
.replace(/ \[.*\]/, '')
|
||||
.split('|')
|
||||
.map((i: string) => i.trim().replace(newLabel, ''))
|
||||
channel.xmltv_id = xmltv_id
|
||||
break
|
||||
}
|
||||
|
||||
const found = buffer.has(`${channel.xmltv_id}/${channel.lang}`)
|
||||
if (found) {
|
||||
const question: QuestionCollection = {
|
||||
name: 'option',
|
||||
message: `"${channel.xmltv_id}" already on the list. Choose an option:`,
|
||||
type: 'list',
|
||||
choices: ['Skip', 'Add', 'Delete'],
|
||||
pageSize: 5
|
||||
}
|
||||
await inquirer.prompt(question).then(async selected => {
|
||||
switch (selected.option) {
|
||||
case 'Skip':
|
||||
channel.xmltv_id = '-'
|
||||
break
|
||||
case 'Delete':
|
||||
option.delete = true
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (channel.xmltv_id !== '-') {
|
||||
buffer.set(`${channel.xmltv_id}/${channel.lang}`, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
function save() {
|
||||
const logger = new Logger()
|
||||
const storage = new Storage()
|
||||
|
||||
if (!storage.existsSync(filepath)) return
|
||||
|
||||
const channels = options
|
||||
.filter((option: { channel: Channel; delete: boolean }) => !option.delete)
|
||||
.map((option: { channel: Channel; delete: boolean }) => option.channel)
|
||||
|
||||
const xml = new XML(channels, site)
|
||||
|
||||
storage.saveSync(filepath, xml.toString())
|
||||
|
||||
logger.info(`\nFile '${filepath}' successfully saved`)
|
||||
}
|
||||
|
||||
nodeCleanup(() => {
|
||||
save()
|
||||
})
|
||||
|
||||
async function getInput(channel: Channel) {
|
||||
const name = channel.name.trim()
|
||||
const input = await inquirer.prompt([
|
||||
{
|
||||
name: 'xmltv_id',
|
||||
message: ' ID:',
|
||||
type: 'input',
|
||||
default: generateCode(name, defaultCountry)
|
||||
}
|
||||
])
|
||||
|
||||
return { name, xmltv_id: input['xmltv_id'] }
|
||||
}
|
||||
|
||||
function getOptions(channels: Collection, channel: Channel) {
|
||||
const channelId = generateCode(channel.name, defaultCountry)
|
||||
const similar = getSimilar(channels, channelId)
|
||||
|
||||
const variants = new Collection()
|
||||
variants.add(`${channel.name.trim()} | ${channelId}${newLabel}`)
|
||||
similar.forEach((_channel: ApiChannel) => {
|
||||
const altNames = _channel.altNames.notEmpty() ? ` (${_channel.altNames.join(',')})` : ''
|
||||
const closed = _channel.closed ? `[closed:${_channel.closed}]` : ``
|
||||
const replacedBy = _channel.replacedBy ? `[replaced_by:${_channel.replacedBy}]` : ''
|
||||
|
||||
variants.add(`${_channel.name}${altNames} | ${_channel.id} ${closed}${replacedBy}[api]`)
|
||||
})
|
||||
variants.add(`Overwrite`)
|
||||
variants.add(`Skip`)
|
||||
|
||||
return variants.all()
|
||||
}
|
||||
|
||||
function getSimilar(channels: Collection, channelId: string) {
|
||||
const normChannelId = channelId.split('.')[0].slice(0, 8).toLowerCase()
|
||||
|
||||
return channels.filter((channel: ApiChannel) =>
|
||||
channel.id.split('.')[0].toLowerCase().startsWith(normChannelId)
|
||||
)
|
||||
}
|
||||
|
||||
function generateCode(name: string, country: string) {
|
||||
const channelId: string = transliterate(name)
|
||||
.replace(/\+/gi, 'Plus')
|
||||
.replace(/^\&/gi, 'And')
|
||||
.replace(/[^a-z\d]+/gi, '')
|
||||
|
||||
return `${channelId}.${country}`
|
||||
}
|
|
@ -1,18 +1,10 @@
|
|||
const chalk = require('chalk')
|
||||
const libxml = require('libxmljs2')
|
||||
const { program } = require('commander')
|
||||
const { logger, file } = require('../../core')
|
||||
import chalk from 'chalk'
|
||||
import libxml, { ValidationError } from 'libxmljs2'
|
||||
import { program } from 'commander'
|
||||
import { Logger, Storage, File } from '@freearhey/core'
|
||||
|
||||
const xsd = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
||||
<xs:element name="site">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element ref="channels"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="site" use="required" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="channels">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
|
@ -22,43 +14,53 @@ const xsd = `<?xml version="1.0" encoding="UTF-8"?>
|
|||
</xs:element>
|
||||
<xs:element name="channel">
|
||||
<xs:complexType mixed="true">
|
||||
<xs:attribute name="site" use="required" type="xs:string"/>
|
||||
<xs:attribute name="lang" use="required" type="xs:string"/>
|
||||
<xs:attribute name="site_id" use="required" type="xs:string"/>
|
||||
<xs:attribute name="xmltv_id" use="required" type="xs:string"/>
|
||||
<xs:attribute name="logo" type="xs:string"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>`
|
||||
|
||||
program.argument('<filepath>', 'Path to file to validate').parse(process.argv)
|
||||
program
|
||||
.option(
|
||||
'-c, --channels <path>',
|
||||
'Path to channels.xml file to validate',
|
||||
'sites/**/*.channels.xml'
|
||||
)
|
||||
.parse(process.argv)
|
||||
|
||||
const options = program.opts()
|
||||
|
||||
async function main() {
|
||||
if (!program.args.length) {
|
||||
logger.error('required argument "filepath" not specified')
|
||||
}
|
||||
const logger = new Logger()
|
||||
const storage = new Storage()
|
||||
|
||||
let errors = []
|
||||
logger.info('options:')
|
||||
logger.tree(options)
|
||||
|
||||
for (const filepath of program.args) {
|
||||
if (!filepath.endsWith('.xml')) continue
|
||||
let errors: ValidationError[] = []
|
||||
|
||||
const xml = await file.read(filepath)
|
||||
let files: string[] = await storage.list(options.channels)
|
||||
for (const filepath of files) {
|
||||
const file = new File(filepath)
|
||||
if (file.extension() !== 'xml') continue
|
||||
|
||||
let localErrors = []
|
||||
const xml = await storage.load(filepath)
|
||||
|
||||
try {
|
||||
const xsdDoc = libxml.parseXml(xsd)
|
||||
const doc = libxml.parseXml(xml)
|
||||
let localErrors: ValidationError[] = []
|
||||
|
||||
if (!doc.validate(xsdDoc)) {
|
||||
localErrors = doc.validationErrors
|
||||
}
|
||||
} catch (error) {
|
||||
localErrors.push(error)
|
||||
const xsdDoc = libxml.parseXml(xsd)
|
||||
const doc = libxml.parseXml(xml)
|
||||
|
||||
if (!doc.validate(xsdDoc)) {
|
||||
localErrors = doc.validationErrors
|
||||
}
|
||||
|
||||
if (localErrors.length) {
|
||||
console.log(`\n${chalk.underline(filepath)}`)
|
||||
localErrors.forEach(error => {
|
||||
localErrors.forEach((error: ValidationError) => {
|
||||
const position = `${error.line}:${error.column}`
|
||||
console.log(` ${chalk.gray(position.padEnd(4, ' '))} ${error.message.trim()}`)
|
||||
})
|
|
@ -1,65 +0,0 @@
|
|||
const { logger, file, xml, parser } = require('../../core')
|
||||
const { Command } = require('commander')
|
||||
const path = require('path')
|
||||
const _ = require('lodash')
|
||||
|
||||
const program = new Command()
|
||||
program
|
||||
.requiredOption('-c, --config <config>', 'Config file')
|
||||
.option('-s, --set [args...]', 'Set custom arguments', [])
|
||||
.option('-o, --output <output>', 'Output file')
|
||||
.option('--clean', 'Delete the previous *.channels.xml if exists')
|
||||
.parse(process.argv)
|
||||
|
||||
const options = program.opts()
|
||||
|
||||
async function main() {
|
||||
const config = require(path.resolve(options.config))
|
||||
const dir = file.dirname(options.config)
|
||||
const outputFilepath = options.output || `${dir}/${config.site}.channels.xml`
|
||||
|
||||
let channels = []
|
||||
if (!options.clean && (await file.exists(outputFilepath))) {
|
||||
let result = await parser.parseChannels(outputFilepath)
|
||||
|
||||
channels = result.channels
|
||||
}
|
||||
|
||||
const args = {}
|
||||
options.set.forEach(arg => {
|
||||
const [key, value] = arg.split(':')
|
||||
args[key] = value
|
||||
})
|
||||
|
||||
let parsedChannels = config.channels(args)
|
||||
if (isPromise(parsedChannels)) {
|
||||
parsedChannels = await parsedChannels
|
||||
}
|
||||
parsedChannels = parsedChannels.map(c => {
|
||||
c.lang = c.lang || 'en'
|
||||
|
||||
return c
|
||||
})
|
||||
|
||||
channels = channels.concat(parsedChannels)
|
||||
|
||||
channels = _.uniqBy(channels, c => c.site_id + c.lang)
|
||||
|
||||
channels = _.sortBy(channels, [
|
||||
'lang',
|
||||
c => (c.xmltv_id ? c.xmltv_id.toLowerCase() : '_'),
|
||||
'site_id'
|
||||
])
|
||||
|
||||
const output = xml.create(channels, config.site)
|
||||
|
||||
await file.write(outputFilepath, output)
|
||||
|
||||
logger.info(`File '${outputFilepath}' successfully saved`)
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
function isPromise(promise) {
|
||||
return !!promise && typeof promise.then === 'function'
|
||||
}
|
76
scripts/commands/channels/parse.ts
Normal file
76
scripts/commands/channels/parse.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { Logger, File, Collection, Storage } from '@freearhey/core'
|
||||
import { ChannelsParser, XML } from '../../core'
|
||||
import { Channel } from 'epg-grabber'
|
||||
import { Command, OptionValues } from 'commander'
|
||||
import path from 'path'
|
||||
|
||||
const program = new Command()
|
||||
program
|
||||
.requiredOption('-c, --config <config>', 'Config file')
|
||||
.option('-s, --set [args...]', 'Set custom arguments')
|
||||
.option('-o, --output <output>', 'Output file')
|
||||
.option('--clean', 'Delete the previous *.channels.xml if exists')
|
||||
.parse(process.argv)
|
||||
|
||||
type ParseOptions = {
|
||||
config: string
|
||||
set?: string
|
||||
output?: string
|
||||
clean?: boolean
|
||||
}
|
||||
|
||||
const options: ParseOptions = program.opts()
|
||||
|
||||
async function main() {
|
||||
const storage = new Storage()
|
||||
const parser = new ChannelsParser({ storage })
|
||||
const logger = new Logger()
|
||||
const file = new File(options.config)
|
||||
const dir = file.dirname()
|
||||
const config = require(path.resolve(options.config))
|
||||
const outputFilepath = options.output || `${dir}/${config.site}.channels.xml`
|
||||
|
||||
let channels = new Collection()
|
||||
if (!options.clean && (await storage.exists(outputFilepath))) {
|
||||
channels = await parser.parse(outputFilepath)
|
||||
}
|
||||
|
||||
const args: {
|
||||
[key: string]: any
|
||||
} = {}
|
||||
|
||||
if (Array.isArray(options.set)) {
|
||||
options.set.forEach((arg: string) => {
|
||||
const [key, value] = arg.split(':')
|
||||
args[key] = value
|
||||
})
|
||||
}
|
||||
|
||||
let parsedChannels = config.channels(args)
|
||||
if (isPromise(parsedChannels)) {
|
||||
parsedChannels = await parsedChannels
|
||||
}
|
||||
|
||||
channels = channels
|
||||
.mergeBy(
|
||||
new Collection(parsedChannels),
|
||||
(channel: Channel) => channel.site_id.toString() + channel.lang
|
||||
)
|
||||
.orderBy([
|
||||
(channel: Channel) => channel.lang,
|
||||
(channel: Channel) => (channel.xmltv_id ? channel.xmltv_id.toLowerCase() : '_'),
|
||||
(channel: Channel) => channel.site_id
|
||||
])
|
||||
|
||||
const xml = new XML(channels, config.site)
|
||||
|
||||
await storage.save(outputFilepath, xml.toString())
|
||||
|
||||
logger.info(`File '${outputFilepath}' successfully saved`)
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
function isPromise(promise: any) {
|
||||
return !!promise && typeof promise.then === 'function'
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
const { parser, logger, api } = require('../../core')
|
||||
const { program } = require('commander')
|
||||
const chalk = require('chalk')
|
||||
const langs = require('langs')
|
||||
|
||||
program.argument('<filepath>', 'Path to file to validate').parse(process.argv)
|
||||
|
||||
async function main() {
|
||||
await api.channels.load()
|
||||
|
||||
const stats = {
|
||||
files: 0,
|
||||
errors: 0
|
||||
}
|
||||
|
||||
if (!program.args.length) {
|
||||
logger.error('required argument "filepath" not specified')
|
||||
}
|
||||
|
||||
for (const filepath of program.args) {
|
||||
if (!filepath.endsWith('.xml')) continue
|
||||
|
||||
const { site, channels } = await parser.parseChannels(filepath)
|
||||
|
||||
const bufferById = {}
|
||||
const bufferBySiteId = {}
|
||||
const errors = []
|
||||
for (const channel of channels) {
|
||||
if (!bufferById[channel.xmltv_id + channel.lang]) {
|
||||
bufferById[channel.xmltv_id + channel.lang] = channel
|
||||
} else {
|
||||
errors.push({ type: 'duplicate', ...channel })
|
||||
stats.errors++
|
||||
}
|
||||
|
||||
if (!bufferBySiteId[channel.site_id + channel.lang]) {
|
||||
bufferBySiteId[channel.site_id + channel.lang] = channel
|
||||
} else {
|
||||
errors.push({ type: 'duplicate', ...channel })
|
||||
stats.errors++
|
||||
}
|
||||
|
||||
if (!api.channels.find({ id: channel.xmltv_id })) {
|
||||
errors.push({ type: 'wrong_xmltv_id', ...channel })
|
||||
stats.errors++
|
||||
}
|
||||
|
||||
if (!langs.where('1', channel.lang)) {
|
||||
errors.push({ type: 'wrong_lang', ...channel })
|
||||
stats.errors++
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length) {
|
||||
console.log(chalk.underline(filepath))
|
||||
console.table(errors, ['type', 'lang', 'xmltv_id', 'site_id', 'name'])
|
||||
console.log()
|
||||
stats.files++
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.errors > 0) {
|
||||
console.log(chalk.red(`${stats.errors} error(s) in ${stats.files} file(s)`))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
95
scripts/commands/channels/validate.ts
Normal file
95
scripts/commands/channels/validate.ts
Normal file
|
@ -0,0 +1,95 @@
|
|||
import { Storage, Collection, Dictionary, File, Logger } from '@freearhey/core'
|
||||
import { ChannelsParser, ApiChannel } from '../../core'
|
||||
import { program } from 'commander'
|
||||
import chalk from 'chalk'
|
||||
import langs from 'langs'
|
||||
import { DATA_DIR } from '../../constants'
|
||||
import { Channel } from 'epg-grabber'
|
||||
|
||||
program
|
||||
.option(
|
||||
'-c, --channels <path>',
|
||||
'Path to channels.xml file to validate',
|
||||
'sites/**/*.channels.xml'
|
||||
)
|
||||
.parse(process.argv)
|
||||
|
||||
const options = program.opts()
|
||||
|
||||
type ValidationError = {
|
||||
type: 'duplicate' | 'wrong_xmltv_id' | 'wrong_lang'
|
||||
name: string
|
||||
lang?: string
|
||||
xmltv_id?: string
|
||||
site_id?: string
|
||||
logo?: string
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger()
|
||||
|
||||
logger.info('options:')
|
||||
logger.tree(options)
|
||||
|
||||
const parser = new ChannelsParser({ storage: new Storage() })
|
||||
|
||||
const dataStorage = new Storage(DATA_DIR)
|
||||
const channelsContent = await dataStorage.json('channels.json')
|
||||
const channels = new Collection(channelsContent).map(data => new ApiChannel(data))
|
||||
|
||||
let totalFiles = 0
|
||||
let totalErrors = 0
|
||||
const storage = new Storage()
|
||||
let files: string[] = await storage.list(options.channels)
|
||||
for (const filepath of files) {
|
||||
const file = new File(filepath)
|
||||
if (file.extension() !== 'xml') continue
|
||||
|
||||
const parsedChannels = await parser.parse(filepath)
|
||||
|
||||
const bufferById = new Dictionary()
|
||||
const bufferBySiteId = new Dictionary()
|
||||
const errors: ValidationError[] = []
|
||||
parsedChannels.forEach((channel: Channel) => {
|
||||
const bufferId: string = `${channel.xmltv_id}:${channel.lang}`
|
||||
if (bufferById.missing(bufferId)) {
|
||||
bufferById.set(bufferId, true)
|
||||
} else {
|
||||
errors.push({ type: 'duplicate', ...channel })
|
||||
totalErrors++
|
||||
}
|
||||
|
||||
const bufferSiteId: string = `${channel.site_id}:${channel.lang}`
|
||||
if (bufferBySiteId.missing(bufferSiteId)) {
|
||||
bufferBySiteId.set(bufferSiteId, true)
|
||||
} else {
|
||||
errors.push({ type: 'duplicate', ...channel })
|
||||
totalErrors++
|
||||
}
|
||||
|
||||
if (channels.missing((_channel: ApiChannel) => _channel.id === channel.xmltv_id)) {
|
||||
errors.push({ type: 'wrong_xmltv_id', ...channel })
|
||||
totalErrors++
|
||||
}
|
||||
|
||||
if (!langs.where('1', channel.lang)) {
|
||||
errors.push({ type: 'wrong_lang', ...channel })
|
||||
totalErrors++
|
||||
}
|
||||
})
|
||||
|
||||
if (errors.length) {
|
||||
console.log(chalk.underline(filepath))
|
||||
console.table(errors, ['type', 'lang', 'xmltv_id', 'site_id', 'name'])
|
||||
console.log()
|
||||
totalFiles++
|
||||
}
|
||||
}
|
||||
|
||||
if (totalErrors > 0) {
|
||||
console.log(chalk.red(`${totalErrors} error(s) in ${totalFiles} file(s)`))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
|
@ -1,220 +0,0 @@
|
|||
const { program } = require('commander')
|
||||
const _ = require('lodash')
|
||||
const { EPGGrabber, generateXMLTV, Channel, Program } = require('epg-grabber')
|
||||
const { db, logger, date, timer, file, parser, api, zip } = require('../../core')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const CronJob = require('cron').CronJob
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const BASE_DIR = process.env.BASE_DIR || '.'
|
||||
const CURR_DATE = process.env.CURR_DATE || new Date()
|
||||
|
||||
program
|
||||
.requiredOption('-s, --site <name>', 'Name of the site to parse')
|
||||
.option('-l, --lang <code>', 'Filter channels by language (ISO 639-2 code)')
|
||||
.option('-o, --output <path>', 'Path to output file')
|
||||
.option('--days <days>', 'Override the number of days for which the program will be loaded')
|
||||
.option('--cron <expression>', 'Schedule a script run')
|
||||
.option('--gzip', 'Create a compressed version of the guide as well', false)
|
||||
.parse(process.argv)
|
||||
|
||||
const options = program.opts()
|
||||
|
||||
options.output = options.output || file.resolve(`${BASE_DIR}/guides/{lang}/{site}.xml`)
|
||||
options.config = file.resolve(`${BASE_DIR}/sites/${options.site}/${options.site}.config.js`)
|
||||
options.channels = file.resolve(`${BASE_DIR}/sites/${options.site}/${options.site}*.channels.xml`)
|
||||
|
||||
let channels = []
|
||||
let programs = []
|
||||
let runIndex = 0
|
||||
|
||||
async function main() {
|
||||
logger.start('staring...')
|
||||
|
||||
logger.info('settings:')
|
||||
for (let prop in options) {
|
||||
logger.info(` ${prop}: ${options[prop]}`)
|
||||
}
|
||||
|
||||
const config = await loadConfig(options.config)
|
||||
const queue = await createQueue(options.channels, config)
|
||||
const outputPath = options.output
|
||||
|
||||
if (options.cron) {
|
||||
const job = new CronJob(options.cron, function () {
|
||||
runJob(config, queue, outputPath)
|
||||
})
|
||||
job.start()
|
||||
} else {
|
||||
await runJob(config, queue, outputPath)
|
||||
}
|
||||
}
|
||||
|
||||
async function loadConfig(configPath) {
|
||||
let config = require(file.resolve(configPath))
|
||||
config = _.merge(config, {})
|
||||
config.days = config.days || 1
|
||||
|
||||
logger.info('config:')
|
||||
logConfig(config)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
function logConfig(config, level = 1) {
|
||||
let padLeft = ' '.repeat(level)
|
||||
for (let prop in config) {
|
||||
if (typeof config[prop] === 'string' || typeof config[prop] === 'number') {
|
||||
logger.info(`${padLeft}${prop}: ${config[prop]}`)
|
||||
} else if (typeof config[prop] === 'object') {
|
||||
level++
|
||||
logger.info(`${padLeft}${prop}:`)
|
||||
logConfig(config[prop], level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function runJob(config, queue, outputPath) {
|
||||
runIndex++
|
||||
logger.info(`run #${runIndex}:`)
|
||||
|
||||
timer.start()
|
||||
|
||||
await grab(queue, config)
|
||||
|
||||
await save(outputPath, channels, programs)
|
||||
|
||||
logger.success(` done in ${timer.format('HH[h] mm[m] ss[s]')}`)
|
||||
}
|
||||
|
||||
async function grab(queue, config) {
|
||||
const grabber = new EPGGrabber(config)
|
||||
const total = queue.length
|
||||
|
||||
let i = 1
|
||||
for (const item of queue) {
|
||||
let channel = item.channel
|
||||
let date = item.date
|
||||
channels.push(item.channel)
|
||||
await grabber
|
||||
.grab(channel, date, (data, err) => {
|
||||
logger.info(
|
||||
` [${i}/${total}] ${channel.site} (${channel.lang}) - ${channel.xmltv_id} - ${dayjs
|
||||
.utc(data.date)
|
||||
.format('MMM D, YYYY')} (${data.programs.length} programs)`
|
||||
)
|
||||
if (i < total) i++
|
||||
|
||||
if (err) {
|
||||
logger.info(` ERR: ${err.message}`)
|
||||
}
|
||||
})
|
||||
.then(results => {
|
||||
programs = programs.concat(results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function createQueue(channelsPath, config) {
|
||||
logger.info('creating queue...')
|
||||
let queue = {}
|
||||
await api.channels.load().catch(logger.error)
|
||||
const files = await file.list(channelsPath).catch(logger.error)
|
||||
const utcDate = date.getUTC(CURR_DATE)
|
||||
const days = options.days ? parseInt(options.days) : config.days
|
||||
for (const filepath of files) {
|
||||
logger.info(` loading "${filepath}"...`)
|
||||
try {
|
||||
const dir = file.dirname(filepath)
|
||||
const { channels } = await parser.parseChannels(filepath)
|
||||
const filename = file.basename(filepath)
|
||||
const dates = Array.from({ length: days }, (_, i) => utcDate.add(i, 'd'))
|
||||
for (const channel of channels) {
|
||||
if (!channel.site || !channel.xmltv_id) continue
|
||||
if (options.lang && channel.lang !== options.lang) continue
|
||||
const found = api.channels.find({ id: channel.xmltv_id })
|
||||
if (found) {
|
||||
channel.logo = found.logo
|
||||
}
|
||||
for (const d of dates) {
|
||||
const dateString = d.toJSON()
|
||||
const key = `${channel.site}:${channel.lang}:${channel.xmltv_id}:${dateString}`
|
||||
if (!queue[key]) {
|
||||
queue[key] = {
|
||||
channel,
|
||||
date: dateString,
|
||||
config,
|
||||
error: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
queue = Object.values(queue)
|
||||
|
||||
logger.info(` added ${queue.length} items`)
|
||||
|
||||
return queue
|
||||
}
|
||||
|
||||
async function save(template, parsedChannels, programs = []) {
|
||||
const variables = file.templateVariables(template)
|
||||
|
||||
const groups = _.groupBy(parsedChannels, channel => {
|
||||
let groupId = ''
|
||||
for (let key in channel) {
|
||||
if (variables.includes(key)) {
|
||||
groupId += channel[key]
|
||||
}
|
||||
}
|
||||
|
||||
return groupId
|
||||
})
|
||||
|
||||
for (let groupId in groups) {
|
||||
const channels = groups[groupId]
|
||||
|
||||
let output = {
|
||||
channels,
|
||||
programs: [],
|
||||
date: CURR_DATE
|
||||
}
|
||||
|
||||
for (let program of programs) {
|
||||
let programLang = program.titles[0].lang
|
||||
let channel = channels.find(c => c.xmltv_id === program.channel && c.lang === programLang)
|
||||
if (!channel) continue
|
||||
|
||||
output.programs.push(new Program(program, channel))
|
||||
}
|
||||
|
||||
output.channels = _.sortBy(output.channels, 'xmltv_id')
|
||||
output.channels = _.uniqBy(output.channels, 'xmltv_id')
|
||||
|
||||
output.programs = _.sortBy(output.programs, ['channel', 'start'])
|
||||
output.programs = _.uniqBy(output.programs, p => p.channel + p.start)
|
||||
|
||||
const outputPath = file.templateFormat(template, output.channels[0])
|
||||
const xmlFilepath = outputPath
|
||||
const xmltv = generateXMLTV(output)
|
||||
logger.info(` saving to "${xmlFilepath}"...`)
|
||||
await file.create(xmlFilepath, xmltv)
|
||||
|
||||
if (options.gzip) {
|
||||
const gzFilepath = `${outputPath}.gz`
|
||||
const compressed = await zip.compress(xmltv)
|
||||
logger.info(` saving to "${gzFilepath}"...`)
|
||||
await file.create(gzFilepath, compressed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
115
scripts/commands/epg/grab.ts
Normal file
115
scripts/commands/epg/grab.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
import { Logger, Timer, Storage, Collection } from '@freearhey/core'
|
||||
import { program } from 'commander'
|
||||
import { CronJob } from 'cron'
|
||||
import { Queue, Job, ChannelsParser } from '../../core'
|
||||
import { Channel } from 'epg-grabber'
|
||||
import path from 'path'
|
||||
import { SITES_DIR } from '../../constants'
|
||||
|
||||
program
|
||||
.option('-s, --site <name>', 'Name of the site to parse')
|
||||
.option(
|
||||
'-c, --channels <path>',
|
||||
'Path to *.channels.xml file (required if the "--site" attribute is not specified)'
|
||||
)
|
||||
.option('-o, --output <path>', 'Path to output file', 'guide.xml')
|
||||
.option('-l, --lang <code>', 'Filter channels by language (ISO 639-2 code)')
|
||||
.option('-t, --timeout <milliseconds>', 'Override the default timeout for each request')
|
||||
.option(
|
||||
'--days <days>',
|
||||
'Override the number of days for which the program will be loaded (defaults to the value from the site config)',
|
||||
value => parseInt(value)
|
||||
)
|
||||
.option(
|
||||
'--maxConnections <number>',
|
||||
'Limit on the number of concurrent requests',
|
||||
value => parseInt(value),
|
||||
1
|
||||
)
|
||||
.option('--cron <expression>', 'Schedule a script run (example: "0 0 * * *")')
|
||||
.option('--gzip', 'Create a compressed version of the guide as well', false)
|
||||
.parse(process.argv)
|
||||
|
||||
export type GrabOptions = {
|
||||
site?: string
|
||||
channels?: string
|
||||
output: string
|
||||
gzip: boolean
|
||||
maxConnections: number
|
||||
timeout?: string
|
||||
lang?: string
|
||||
days?: number
|
||||
cron?: string
|
||||
}
|
||||
|
||||
const options: GrabOptions = program.opts()
|
||||
|
||||
async function main() {
|
||||
if (!options.site && !options.channels)
|
||||
throw new Error('One of the arguments must be presented: `--site` or `--channels`')
|
||||
|
||||
const logger = new Logger()
|
||||
|
||||
logger.start('staring...')
|
||||
|
||||
logger.info('config:')
|
||||
logger.tree(options)
|
||||
|
||||
logger.info(`loading channels...`)
|
||||
const storage = new Storage()
|
||||
const parser = new ChannelsParser({ storage })
|
||||
|
||||
let files: string[] = []
|
||||
if (options.site) {
|
||||
files = await storage.list(path.join(SITES_DIR, `${options.site}/*.channels.xml`))
|
||||
} else if (options.channels) {
|
||||
files = await storage.list(options.channels)
|
||||
}
|
||||
|
||||
let parsedChannels = new Collection()
|
||||
for (let filepath of files) {
|
||||
parsedChannels = parsedChannels.concat(await parser.parse(filepath))
|
||||
}
|
||||
if (options.lang) {
|
||||
parsedChannels = parsedChannels.filter((channel: Channel) => channel.lang === options.lang)
|
||||
}
|
||||
logger.info(` found ${parsedChannels.count()} channels`)
|
||||
|
||||
logger.info('creating queue...')
|
||||
const queue = new Queue({
|
||||
parsedChannels,
|
||||
logger,
|
||||
options
|
||||
})
|
||||
await queue.create()
|
||||
logger.info(` added ${queue.size()} items`)
|
||||
|
||||
const job = new Job({
|
||||
queue,
|
||||
logger,
|
||||
options
|
||||
})
|
||||
|
||||
let runIndex = 1
|
||||
if (options.cron) {
|
||||
const cronJob = new CronJob(options.cron, async () => {
|
||||
logger.info(`run #${runIndex}:`)
|
||||
const timer = new Timer()
|
||||
timer.start()
|
||||
await job.run()
|
||||
runIndex++
|
||||
logger.success(` done in ${timer.format('HH[h] mm[m] ss[s]')}`)
|
||||
})
|
||||
cronJob.start()
|
||||
} else {
|
||||
logger.info(`run #${runIndex}:`)
|
||||
const timer = new Timer()
|
||||
timer.start()
|
||||
await job.run()
|
||||
logger.success(` done in ${timer.format('HH[h] mm[m] ss[s]')}`)
|
||||
}
|
||||
|
||||
logger.info('finished')
|
||||
}
|
||||
|
||||
main()
|
4
scripts/constants.ts
Normal file
4
scripts/constants.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export const SITES_DIR = process.env.SITES_DIR || './sites'
|
||||
export const GUIDES_DIR = process.env.GUIDES_DIR || './guides'
|
||||
export const DATA_DIR = process.env.DATA_DIR || './temp/data'
|
||||
export const CURR_DATE = process.env.CURR_DATE || new Date().toISOString()
|
|
@ -1,32 +0,0 @@
|
|||
const _ = require('lodash')
|
||||
const file = require('./file')
|
||||
|
||||
const DATA_DIR = process.env.DATA_DIR || './scripts/tmp/data'
|
||||
|
||||
class API {
|
||||
constructor(filepath) {
|
||||
this.filepath = file.resolve(filepath)
|
||||
}
|
||||
|
||||
async load() {
|
||||
const data = await file.read(this.filepath)
|
||||
this.collection = JSON.parse(data)
|
||||
}
|
||||
|
||||
find(query) {
|
||||
return _.find(this.collection, query)
|
||||
}
|
||||
|
||||
all() {
|
||||
return this.collection
|
||||
}
|
||||
}
|
||||
|
||||
const api = {}
|
||||
|
||||
api.channels = new API(`${DATA_DIR}/channels.json`)
|
||||
api.regions = new API(`${DATA_DIR}/regions.json`)
|
||||
api.countries = new API(`${DATA_DIR}/countries.json`)
|
||||
api.subdivisions = new API(`${DATA_DIR}/subdivisions.json`)
|
||||
|
||||
module.exports = api
|
79
scripts/core/apiChannel.ts
Normal file
79
scripts/core/apiChannel.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { Collection } from '@freearhey/core'
|
||||
|
||||
type ApiChannelProps = {
|
||||
id: string
|
||||
name: string
|
||||
alt_names: string[]
|
||||
network: string
|
||||
owners: string[]
|
||||
country: string
|
||||
subdivision: string
|
||||
city: string
|
||||
broadcast_area: string[]
|
||||
languages: string[]
|
||||
categories: string[]
|
||||
is_nsfw: boolean
|
||||
launched: string
|
||||
closed: string
|
||||
replaced_by: string
|
||||
website: string
|
||||
logo: string
|
||||
}
|
||||
|
||||
export class ApiChannel {
|
||||
id: string
|
||||
name: string
|
||||
altNames: Collection
|
||||
network: string
|
||||
owners: Collection
|
||||
country: string
|
||||
subdivision: string
|
||||
city: string
|
||||
broadcastArea: Collection
|
||||
languages: Collection
|
||||
categories: Collection
|
||||
isNSFW: boolean
|
||||
launched: string
|
||||
closed: string
|
||||
replacedBy: string
|
||||
website: string
|
||||
logo: string
|
||||
|
||||
constructor({
|
||||
id,
|
||||
name,
|
||||
alt_names,
|
||||
network,
|
||||
owners,
|
||||
country,
|
||||
subdivision,
|
||||
city,
|
||||
broadcast_area,
|
||||
languages,
|
||||
categories,
|
||||
is_nsfw,
|
||||
launched,
|
||||
closed,
|
||||
replaced_by,
|
||||
website,
|
||||
logo
|
||||
}: ApiChannelProps) {
|
||||
this.id = id
|
||||
this.name = name
|
||||
this.altNames = new Collection(alt_names)
|
||||
this.network = network
|
||||
this.owners = new Collection(owners)
|
||||
this.country = country
|
||||
this.subdivision = subdivision
|
||||
this.city = city
|
||||
this.broadcastArea = new Collection(broadcast_area)
|
||||
this.languages = new Collection(languages)
|
||||
this.categories = new Collection(categories)
|
||||
this.isNSFW = is_nsfw
|
||||
this.launched = launched
|
||||
this.closed = closed
|
||||
this.replacedBy = replaced_by
|
||||
this.website = website
|
||||
this.logo = logo
|
||||
}
|
||||
}
|
59
scripts/core/apiClient.ts
Normal file
59
scripts/core/apiClient.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { Logger, Storage } from '@freearhey/core'
|
||||
import axios, { AxiosInstance, AxiosResponse, AxiosProgressEvent } from 'axios'
|
||||
import cliProgress, { MultiBar } from 'cli-progress'
|
||||
import numeral from 'numeral'
|
||||
|
||||
export class ApiClient {
|
||||
progressBar: MultiBar
|
||||
client: AxiosInstance
|
||||
storage: Storage
|
||||
logger: Logger
|
||||
|
||||
constructor({ logger }: { logger: Logger }) {
|
||||
this.logger = logger
|
||||
this.client = axios.create({
|
||||
responseType: 'stream'
|
||||
})
|
||||
this.storage = new Storage()
|
||||
this.progressBar = new cliProgress.MultiBar({
|
||||
stopOnComplete: true,
|
||||
hideCursor: true,
|
||||
forceRedraw: true,
|
||||
barsize: 36,
|
||||
format(options, params, payload) {
|
||||
const filename = payload.filename.padEnd(18, ' ')
|
||||
const barsize = options.barsize || 40
|
||||
const percent = (params.progress * 100).toFixed(2)
|
||||
const speed = payload.speed ? numeral(payload.speed).format('0.0 b') + '/s' : 'N/A'
|
||||
const total = numeral(params.total).format('0.0 b')
|
||||
const completeSize = Math.round(params.progress * barsize)
|
||||
const incompleteSize = barsize - completeSize
|
||||
const bar =
|
||||
options.barCompleteString && options.barIncompleteString
|
||||
? options.barCompleteString.substr(0, completeSize) +
|
||||
options.barGlue +
|
||||
options.barIncompleteString.substr(0, incompleteSize)
|
||||
: '-'.repeat(barsize)
|
||||
|
||||
return `${filename} [${bar}] ${percent}% | ETA: ${params.eta}s | ${total} | ${speed}`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async download(filename: string) {
|
||||
const stream = await this.storage.createStream(`/temp/data/${filename}`)
|
||||
|
||||
const bar = this.progressBar.create(0, 0, { filename })
|
||||
|
||||
this.client
|
||||
.get(`https://iptv-org.github.io/api/${filename}`, {
|
||||
onDownloadProgress({ total, loaded, rate }: AxiosProgressEvent) {
|
||||
if (total) bar.setTotal(total)
|
||||
bar.update(loaded, { speed: rate })
|
||||
}
|
||||
})
|
||||
.then((response: AxiosResponse) => {
|
||||
response.data.pipe(stream)
|
||||
})
|
||||
}
|
||||
}
|
24
scripts/core/channelsParser.ts
Normal file
24
scripts/core/channelsParser.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { parseChannels } from 'epg-grabber'
|
||||
import { Storage, Collection } from '@freearhey/core'
|
||||
|
||||
type ChannelsParserProps = {
|
||||
storage: Storage
|
||||
}
|
||||
|
||||
export class ChannelsParser {
|
||||
storage: Storage
|
||||
|
||||
constructor({ storage }: ChannelsParserProps) {
|
||||
this.storage = storage
|
||||
}
|
||||
|
||||
async parse(filepath: string) {
|
||||
let parsedChannels = new Collection()
|
||||
|
||||
const content = await this.storage.load(filepath)
|
||||
const channels = parseChannels(content)
|
||||
parsedChannels = parsedChannels.concat(new Collection(channels))
|
||||
|
||||
return parsedChannels
|
||||
}
|
||||
}
|
19
scripts/core/configLoader.ts
Normal file
19
scripts/core/configLoader.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { SiteConfig } from 'epg-grabber'
|
||||
import _ from 'lodash'
|
||||
|
||||
export class ConfigLoader {
|
||||
async load(filepath: string): Promise<SiteConfig> {
|
||||
const config = (await import(filepath)).default
|
||||
|
||||
return _.merge(
|
||||
{
|
||||
delay: 0,
|
||||
maxConnections: 1,
|
||||
request: {
|
||||
timeout: 30000
|
||||
}
|
||||
},
|
||||
config
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
const nedb = require('nedb-promises')
|
||||
const file = require('./file')
|
||||
|
||||
const DB_DIR = process.env.DB_DIR || './scripts/tmp/database'
|
||||
|
||||
class Database {
|
||||
constructor(filepath) {
|
||||
this.filepath = filepath
|
||||
}
|
||||
|
||||
load() {
|
||||
this.db = nedb.create({
|
||||
filename: file.resolve(this.filepath),
|
||||
autoload: true,
|
||||
onload: err => {
|
||||
if (err) console.error(err)
|
||||
},
|
||||
compareStrings: (a, b) => {
|
||||
a = a.replace(/\s/g, '_')
|
||||
b = b.replace(/\s/g, '_')
|
||||
|
||||
return a.localeCompare(b, undefined, {
|
||||
sensitivity: 'accent',
|
||||
numeric: true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
removeIndex(field) {
|
||||
return this.db.removeIndex(field)
|
||||
}
|
||||
|
||||
addIndex(options) {
|
||||
return this.db.ensureIndex(options)
|
||||
}
|
||||
|
||||
compact() {
|
||||
return this.db.persistence.compactDatafile()
|
||||
}
|
||||
|
||||
stopAutocompact() {
|
||||
return this.db.persistence.stopAutocompaction()
|
||||
}
|
||||
|
||||
reset() {
|
||||
return file.clear(this.filepath)
|
||||
}
|
||||
|
||||
count(query) {
|
||||
return this.db.count(query)
|
||||
}
|
||||
|
||||
insert(doc) {
|
||||
return this.db.insert(doc)
|
||||
}
|
||||
|
||||
update(query, update) {
|
||||
return this.db.update(query, update)
|
||||
}
|
||||
|
||||
find(query) {
|
||||
return this.db.find(query)
|
||||
}
|
||||
|
||||
remove(query, options) {
|
||||
return this.db.remove(query, options)
|
||||
}
|
||||
}
|
||||
|
||||
const db = {}
|
||||
|
||||
db.queue = new Database(`${DB_DIR}/queue.db`)
|
||||
db.programs = new Database(`${DB_DIR}/programs.db`)
|
||||
|
||||
module.exports = db
|
|
@ -1,93 +0,0 @@
|
|||
const path = require('path')
|
||||
const glob = require('glob')
|
||||
const fs = require('fs-extra')
|
||||
|
||||
const file = {}
|
||||
|
||||
file.templateVariables = function (template) {
|
||||
const match = template.match(/{[^}]+}/g)
|
||||
|
||||
return Array.isArray(match) ? match.map(s => s.substring(1, s.length - 1)) : []
|
||||
}
|
||||
|
||||
file.templateFormat = function (template, obj) {
|
||||
let output = template
|
||||
for (let key in obj) {
|
||||
const regex = new RegExp(`{${key}}`, 'g')
|
||||
const value = obj[key] || undefined
|
||||
output = output.replace(regex, value)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
file.list = function (pattern) {
|
||||
return new Promise(resolve => {
|
||||
glob(pattern, function (err, files) {
|
||||
resolve(files)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
file.getFilename = function (filepath) {
|
||||
return path.parse(filepath).name
|
||||
}
|
||||
|
||||
file.createDir = async function (dir) {
|
||||
if (await file.exists(dir)) return
|
||||
|
||||
return fs.mkdir(dir, { recursive: true }).catch(console.error)
|
||||
}
|
||||
|
||||
file.exists = function (filepath) {
|
||||
return fs.exists(path.resolve(filepath))
|
||||
}
|
||||
|
||||
file.existsSync = function (filepath) {
|
||||
return fs.existsSync(path.resolve(filepath))
|
||||
}
|
||||
|
||||
file.read = function (filepath) {
|
||||
return fs.readFile(path.resolve(filepath), { encoding: 'utf8' }).catch(console.error)
|
||||
}
|
||||
|
||||
file.append = function (filepath, data) {
|
||||
return fs.appendFile(path.resolve(filepath), data).catch(console.error)
|
||||
}
|
||||
|
||||
file.create = function (filepath, data = '') {
|
||||
filepath = path.resolve(filepath)
|
||||
const dir = path.dirname(filepath)
|
||||
|
||||
return file
|
||||
.createDir(dir)
|
||||
.then(() => file.write(filepath, data))
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
file.write = function (filepath, data = '') {
|
||||
return fs.writeFile(path.resolve(filepath), data, { encoding: 'utf8' }).catch(console.error)
|
||||
}
|
||||
|
||||
file.writeSync = function (filepath, data = '') {
|
||||
return fs.writeFileSync(path.resolve(filepath), data, { encoding: 'utf8' })
|
||||
}
|
||||
|
||||
file.clear = async function (filepath) {
|
||||
if (await file.exists(filepath)) return file.write(filepath, '')
|
||||
return true
|
||||
}
|
||||
|
||||
file.resolve = function (filepath) {
|
||||
return path.resolve(filepath)
|
||||
}
|
||||
|
||||
file.dirname = function (filepath) {
|
||||
return path.dirname(filepath)
|
||||
}
|
||||
|
||||
file.basename = function (filepath) {
|
||||
return path.basename(filepath)
|
||||
}
|
||||
|
||||
module.exports = file
|
75
scripts/core/grabber.ts
Normal file
75
scripts/core/grabber.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import { EPGGrabber, GrabCallbackData, EPGGrabberMock, SiteConfig, Channel } from 'epg-grabber'
|
||||
import { Logger, Collection } from '@freearhey/core'
|
||||
import { Queue } from './'
|
||||
import { GrabOptions } from '../commands/epg/grab'
|
||||
import { TaskQueue, PromisyClass } from 'cwait'
|
||||
|
||||
type GrabberProps = {
|
||||
logger: Logger
|
||||
queue: Queue
|
||||
options: GrabOptions
|
||||
}
|
||||
|
||||
export class Grabber {
|
||||
logger: Logger
|
||||
queue: Queue
|
||||
options: GrabOptions
|
||||
|
||||
constructor({ logger, queue, options }: GrabberProps) {
|
||||
this.logger = logger
|
||||
this.queue = queue
|
||||
this.options = options
|
||||
}
|
||||
|
||||
async grab(): Promise<{ channels: Collection; programs: Collection }> {
|
||||
const taskQueue = new TaskQueue(Promise as PromisyClass, this.options.maxConnections)
|
||||
|
||||
const total = this.queue.size()
|
||||
|
||||
const channels = new Collection()
|
||||
let programs = new Collection()
|
||||
let i = 1
|
||||
|
||||
await Promise.all(
|
||||
this.queue.items().map(
|
||||
taskQueue.wrap(
|
||||
async (queueItem: { channel: Channel; config: SiteConfig; date: string }) => {
|
||||
const { channel, config, date } = queueItem
|
||||
|
||||
channels.add(channel)
|
||||
|
||||
if (this.options.timeout !== undefined) {
|
||||
const timeout = parseInt(this.options.timeout)
|
||||
config.request = { ...config.request, ...{ timeout } }
|
||||
}
|
||||
|
||||
const grabber =
|
||||
process.env.NODE_ENV === 'test' ? new EPGGrabberMock(config) : new EPGGrabber(config)
|
||||
const _programs = await grabber.grab(
|
||||
channel,
|
||||
date,
|
||||
(data: GrabCallbackData, error: Error | null) => {
|
||||
const { programs, date } = data
|
||||
|
||||
this.logger.info(
|
||||
` [${i}/${total}] ${channel.site} (${channel.lang}) - ${
|
||||
channel.xmltv_id
|
||||
} - ${date.format('MMM D, YYYY')} (${programs.length} programs)`
|
||||
)
|
||||
if (i < total) i++
|
||||
|
||||
if (error) {
|
||||
this.logger.info(` ERR: ${error.message}`)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
programs = programs.concat(new Collection(_programs))
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return { channels, programs }
|
||||
}
|
||||
}
|
55
scripts/core/guide.ts
Normal file
55
scripts/core/guide.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { Collection, Logger, DateTime, Storage, Zip } from '@freearhey/core'
|
||||
import { Channel } from 'epg-grabber'
|
||||
import { XMLTV } from '../core'
|
||||
import { CURR_DATE } from '../constants'
|
||||
|
||||
type GuideProps = {
|
||||
channels: Collection
|
||||
programs: Collection
|
||||
logger: Logger
|
||||
filepath: string
|
||||
gzip: boolean
|
||||
}
|
||||
|
||||
export class Guide {
|
||||
channels: Collection
|
||||
programs: Collection
|
||||
logger: Logger
|
||||
storage: Storage
|
||||
filepath: string
|
||||
gzip: boolean
|
||||
|
||||
constructor({ channels, programs, logger, filepath, gzip }: GuideProps) {
|
||||
this.channels = channels
|
||||
this.programs = programs
|
||||
this.logger = logger
|
||||
this.storage = new Storage()
|
||||
this.filepath = filepath
|
||||
this.gzip = gzip || false
|
||||
}
|
||||
|
||||
async save() {
|
||||
const channels = this.channels.uniqBy(
|
||||
(channel: Channel) => `${channel.xmltv_id}:${channel.site}`
|
||||
)
|
||||
const programs = this.programs
|
||||
|
||||
const xmltv = new XMLTV({
|
||||
channels,
|
||||
programs,
|
||||
date: new DateTime(CURR_DATE, { zone: 'UTC' })
|
||||
})
|
||||
|
||||
const xmlFilepath = this.filepath
|
||||
this.logger.info(` saving to "${xmlFilepath}"...`)
|
||||
await this.storage.save(xmlFilepath, xmltv.toString())
|
||||
|
||||
if (this.gzip) {
|
||||
const zip = new Zip()
|
||||
const compressed = await zip.compress(xmltv.toString())
|
||||
const gzFilepath = `${this.filepath}.gz`
|
||||
this.logger.info(` saving to "${gzFilepath}"...`)
|
||||
await this.storage.save(gzFilepath, compressed)
|
||||
}
|
||||
}
|
||||
}
|
61
scripts/core/guideManager.ts
Normal file
61
scripts/core/guideManager.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
import { Collection, Logger, Storage, StringTemplate } from '@freearhey/core'
|
||||
import { OptionValues } from 'commander'
|
||||
import { Channel, Program } from 'epg-grabber'
|
||||
import { Guide } from '.'
|
||||
|
||||
type GuideManagerProps = {
|
||||
options: OptionValues
|
||||
logger: Logger
|
||||
channels: Collection
|
||||
programs: Collection
|
||||
}
|
||||
|
||||
export class GuideManager {
|
||||
options: OptionValues
|
||||
storage: Storage
|
||||
logger: Logger
|
||||
channels: Collection
|
||||
programs: Collection
|
||||
|
||||
constructor({ channels, programs, logger, options }: GuideManagerProps) {
|
||||
this.options = options
|
||||
this.logger = logger
|
||||
this.channels = channels
|
||||
this.programs = programs
|
||||
this.storage = new Storage()
|
||||
}
|
||||
|
||||
async createGuides() {
|
||||
const pathTemplate = new StringTemplate(this.options.output)
|
||||
|
||||
const groupedChannels = this.channels
|
||||
.orderBy([(channel: Channel) => channel.xmltv_id])
|
||||
.uniqBy((channel: Channel) => `${channel.xmltv_id}:${channel.site}:${channel.lang}`)
|
||||
.groupBy((channel: Channel) => {
|
||||
return pathTemplate.format({ lang: channel.lang || 'en', site: channel.site || '' })
|
||||
})
|
||||
|
||||
const groupedPrograms = this.programs
|
||||
.orderBy([(program: Program) => program.channel, (program: Program) => program.start])
|
||||
.groupBy((program: Program) => {
|
||||
const lang =
|
||||
program.titles && program.titles.length && program.titles[0].lang
|
||||
? program.titles[0].lang
|
||||
: 'en'
|
||||
|
||||
return pathTemplate.format({ lang, site: program.site || '' })
|
||||
})
|
||||
|
||||
for (const groupKey of groupedPrograms.keys()) {
|
||||
const guide = new Guide({
|
||||
filepath: groupKey,
|
||||
gzip: this.options.gzip,
|
||||
channels: new Collection(groupedChannels.get(groupKey)),
|
||||
programs: new Collection(groupedPrograms.get(groupKey)),
|
||||
logger: this.logger
|
||||
})
|
||||
|
||||
await guide.save()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
exports.db = require('./db')
|
||||
exports.logger = require('./logger')
|
||||
exports.file = require('./file')
|
||||
exports.parser = require('./parser')
|
||||
exports.timer = require('./timer')
|
||||
exports.markdown = require('./markdown')
|
||||
exports.api = require('./api')
|
||||
exports.date = require('./date')
|
||||
exports.table = require('./table')
|
||||
exports.xml = require('./xml')
|
||||
exports.zip = require('./zip')
|
11
scripts/core/index.ts
Normal file
11
scripts/core/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
export * from './xml'
|
||||
export * from './channelsParser'
|
||||
export * from './xmltv'
|
||||
export * from './configLoader'
|
||||
export * from './grabber'
|
||||
export * from './job'
|
||||
export * from './queue'
|
||||
export * from './guideManager'
|
||||
export * from './guide'
|
||||
export * from './apiChannel'
|
||||
export * from './apiClient'
|
34
scripts/core/job.ts
Normal file
34
scripts/core/job.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { Logger } from '@freearhey/core'
|
||||
import { Queue, Grabber, GuideManager } from '.'
|
||||
import { GrabOptions } from '../commands/epg/grab'
|
||||
|
||||
type JobProps = {
|
||||
options: GrabOptions
|
||||
logger: Logger
|
||||
queue: Queue
|
||||
}
|
||||
|
||||
export class Job {
|
||||
options: GrabOptions
|
||||
logger: Logger
|
||||
grabber: Grabber
|
||||
|
||||
constructor({ queue, logger, options }: JobProps) {
|
||||
this.options = options
|
||||
this.logger = logger
|
||||
this.grabber = new Grabber({ logger, queue, options })
|
||||
}
|
||||
|
||||
async run() {
|
||||
const { channels, programs } = await this.grabber.grab()
|
||||
|
||||
const manager = new GuideManager({
|
||||
channels,
|
||||
programs,
|
||||
options: this.options,
|
||||
logger: this.logger
|
||||
})
|
||||
|
||||
await manager.createGuides()
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
const { consola } = require('consola')
|
||||
|
||||
module.exports = consola
|
|
@ -1,10 +0,0 @@
|
|||
const markdownInclude = require('markdown-include')
|
||||
const file = require('./file')
|
||||
|
||||
const markdown = {}
|
||||
|
||||
markdown.compile = function (filepath) {
|
||||
markdownInclude.compileFiles(file.resolve(filepath))
|
||||
}
|
||||
|
||||
module.exports = markdown
|
|
@ -1,29 +0,0 @@
|
|||
const file = require('./file')
|
||||
const grabber = require('epg-grabber')
|
||||
|
||||
const parser = {}
|
||||
|
||||
parser.parseChannels = async function (filepath) {
|
||||
const content = await file.read(filepath)
|
||||
|
||||
return grabber.parseChannels(content)
|
||||
}
|
||||
|
||||
parser.parseLogs = async function (filepath) {
|
||||
const content = await file.read(filepath)
|
||||
if (!content) return []
|
||||
const lines = content.split('\n')
|
||||
|
||||
return lines.map(line => (line ? JSON.parse(line) : null)).filter(l => l)
|
||||
}
|
||||
|
||||
parser.parseNumber = function (string) {
|
||||
const parsed = parseInt(string)
|
||||
if (isNaN(parsed)) {
|
||||
throw new Error('scripts/core/parser.js:parseNumber() Input value is not a number')
|
||||
}
|
||||
|
||||
return parsed
|
||||
}
|
||||
|
||||
module.exports = parser
|
94
scripts/core/queue.ts
Normal file
94
scripts/core/queue.ts
Normal file
|
@ -0,0 +1,94 @@
|
|||
import { Storage, Collection, DateTime, Logger, Dictionary } from '@freearhey/core'
|
||||
import { ChannelsParser, ConfigLoader, ApiChannel } from './'
|
||||
import { SITES_DIR, DATA_DIR, CURR_DATE } from '../constants'
|
||||
import { Channel, SiteConfig } from 'epg-grabber'
|
||||
import path from 'path'
|
||||
import { GrabOptions } from '../commands/epg/grab'
|
||||
|
||||
export type QueueItem = {
|
||||
channel: Channel
|
||||
date: string
|
||||
config: SiteConfig
|
||||
error: string | null
|
||||
}
|
||||
|
||||
type QueueProps = {
|
||||
logger: Logger
|
||||
options: GrabOptions
|
||||
parsedChannels: Collection
|
||||
}
|
||||
|
||||
export class Queue {
|
||||
configLoader: ConfigLoader
|
||||
logger: Logger
|
||||
sitesStorage: Storage
|
||||
dataStorage: Storage
|
||||
parser: ChannelsParser
|
||||
parsedChannels: Collection
|
||||
options: GrabOptions
|
||||
date: DateTime
|
||||
_items: QueueItem[] = []
|
||||
|
||||
constructor({ parsedChannels, logger, options }: QueueProps) {
|
||||
this.parsedChannels = parsedChannels
|
||||
this.logger = logger
|
||||
this.sitesStorage = new Storage()
|
||||
this.dataStorage = new Storage(DATA_DIR)
|
||||
this.parser = new ChannelsParser({ storage: new Storage() })
|
||||
this.date = new DateTime(CURR_DATE)
|
||||
this.options = options
|
||||
this.configLoader = new ConfigLoader()
|
||||
}
|
||||
|
||||
async create() {
|
||||
const channelsContent = await this.dataStorage.json('channels.json')
|
||||
const channels = new Collection(channelsContent).map(data => new ApiChannel(data))
|
||||
|
||||
const queue = new Dictionary()
|
||||
|
||||
for (const channel of this.parsedChannels.all()) {
|
||||
if (!channel.site || !channel.xmltv_id) continue
|
||||
if (this.options.lang && channel.lang !== this.options.lang) continue
|
||||
|
||||
const configPath = path.resolve(SITES_DIR, `${channel.site}/${channel.site}.config.js`)
|
||||
const config: SiteConfig = await this.configLoader.load(configPath)
|
||||
|
||||
const found: ApiChannel = channels.first(
|
||||
(_channel: ApiChannel) => _channel.id === channel.xmltv_id
|
||||
)
|
||||
if (found) {
|
||||
channel.logo = found.logo
|
||||
}
|
||||
|
||||
const days = this.options.days || config.days || 1
|
||||
const dates = Array.from({ length: days }, (_, day) => this.date.add(day, 'd'))
|
||||
dates.forEach((date: DateTime) => {
|
||||
const dateString = date.toJSON()
|
||||
const key = `${channel.site}:${channel.lang}:${channel.xmltv_id}:${dateString}`
|
||||
|
||||
if (queue.missing(key)) {
|
||||
queue.set(key, {
|
||||
channel,
|
||||
date: dateString,
|
||||
config,
|
||||
error: null
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this._items = Object.values(queue.data())
|
||||
}
|
||||
|
||||
size(): number {
|
||||
return this._items.length
|
||||
}
|
||||
|
||||
items(): QueueItem[] {
|
||||
return this._items
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this._items.length === 0
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
const table = {}
|
||||
|
||||
table.create = function (data, cols) {
|
||||
let output = '<table>\r\n'
|
||||
|
||||
output += ' <thead>\r\n <tr>'
|
||||
for (let column of cols) {
|
||||
output += `<th align="left">${column}</th>`
|
||||
}
|
||||
output += '</tr>\r\n </thead>\r\n'
|
||||
|
||||
output += ' <tbody>\r\n'
|
||||
output += getHTMLRows(data)
|
||||
output += ' </tbody>\r\n'
|
||||
|
||||
output += '</table>'
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
function getHTMLRows(data) {
|
||||
let output = ''
|
||||
for (let group of data) {
|
||||
let rowspan = group.length
|
||||
for (let [j, row] of group.entries()) {
|
||||
output += ' <tr>'
|
||||
for (let [i, value] of row.entries()) {
|
||||
if (i === 0 && j === 0) {
|
||||
output += `<td valign="top" rowspan="${rowspan}">${value}</td>`
|
||||
} else if (i > 0) {
|
||||
if (typeof value === 'number') {
|
||||
output += `<td align="right" nowrap>${value}</td>`
|
||||
} else {
|
||||
output += `<td nowrap>${value}</td>`
|
||||
}
|
||||
}
|
||||
}
|
||||
output += '</tr>\r\n'
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
function getSpan() {}
|
||||
|
||||
module.exports = table
|
|
@ -1,29 +0,0 @@
|
|||
const { performance } = require('perf_hooks')
|
||||
const dayjs = require('dayjs')
|
||||
const duration = require('dayjs/plugin/duration')
|
||||
const relativeTime = require('dayjs/plugin/relativeTime')
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
dayjs.extend(duration)
|
||||
|
||||
const timer = {}
|
||||
|
||||
let t0 = 0
|
||||
|
||||
timer.start = function () {
|
||||
t0 = performance.now()
|
||||
}
|
||||
|
||||
timer.format = function (f) {
|
||||
let t1 = performance.now()
|
||||
|
||||
return dayjs.duration(t1 - t0).format(f)
|
||||
}
|
||||
|
||||
timer.humanize = function (suffix = true) {
|
||||
let t1 = performance.now()
|
||||
|
||||
return dayjs.duration(t1 - t0).humanize(suffix)
|
||||
}
|
||||
|
||||
module.exports = timer
|
|
@ -1,25 +1,36 @@
|
|||
const xml = {}
|
||||
import { Collection } from '@freearhey/core'
|
||||
import { Channel } from 'epg-grabber'
|
||||
|
||||
xml.create = function (items, site) {
|
||||
let output = `<?xml version="1.0" encoding="UTF-8"?>\r\n<site site="${site}">\r\n <channels>\r\n`
|
||||
export class XML {
|
||||
items: Collection
|
||||
site: string
|
||||
|
||||
items.forEach(channel => {
|
||||
const logo = channel.logo ? ` logo="${channel.logo}"` : ''
|
||||
const xmltv_id = channel.xmltv_id || ''
|
||||
const lang = channel.lang || ''
|
||||
const site_id = channel.site_id || ''
|
||||
output += ` <channel lang="${lang}" xmltv_id="${escapeString(
|
||||
xmltv_id
|
||||
)}" site_id="${site_id}"${logo}>${escapeString(channel.name)}</channel>\r\n`
|
||||
})
|
||||
constructor(items: Collection, site: string) {
|
||||
this.items = items
|
||||
this.site = site
|
||||
}
|
||||
|
||||
output += ` </channels>\r\n</site>\r\n`
|
||||
toString() {
|
||||
let output = '<?xml version="1.0" encoding="UTF-8"?>\r\n<channels>\r\n'
|
||||
|
||||
return output
|
||||
this.items.forEach((channel: Channel) => {
|
||||
const logo = channel.logo ? ` logo="${channel.logo}"` : ''
|
||||
const xmltv_id = channel.xmltv_id || ''
|
||||
const lang = channel.lang || ''
|
||||
const site_id = channel.site_id || ''
|
||||
output += ` <channel site="${this.site}" lang="${lang}" xmltv_id="${escapeString(
|
||||
xmltv_id
|
||||
)}" site_id="${site_id}"${logo}>${escapeString(channel.name)}</channel>\r\n`
|
||||
})
|
||||
|
||||
output += '</channels>\r\n'
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
function escapeString(string, defaultValue = '') {
|
||||
if (!string) return defaultValue
|
||||
function escapeString(value: string, defaultValue: string = '') {
|
||||
if (!value) return defaultValue
|
||||
|
||||
const regex = new RegExp(
|
||||
'((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))|([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|(?:\\uD83F[\\uDFFE\\uDFFF])|(?:\\uD87F[\\uDF' +
|
||||
|
@ -33,9 +44,9 @@ function escapeString(string, defaultValue = '') {
|
|||
'g'
|
||||
)
|
||||
|
||||
string = String(string || '').replace(regex, '')
|
||||
value = String(value || '').replace(regex, '')
|
||||
|
||||
return string
|
||||
return value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
|
@ -45,5 +56,3 @@ function escapeString(string, defaultValue = '') {
|
|||
.replace(/ +/g, ' ')
|
||||
.trim()
|
||||
}
|
||||
|
||||
module.exports = xml
|
28
scripts/core/xmltv.ts
Normal file
28
scripts/core/xmltv.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { DateTime, Collection } from '@freearhey/core'
|
||||
import { generateXMLTV } from 'epg-grabber'
|
||||
|
||||
type XMLTVProps = {
|
||||
channels: Collection
|
||||
programs: Collection
|
||||
date: DateTime
|
||||
}
|
||||
|
||||
export class XMLTV {
|
||||
channels: Collection
|
||||
programs: Collection
|
||||
date: DateTime
|
||||
|
||||
constructor({ channels, programs, date }: XMLTVProps) {
|
||||
this.channels = channels
|
||||
this.programs = programs
|
||||
this.date = date
|
||||
}
|
||||
|
||||
toString() {
|
||||
return generateXMLTV({
|
||||
channels: this.channels.all(),
|
||||
programs: this.programs.all(),
|
||||
date: this.date.toJSON()
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
const { gzip, ungzip } = require('node-gzip')
|
||||
|
||||
const zip = {}
|
||||
|
||||
zip.compress = async function (string) {
|
||||
return gzip(string)
|
||||
}
|
||||
|
||||
zip.decompress = async function (string) {
|
||||
return ungzip(string)
|
||||
}
|
||||
|
||||
module.exports = zip
|
2
scripts/tmp/.gitignore
vendored
2
scripts/tmp/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
|||
*
|
||||
!.gitignore
|
1
scripts/types/langs.d.ts
vendored
Normal file
1
scripts/types/langs.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
declare module 'langs'
|
|
@ -1,6 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="9tv.co.il">
|
||||
<channels>
|
||||
<channel lang="ru" xmltv_id="Channel9.il" site_id="#">9 канал</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="9tv.co.il" lang="ru" xmltv_id="Channel9.il" site_id="#">9 канал</channel>
|
||||
</channels>
|
||||
|
|
|
@ -49,7 +49,7 @@ function parseIcon($item) {
|
|||
'background-image'
|
||||
)
|
||||
if (!backgroundImage) return null
|
||||
const [_, relativePath] = backgroundImage.match(/url\((.*)\)/) || [null, null]
|
||||
const [, relativePath] = backgroundImage.match(/url\((.*)\)/) || [null, null]
|
||||
|
||||
return relativePath ? `https://www.9tv.co.il${relativePath}` : null
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// npx epg-grabber --config=sites/9tv.co.il/9tv.co.il.config.js --channels=sites/9tv.co.il/9tv.co.il.channels.xml --output=guide.xml --days=2
|
||||
// npm run grab -- --site=9tv.co.il
|
||||
|
||||
const { parser, url } = require('./9tv.co.il.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
|
@ -20,7 +20,8 @@ it('can generate valid url', () => {
|
|||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `<li> <a href="#" class="guide_list_link w-inline-block"> <div class="guide_list_time">06:30</div><div class="guide_info_group"> <div class="guide_info_pict" style="background-image: url(/download/pictures/img_id=8484.jpg);"></div><div class="guide_txt_group"> <h3 class="guide_info_title">Слепая</h3> <div>Она не очень любит говорить о себе или о том, кто и зачем к ней обращается. Живет уединенно, в глуши. Но тех, кто приходит -принимает. Она видит судьбы. </div></div></div></a></li><li> <a href="#" class="guide_list_link even w-inline-block"> <div class="guide_list_time">09:10</div><div class="guide_info_group"> <div class="guide_info_pict" style="background-image: url(/download/pictures/img_id=23694.jpg);"></div><div class="guide_txt_group"> <h3 class="guide_info_title">Орел и решка. Морской сезон</h3> <div>Орел и решка. Морской сезон. Ведущие -Алина Астровская и Коля Серга.</div></div></div></a></li>`
|
||||
const content =
|
||||
'<li> <a href="#" class="guide_list_link w-inline-block"> <div class="guide_list_time">06:30</div><div class="guide_info_group"> <div class="guide_info_pict" style="background-image: url(/download/pictures/img_id=8484.jpg);"></div><div class="guide_txt_group"> <h3 class="guide_info_title">Слепая</h3> <div>Она не очень любит говорить о себе или о том, кто и зачем к ней обращается. Живет уединенно, в глуши. Но тех, кто приходит -принимает. Она видит судьбы. </div></div></div></a></li><li> <a href="#" class="guide_list_link even w-inline-block"> <div class="guide_list_time">09:10</div><div class="guide_info_group"> <div class="guide_info_pict" style="background-image: url(/download/pictures/img_id=23694.jpg);"></div><div class="guide_txt_group"> <h3 class="guide_info_title">Орел и решка. Морской сезон</h3> <div>Орел и решка. Морской сезон. Ведущие -Алина Астровская и Коля Серга.</div></div></div></a></li>'
|
||||
const result = parser({ content, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
|
@ -31,7 +32,7 @@ it('can parse response', () => {
|
|||
{
|
||||
start: '2022-03-06T04:30:00.000Z',
|
||||
stop: '2022-03-06T07:10:00.000Z',
|
||||
title: `Слепая`,
|
||||
title: 'Слепая',
|
||||
icon: 'https://www.9tv.co.il/download/pictures/img_id=8484.jpg',
|
||||
description:
|
||||
'Она не очень любит говорить о себе или о том, кто и зачем к ней обращается. Живет уединенно, в глуши. Но тех, кто приходит -принимает. Она видит судьбы.'
|
||||
|
@ -40,7 +41,7 @@ it('can parse response', () => {
|
|||
start: '2022-03-06T07:10:00.000Z',
|
||||
stop: '2022-03-06T08:10:00.000Z',
|
||||
icon: 'https://www.9tv.co.il/download/pictures/img_id=23694.jpg',
|
||||
title: `Орел и решка. Морской сезон`,
|
||||
title: 'Орел и решка. Морской сезон',
|
||||
description: 'Орел и решка. Морской сезон. Ведущие -Алина Астровская и Коля Серга.'
|
||||
}
|
||||
])
|
||||
|
@ -50,7 +51,7 @@ it('can handle empty guide', () => {
|
|||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: `<!DOCTYPE html><html><head></head><body></body></html>`
|
||||
content: '<!DOCTYPE html><html><head></head><body></body></html>'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -1,37 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="abc.net.au">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="10Bold.au" site_id="ONE">10 Bold</channel>
|
||||
<channel lang="en" xmltv_id="10Peach.au" site_id="11">10 Peach</channel>
|
||||
<channel lang="en" xmltv_id="10Shake.au" site_id="SHAKE">10 Shake</channel>
|
||||
<channel lang="en" xmltv_id="7flix.au" site_id="7flix">7flix</channel>
|
||||
<channel lang="en" xmltv_id="7mate.au" site_id="7MATE">7mate</channel>
|
||||
<channel lang="en" xmltv_id="7two.au" site_id="7TWO">7two</channel>
|
||||
<channel lang="en" xmltv_id="9Gem.au" site_id="GEM">9 Gem</channel>
|
||||
<channel lang="en" xmltv_id="9Go.au" site_id="GO">9 Go!</channel>
|
||||
<channel lang="en" xmltv_id="9Life.au" site_id="9Life">9 Life</channel>
|
||||
<channel lang="en" xmltv_id="9Rush.au" site_id="9Rush">9 Rush</channel>
|
||||
<channel lang="en" xmltv_id="ABCKids.au" site_id="ABC4KIDS">ABC Kids</channel>
|
||||
<channel lang="en" xmltv_id="ABCMe.au" site_id="ABC3">ABC ME</channel>
|
||||
<channel lang="en" xmltv_id="ABCNewsAustralia.au" site_id="ABCN">ABC News</channel>
|
||||
<channel lang="en" xmltv_id="ABCTV.au" site_id="ABC1">ABC TV</channel>
|
||||
<channel lang="en" xmltv_id="ABCTVPlus.au" site_id="ABC2">ABC TV Plus</channel>
|
||||
<channel lang="en" xmltv_id="Channel10.au" site_id="10">Channel 10</channel>
|
||||
<channel lang="en" xmltv_id="Channel7.au" site_id="7">Channel 7</channel>
|
||||
<channel lang="en" xmltv_id="Channel9.au" site_id="9">Channel 9</channel>
|
||||
<channel lang="en" xmltv_id="NITV.au" site_id="NITV">NITV</channel>
|
||||
<channel lang="en" xmltv_id="Racingcom.au" site_id="RTV">Racing.com</channel>
|
||||
<channel lang="en" xmltv_id="SBS.au" site_id="SBS">SBS One</channel>
|
||||
<channel lang="en" xmltv_id="SBSFood.au" site_id="SBS3">SBS Food</channel>
|
||||
<channel lang="en" xmltv_id="SBSViceland.au" site_id="VICHD">SBS Viceland</channel>
|
||||
<channel lang="en" xmltv_id="SBSWorldMovies.au" site_id="SBS2">SBS World Movies</channel>
|
||||
<channel lang="en" xmltv_id="SBSWorldWatch.au" site_id="SBSWW">SBS World Watch</channel>
|
||||
<channel lang="en" xmltv_id="SpreeTV.au" site_id="SPREE">Spree TV</channel>
|
||||
<channel lang="en" xmltv_id="TVSN.au" site_id="TVSN">TSVN</channel>
|
||||
<!-- <channel lang="en" xmltv_id="ABCTV.au" site_id="ABCHD">ABC TV HD</channel> -->
|
||||
<!-- <channel lang="en" xmltv_id="Channel10.au" site_id="TENHD">Channel 10 HD</channel> -->
|
||||
<!-- <channel lang="en" xmltv_id="Channel7.au" site_id="7HD">Channel 7 HD</channel> -->
|
||||
<!-- <channel lang="en" xmltv_id="Channel9.au" site_id="9HD">Channel 9 HD</channel> -->
|
||||
<!-- <channel lang="en" xmltv_id="SBS.au" site_id="SBSHD">SBS HD</channel> -->
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<!-- <channel site="abc.net.au" lang="en" xmltv_id="ABCTV.au" site_id="ABCHD">ABC TV HD</channel> -->
|
||||
<!-- <channel site="abc.net.au" lang="en" xmltv_id="Channel10.au" site_id="TENHD">Channel 10 HD</channel> -->
|
||||
<!-- <channel site="abc.net.au" lang="en" xmltv_id="Channel7.au" site_id="7HD">Channel 7 HD</channel> -->
|
||||
<!-- <channel site="abc.net.au" lang="en" xmltv_id="Channel9.au" site_id="9HD">Channel 9 HD</channel> -->
|
||||
<!-- <channel site="abc.net.au" lang="en" xmltv_id="SBS.au" site_id="SBSHD">SBS HD</channel> -->
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="10Bold.au" site_id="ONE">10 Bold</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="10Peach.au" site_id="11">10 Peach</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="10Shake.au" site_id="SHAKE">10 Shake</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="7flix.au" site_id="7flix">7flix</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="7mate.au" site_id="7MATE">7mate</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="7two.au" site_id="7TWO">7two</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="9Gem.au" site_id="GEM">9 Gem</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="9Go.au" site_id="GO">9 Go!</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="9Life.au" site_id="9Life">9 Life</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="9Rush.au" site_id="9Rush">9 Rush</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="ABCKids.au" site_id="ABC4KIDS">ABC Kids</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="ABCMe.au" site_id="ABC3">ABC ME</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="ABCNewsAustralia.au" site_id="ABCN">ABC News</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="ABCTV.au" site_id="ABC1">ABC TV</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="ABCTVPlus.au" site_id="ABC2">ABC TV Plus</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="Channel10.au" site_id="10">Channel 10</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="Channel7.au" site_id="7">Channel 7</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="Channel9.au" site_id="9">Channel 9</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="NITV.au" site_id="NITV">NITV</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="Racingcom.au" site_id="RTV">Racing.com</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="SBS.au" site_id="SBS">SBS One</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="SBSFood.au" site_id="SBS3">SBS Food</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="SBSViceland.au" site_id="VICHD">SBS Viceland</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="SBSWorldMovies.au" site_id="SBS2">SBS World Movies</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="SBSWorldWatch.au" site_id="SBSWW">SBS World Watch</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="SpreeTV.au" site_id="SPREE">Spree TV</channel>
|
||||
<channel site="abc.net.au" lang="en" xmltv_id="TVSN.au" site_id="TVSN">TSVN</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// npx epg-grabber --config=sites/abc.net.au/abc.net.au.config.js --channels=sites/abc.net.au/abc.net.au.channels.xml --output=guide.xml --days=2
|
||||
// npm run grab -- --site=abc.net.au
|
||||
|
||||
const { parser, url } = require('./abc.net.au.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
|
@ -15,7 +15,8 @@ it('can generate valid url', () => {
|
|||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `{"date":"2022-12-22","region":"Sydney","schedule":[{"channel":"ABC1","listing":[{"consumer_advice":"Adult Themes, Drug Use, Violence","rating":"M","show_id":912747,"repeat":true,"description":"When tragedy strikes close to home, it puts head teacher Noah Taylor on a collision course with the criminals responsible. Can the Lyell team help him stop the cycle of violence?","title":"Silent Witness","crid":"ZW2178A004S00","start_time":"2022-12-22T00:46:00","series-crid":"ZW2178A","live":false,"captioning":true,"show_type":"Episode","series_num":22,"episode_title":"Lift Up Your Hearts (part Two)","length":58,"onair_title":"Silent Witness","end_time":"2022-12-22T01:44:00","genres":["Entertainment"],"image_file":"ZW2178A004S00_460.jpg","prog_slug":"silent-witness","episode_num":4}]}]}`
|
||||
const content =
|
||||
'{"date":"2022-12-22","region":"Sydney","schedule":[{"channel":"ABC1","listing":[{"consumer_advice":"Adult Themes, Drug Use, Violence","rating":"M","show_id":912747,"repeat":true,"description":"When tragedy strikes close to home, it puts head teacher Noah Taylor on a collision course with the criminals responsible. Can the Lyell team help him stop the cycle of violence?","title":"Silent Witness","crid":"ZW2178A004S00","start_time":"2022-12-22T00:46:00","series-crid":"ZW2178A","live":false,"captioning":true,"show_type":"Episode","series_num":22,"episode_title":"Lift Up Your Hearts (part Two)","length":58,"onair_title":"Silent Witness","end_time":"2022-12-22T01:44:00","genres":["Entertainment"],"image_file":"ZW2178A004S00_460.jpg","prog_slug":"silent-witness","episode_num":4}]}]}'
|
||||
|
||||
const result = parser({ content, channel }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
|
@ -27,7 +28,8 @@ it('can parse response', () => {
|
|||
{
|
||||
title: 'Silent Witness',
|
||||
sub_title: 'Lift Up Your Hearts (part Two)',
|
||||
description: `When tragedy strikes close to home, it puts head teacher Noah Taylor on a collision course with the criminals responsible. Can the Lyell team help him stop the cycle of violence?`,
|
||||
description:
|
||||
'When tragedy strikes close to home, it puts head teacher Noah Taylor on a collision course with the criminals responsible. Can the Lyell team help him stop the cycle of violence?',
|
||||
category: ['Entertainment'],
|
||||
rating: {
|
||||
system: 'ACB',
|
||||
|
@ -45,7 +47,8 @@ it('can parse response', () => {
|
|||
it('can handle empty guide', () => {
|
||||
const result = parser(
|
||||
{
|
||||
content: `<Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message><Key>processed/Sydney_2023-01-17.json</Key><RequestId>6MRHX5TJ12X39B3Y</RequestId><HostId>59rH6XRMrmkFywg8Kv58iqpI6O1fuOCuEbKa1HRRYa4buByXMBTvAhz8zuAK7X5D+ZN9ZuWxyGs=</HostId></Error>`
|
||||
content:
|
||||
'<Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message><Key>processed/Sydney_2023-01-17.json</Key><RequestId>6MRHX5TJ12X39B3Y</RequestId><HostId>59rH6XRMrmkFywg8Kv58iqpI6O1fuOCuEbKa1HRRYa4buByXMBTvAhz8zuAK7X5D+ZN9ZuWxyGs=</HostId></Error>'
|
||||
},
|
||||
channel
|
||||
)
|
||||
|
|
|
@ -47,7 +47,7 @@ module.exports = {
|
|||
}
|
||||
|
||||
function parseItems(content, channel) {
|
||||
const [_, channelId] = channel.site_id.split('#')
|
||||
const [, channelId] = channel.site_id.split('#')
|
||||
const data = JSON.parse(content)
|
||||
if (!data || !Array.isArray(data.channels)) return []
|
||||
const channelData = data.channels.find(i => i.id === channelId)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// node ./scripts/channels.js --config=./sites/allente.se/allente.se.config.js --output=./sites/allente.se/allente.se_se.channels.xml --set=country:se --set=lang:sv
|
||||
// node ./scripts/channels.js --config=./sites/allente.se/allente.se.config.js --output=./sites/allente.se/allente.se_fi.channels.xml --set=country:fi --set=lang:fi
|
||||
// node ./scripts/channels.js --config=./sites/allente.se/allente.se.config.js --output=./sites/allente.se/allente.se_no.channels.xml --set=country:no --set=lang:no
|
||||
// node ./scripts/channels.js --config=./sites/allente.se/allente.se.config.js --output=./sites/allente.se/allente.se_dk.channels.xml --set=country:dk --set=lang:da
|
||||
// npx epg-grabber --config=sites/allente.se/allente.se.config.js --channels=sites/allente.se/allente.se_se.channels.xml --output=guide.xml --days=2
|
||||
// npm run channels:parse -- --config=./sites/allente.se/allente.se.config.js --output=./sites/allente.se/allente.se_se.channels.xml --set=country:se --set=lang:sv
|
||||
// npm run channels:parse -- --config=./sites/allente.se/allente.se.config.js --output=./sites/allente.se/allente.se_fi.channels.xml --set=country:fi --set=lang:fi
|
||||
// npm run channels:parse -- --config=./sites/allente.se/allente.se.config.js --output=./sites/allente.se/allente.se_no.channels.xml --set=country:no --set=lang:no
|
||||
// npm run channels:parse -- --config=./sites/allente.se/allente.se.config.js --output=./sites/allente.se/allente.se_dk.channels.xml --set=country:dk --set=lang:da
|
||||
// npm run grab -- --site=allente.se
|
||||
|
||||
const { parser, url } = require('./allente.se.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
|
@ -29,7 +29,8 @@ it('can generate valid url for different country', () => {
|
|||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `{"channels":[{"id":"0148","icon":"//images.ctfassets.net/989y85n5kcxs/5uT9g9pdQWRZeDPQXVI9g6/9cc44da567f591822ed645c99ecdcb64/SVT_1_black_new__2_.png","name":"SVT1 HD (T)","events":[{"id":"0086202208220710","live":false,"time":"2022-08-22T07:10:00Z","title":"Hemmagympa med Sofia","details":{"title":"Hemmagympa med Sofia","image":"https://viasatps.api.comspace.se/PS/channeldate/image/viasat.ps/21/2022-08-22/se.cs.svt1.event.A_41214031600.jpg?size=2560x1440","description":"Svenskt träningsprogram från 2021. Styrka. Sofia Åhman leder SVT:s hemmagympapass. Denna gång fokuserar vi på styrka.","season":4,"episode":1,"categories":["other"],"duration":"20"}}]}]}`
|
||||
const content =
|
||||
'{"channels":[{"id":"0148","icon":"//images.ctfassets.net/989y85n5kcxs/5uT9g9pdQWRZeDPQXVI9g6/9cc44da567f591822ed645c99ecdcb64/SVT_1_black_new__2_.png","name":"SVT1 HD (T)","events":[{"id":"0086202208220710","live":false,"time":"2022-08-22T07:10:00Z","title":"Hemmagympa med Sofia","details":{"title":"Hemmagympa med Sofia","image":"https://viasatps.api.comspace.se/PS/channeldate/image/viasat.ps/21/2022-08-22/se.cs.svt1.event.A_41214031600.jpg?size=2560x1440","description":"Svenskt träningsprogram från 2021. Styrka. Sofia Åhman leder SVT:s hemmagympapass. Denna gång fokuserar vi på styrka.","season":4,"episode":1,"categories":["other"],"duration":"20"}}]}]}'
|
||||
const result = parser({ content, channel }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
|
@ -40,9 +41,10 @@ it('can parse response', () => {
|
|||
{
|
||||
start: '2022-08-22T07:10:00.000Z',
|
||||
stop: '2022-08-22T07:30:00.000Z',
|
||||
title: `Hemmagympa med Sofia`,
|
||||
title: 'Hemmagympa med Sofia',
|
||||
category: ['other'],
|
||||
description: `Svenskt träningsprogram från 2021. Styrka. Sofia Åhman leder SVT:s hemmagympapass. Denna gång fokuserar vi på styrka.`,
|
||||
description:
|
||||
'Svenskt träningsprogram från 2021. Styrka. Sofia Åhman leder SVT:s hemmagympapass. Denna gång fokuserar vi på styrka.',
|
||||
icon: 'https://viasatps.api.comspace.se/PS/channeldate/image/viasat.ps/21/2022-08-22/se.cs.svt1.event.A_41214031600.jpg?size=2560x1440',
|
||||
season: 4,
|
||||
episode: 1
|
||||
|
@ -54,7 +56,7 @@ it('can handle empty guide', () => {
|
|||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: `{"date":"2001-11-17","categories":[],"channels":[]}`
|
||||
content: '{"date":"2001-11-17","categories":[],"channels":[]}'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -1,66 +1,64 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="allente.se">
|
||||
<channels>
|
||||
<channel lang="da" xmltv_id="6eren.dk" site_id="dk#568">6'eren</channel>
|
||||
<channel lang="da" xmltv_id="BoomerangNordic.uk" site_id="dk#0017">Boomerang Nordic</channel>
|
||||
<channel lang="da" xmltv_id="Canal9.dk" site_id="dk#0368">Canal 9</channel>
|
||||
<channel lang="da" xmltv_id="CartoonNetworkScandinavia.uk" site_id="dk#0028">Cartoon Network Nordic</channel>
|
||||
<channel lang="da" xmltv_id="CMoreFirst.se" site_id="dk#968">C More First</channel>
|
||||
<channel lang="da" xmltv_id="CMoreHits.se" site_id="dk#969">C More Hits</channel>
|
||||
<channel lang="da" xmltv_id="CMoreSeries.se" site_id="dk#971">C More Series</channel>
|
||||
<channel lang="da" xmltv_id="CMoreStars.se" site_id="dk#970">C More Stars</channel>
|
||||
<channel lang="da" xmltv_id="DisneyChannelScandinavia.uk" site_id="dk#0037">Disney Channel Scandinavia</channel>
|
||||
<channel lang="da" xmltv_id="DisneyJuniorScandinavia.uk" site_id="dk#0307">Disney Junior Scandinavia</channel>
|
||||
<channel lang="da" xmltv_id="dk4.dk" site_id="dk#0376">DK 4</channel>
|
||||
<channel lang="da" xmltv_id="DR1.dk" site_id="dk#452">DR 1</channel>
|
||||
<channel lang="da" xmltv_id="DR2.dk" site_id="dk#0051">DR 2</channel>
|
||||
<channel lang="da" xmltv_id="DRRamasjang.dk" site_id="dk#0048">DR Ramasjang</channel>
|
||||
<channel lang="da" xmltv_id="EEurope.us" site_id="dk#0052">E! Europe</channel>
|
||||
<channel lang="da" xmltv_id="EuronewsEnglish.fr" site_id="dk#0281">EuroNews English</channel>
|
||||
<channel lang="da" xmltv_id="Eurosport2Danmark.dk" site_id="dk#0367">Eurosport 2 Danmark</channel>
|
||||
<channel lang="da" xmltv_id="GodTV.uk" site_id="dk#0058">God TV Scandinavia</channel>
|
||||
<channel lang="da" xmltv_id="Kanal4.dk" site_id="dk#0064">Kanal 4</channel>
|
||||
<channel lang="da" xmltv_id="Kanal5.dk" site_id="dk#0065">Kanal 5</channel>
|
||||
<channel lang="da" xmltv_id="MTV00s.uk" site_id="dk#0246">MTV 00s</channel>
|
||||
<channel lang="da" xmltv_id="MTV80s.uk" site_id="dk#604">MTV 80s</channel>
|
||||
<channel lang="da" xmltv_id="MTVGlobal.uk" site_id="dk#0076">MTV Nordic</channel>
|
||||
<channel lang="da" xmltv_id="MTVHitsEurope.uk" site_id="dk#0077">MTV Hits Europe</channel>
|
||||
<channel lang="da" xmltv_id="NationalGeographicDenmark.dk" site_id="dk#0317">National Geographic Danmark</channel>
|
||||
<channel lang="da" xmltv_id="NationalGeographicWildDenmark.dk" site_id="dk#0082">National Geographic Wild Europe</channel>
|
||||
<channel lang="da" xmltv_id="NickelodeonDenmark.dk" site_id="dk#0087">Nickelodeon Danmark</channel>
|
||||
<channel lang="da" xmltv_id="NickJrScandinavia.nl" site_id="dk#0088">Nick Jr Scandinavia</channel>
|
||||
<channel lang="da" xmltv_id="NicktoonsScandinavia.nl" site_id="dk#570">Nicktoons Scandinavia</channel>
|
||||
<channel lang="da" xmltv_id="NRK1.no" site_id="dk#0090">NRK1</channel>
|
||||
<channel lang="da" xmltv_id="ParamountNetworkDenmark.dk" site_id="dk#450">Paramount Network Danmark</channel>
|
||||
<channel lang="da" xmltv_id="SFkanalen.se" site_id="dk#972">SF-kanalen</channel>
|
||||
<channel lang="da" xmltv_id="SkyNewsInternational.uk" site_id="dk#0008">Sky News International</channel>
|
||||
<channel lang="da" xmltv_id="SVT1.se" site_id="dk#0121">SVT 1</channel>
|
||||
<channel lang="da" xmltv_id="TV2.dk" site_id="dk#0297">TV 2</channel>
|
||||
<channel lang="da" xmltv_id="TV2Charlie.dk" site_id="dk#0180">TV 2 Charlie</channel>
|
||||
<channel lang="da" xmltv_id="TV2Fri.dk" site_id="dk#0378">TV 2 Fri</channel>
|
||||
<channel lang="da" xmltv_id="TV2News.dk" site_id="dk#0190">TV 2 News</channel>
|
||||
<channel lang="da" xmltv_id="TV2Sport.dk" site_id="dk#454">TV 2 Sport</channel>
|
||||
<channel lang="da" xmltv_id="TV2Zulu.dk" site_id="dk#0209">TV 2 Zulu</channel>
|
||||
<channel lang="da" xmltv_id="TV3Danmark.dk" site_id="dk#0359">TV 3 Danmark</channel>
|
||||
<channel lang="da" xmltv_id="TV3Max.dk" site_id="dk#0374">TV 3 Max</channel>
|
||||
<channel lang="da" xmltv_id="TV3Plus.dk" site_id="dk#0248">TV3+</channel>
|
||||
<channel lang="da" xmltv_id="TV3Puls.dk" site_id="dk#665">TV 3 Puls</channel>
|
||||
<channel lang="da" xmltv_id="TV3SportDenmark.dk" site_id="dk#0200">TV 3 Sport</channel>
|
||||
<channel lang="da" xmltv_id="TV4.se" site_id="dk#0227">TV 4</channel>
|
||||
<channel lang="da" xmltv_id="VFilmAction.se" site_id="dk#0299">V Film Action</channel>
|
||||
<channel lang="da" xmltv_id="VFilmFamily.se" site_id="dk#0308">V Film Family</channel>
|
||||
<channel lang="da" xmltv_id="VFilmHits.se" site_id="dk#0322">V Film Hits</channel>
|
||||
<channel lang="da" xmltv_id="VFilmPremiere.se" site_id="dk#0321">V Film Premiere</channel>
|
||||
<channel lang="da" xmltv_id="ViasatExplore.se" site_id="dk#0358">Viasat Explore</channel>
|
||||
<channel lang="da" xmltv_id="ViasatHistory.se" site_id="dk#0357">Viasat History HD</channel>
|
||||
<channel lang="da" xmltv_id="ViasatNature.se" site_id="dk#0250">Viasat Nature</channel>
|
||||
<channel lang="da" xmltv_id="VSeries.se" site_id="dk#0320">V Series</channel>
|
||||
<channel lang="da" xmltv_id="VSportGolf.se" site_id="dk#0364">V Sport Golf</channel>
|
||||
<channel lang="da" xmltv_id="VSportUltraHD.se" site_id="dk#418">V Sport Ultra HD</channel>
|
||||
<channel lang="da" xmltv_id="Xee.dk" site_id="dk#707">Xee</channel>
|
||||
<channel lang="en" xmltv_id="AlJazeeraEnglish.qa" site_id="dk#0344">Aljazeera English</channel>
|
||||
<channel lang="en" xmltv_id="BBCWorldNewsEurope.uk" site_id="dk#0016">BBC World News Europe</channel>
|
||||
<channel lang="en" xmltv_id="CNBCEurope.uk" site_id="dk#0032">CNBC Europe</channel>
|
||||
<channel lang="en" xmltv_id="CNNInternationalEurope.us" site_id="dk#0033">CNN International Europe</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="allente.se" lang="da" xmltv_id="6eren.dk" site_id="dk#568">6'eren</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="BoomerangNordic.uk" site_id="dk#0017">Boomerang Nordic</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="Canal9.dk" site_id="dk#0368">Canal 9</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="CartoonNetworkScandinavia.uk" site_id="dk#0028">Cartoon Network Nordic</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="CMoreFirst.se" site_id="dk#968">C More First</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="CMoreHits.se" site_id="dk#969">C More Hits</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="CMoreSeries.se" site_id="dk#971">C More Series</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="CMoreStars.se" site_id="dk#970">C More Stars</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="DisneyChannelScandinavia.uk" site_id="dk#0037">Disney Channel Scandinavia</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="DisneyJuniorScandinavia.uk" site_id="dk#0307">Disney Junior Scandinavia</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="dk4.dk" site_id="dk#0376">DK 4</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="DR1.dk" site_id="dk#452">DR 1</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="DR2.dk" site_id="dk#0051">DR 2</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="DRRamasjang.dk" site_id="dk#0048">DR Ramasjang</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="EEurope.us" site_id="dk#0052">E! Europe</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="EuronewsEnglish.fr" site_id="dk#0281">EuroNews English</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="Eurosport2Danmark.dk" site_id="dk#0367">Eurosport 2 Danmark</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="GodTV.uk" site_id="dk#0058">God TV Scandinavia</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="Kanal4.dk" site_id="dk#0064">Kanal 4</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="Kanal5.dk" site_id="dk#0065">Kanal 5</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="MTV00s.uk" site_id="dk#0246">MTV 00s</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="MTV80s.uk" site_id="dk#604">MTV 80s</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="MTVGlobal.uk" site_id="dk#0076">MTV Nordic</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="MTVHitsEurope.uk" site_id="dk#0077">MTV Hits Europe</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="NationalGeographicDenmark.dk" site_id="dk#0317">National Geographic Danmark</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="NationalGeographicWildDenmark.dk" site_id="dk#0082">National Geographic Wild Europe</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="NickelodeonDenmark.dk" site_id="dk#0087">Nickelodeon Danmark</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="NickJrScandinavia.nl" site_id="dk#0088">Nick Jr Scandinavia</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="NicktoonsScandinavia.nl" site_id="dk#570">Nicktoons Scandinavia</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="NRK1.no" site_id="dk#0090">NRK1</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="ParamountNetworkDenmark.dk" site_id="dk#450">Paramount Network Danmark</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="SFkanalen.se" site_id="dk#972">SF-kanalen</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="SkyNewsInternational.uk" site_id="dk#0008">Sky News International</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="SVT1.se" site_id="dk#0121">SVT 1</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV2.dk" site_id="dk#0297">TV 2</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV2Charlie.dk" site_id="dk#0180">TV 2 Charlie</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV2Fri.dk" site_id="dk#0378">TV 2 Fri</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV2News.dk" site_id="dk#0190">TV 2 News</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV2Sport.dk" site_id="dk#454">TV 2 Sport</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV2Zulu.dk" site_id="dk#0209">TV 2 Zulu</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV3Danmark.dk" site_id="dk#0359">TV 3 Danmark</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV3Max.dk" site_id="dk#0374">TV 3 Max</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV3Plus.dk" site_id="dk#0248">TV3+</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV3Puls.dk" site_id="dk#665">TV 3 Puls</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV3SportDenmark.dk" site_id="dk#0200">TV 3 Sport</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="TV4.se" site_id="dk#0227">TV 4</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="VFilmAction.se" site_id="dk#0299">V Film Action</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="VFilmFamily.se" site_id="dk#0308">V Film Family</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="VFilmHits.se" site_id="dk#0322">V Film Hits</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="VFilmPremiere.se" site_id="dk#0321">V Film Premiere</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="ViasatExplore.se" site_id="dk#0358">Viasat Explore</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="ViasatHistory.se" site_id="dk#0357">Viasat History HD</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="ViasatNature.se" site_id="dk#0250">Viasat Nature</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="VSeries.se" site_id="dk#0320">V Series</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="VSportGolf.se" site_id="dk#0364">V Sport Golf</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="VSportUltraHD.se" site_id="dk#418">V Sport Ultra HD</channel>
|
||||
<channel site="allente.se" lang="da" xmltv_id="Xee.dk" site_id="dk#707">Xee</channel>
|
||||
<channel site="allente.se" lang="en" xmltv_id="AlJazeeraEnglish.qa" site_id="dk#0344">Aljazeera English</channel>
|
||||
<channel site="allente.se" lang="en" xmltv_id="BBCWorldNewsEurope.uk" site_id="dk#0016">BBC World News Europe</channel>
|
||||
<channel site="allente.se" lang="en" xmltv_id="CNBCEurope.uk" site_id="dk#0032">CNBC Europe</channel>
|
||||
<channel site="allente.se" lang="en" xmltv_id="CNNInternationalEurope.us" site_id="dk#0033">CNN International Europe</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,40 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="allente.se">
|
||||
<channels>
|
||||
<channel lang="fi" xmltv_id="BoomerangNordic.uk" site_id="fi#0017">Boomerang</channel>
|
||||
<channel lang="fi" xmltv_id="CartoonNetworkScandinavia.uk" site_id="fi#0028">Cartoon Network</channel>
|
||||
<channel lang="fi" xmltv_id="CNBCEurope.uk" site_id="fi#0032">CNBC</channel>
|
||||
<channel lang="fi" xmltv_id="CNNInternationalEurope.us" site_id="fi#0033">CNN</channel>
|
||||
<channel lang="fi" xmltv_id="DisneyChannelScandinavia.uk" site_id="fi#0037">Disney Channel</channel>
|
||||
<channel lang="fi" xmltv_id="DisneyJuniorScandinavia.uk" site_id="fi#0307">Disney Junior</channel>
|
||||
<channel lang="fi" xmltv_id="EEurope.us" site_id="fi#0052">E!</channel>
|
||||
<channel lang="fi" xmltv_id="MTV00s.uk" site_id="fi#0246">MTV 00s</channel>
|
||||
<channel lang="fi" xmltv_id="MTVGlobal.uk" site_id="fi#0080">MTV</channel>
|
||||
<channel lang="fi" xmltv_id="NationalGeographicFinland.fi" site_id="fi#0084">National Geographic</channel>
|
||||
<channel lang="fi" xmltv_id="NationalGeographicWildFinland.fi" site_id="fi#558">National Geographic Wild</channel>
|
||||
<channel lang="fi" xmltv_id="NickJrScandinavia.nl" site_id="fi#0088">Nick Jr</channel>
|
||||
<channel lang="fi" xmltv_id="TV3.se" site_id="fi#0290">TV 3 Sverige</channel>
|
||||
<channel lang="fi" xmltv_id="TV6Sweden.se" site_id="fi#0360">TV 6 Sverige</channel>
|
||||
<channel lang="fi" xmltv_id="VFilmAction.se" site_id="fi#0299">V Film Action</channel>
|
||||
<channel lang="fi" xmltv_id="VFilmFamily.se" site_id="fi#0308">V Film Family</channel>
|
||||
<channel lang="fi" xmltv_id="VFilmHits.se" site_id="fi#0322">V Film Hits</channel>
|
||||
<channel lang="fi" xmltv_id="VFilmPremiere.se" site_id="fi#0321">V Film Premiere</channel>
|
||||
<channel lang="fi" xmltv_id="ViasatExplore.se" site_id="fi#0252">Viasat Explore</channel>
|
||||
<channel lang="fi" xmltv_id="ViasatHistory.se" site_id="fi#0263">Viasat History HD</channel>
|
||||
<channel lang="fi" xmltv_id="ViasatNature.se" site_id="fi#0250">Viasat Nature</channel>
|
||||
<channel lang="fi" xmltv_id="VSport1Finland.fi" site_id="fi#0159">V Sport 1 Suomi</channel>
|
||||
<channel lang="fi" xmltv_id="VSport1Sweden.se" site_id="fi#0362">V Sport 1 Sverige</channel>
|
||||
<channel lang="fi" xmltv_id="VSport2Finland.fi" site_id="fi#488">V Sport 2 Suomi</channel>
|
||||
<channel lang="fi" xmltv_id="VSportFootball.se" site_id="fi#0269">V Sport Football</channel>
|
||||
<channel lang="fi" xmltv_id="VSportGolf.se" site_id="fi#0364">V Sport Golf</channel>
|
||||
<channel lang="fi" xmltv_id="VSportLive1.se" site_id="fi#0255">V Sport Live 1</channel>
|
||||
<channel lang="fi" xmltv_id="VSportLive2.se" site_id="fi#0256">V Sport Live 2</channel>
|
||||
<channel lang="fi" xmltv_id="VSportLive3.se" site_id="fi#0257">V Sport Live 3</channel>
|
||||
<channel lang="fi" xmltv_id="VSportLive4.se" site_id="fi#0258">V Sport Live 4</channel>
|
||||
<channel lang="fi" xmltv_id="VSportLive5.se" site_id="fi#0259">V Sport Live 5</channel>
|
||||
<channel lang="fi" xmltv_id="VSportPlusFinland.fi" site_id="fi#0369">V Sport + Suomi</channel>
|
||||
<channel lang="fi" xmltv_id="VSportPremium.se" site_id="fi#527">V Sport Premium</channel>
|
||||
<channel lang="fi" xmltv_id="VSportUltraHD.se" site_id="fi#418">V Sport Ultra HD</channel>
|
||||
<channel lang="fi" xmltv_id="VSportVinter.se" site_id="fi#0363">V Sport Vinter</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="BoomerangNordic.uk" site_id="fi#0017">Boomerang</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="CartoonNetworkScandinavia.uk" site_id="fi#0028">Cartoon Network</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="CNBCEurope.uk" site_id="fi#0032">CNBC</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="CNNInternationalEurope.us" site_id="fi#0033">CNN</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="DisneyChannelScandinavia.uk" site_id="fi#0037">Disney Channel</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="DisneyJuniorScandinavia.uk" site_id="fi#0307">Disney Junior</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="EEurope.us" site_id="fi#0052">E!</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="MTV00s.uk" site_id="fi#0246">MTV 00s</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="MTVGlobal.uk" site_id="fi#0080">MTV</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="NationalGeographicFinland.fi" site_id="fi#0084">National Geographic</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="NationalGeographicWildFinland.fi" site_id="fi#558">National Geographic Wild</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="NickJrScandinavia.nl" site_id="fi#0088">Nick Jr</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="TV3.se" site_id="fi#0290">TV 3 Sverige</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="TV6Sweden.se" site_id="fi#0360">TV 6 Sverige</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VFilmAction.se" site_id="fi#0299">V Film Action</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VFilmFamily.se" site_id="fi#0308">V Film Family</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VFilmHits.se" site_id="fi#0322">V Film Hits</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VFilmPremiere.se" site_id="fi#0321">V Film Premiere</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="ViasatExplore.se" site_id="fi#0252">Viasat Explore</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="ViasatHistory.se" site_id="fi#0263">Viasat History HD</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="ViasatNature.se" site_id="fi#0250">Viasat Nature</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSport1Finland.fi" site_id="fi#0159">V Sport 1 Suomi</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSport1Sweden.se" site_id="fi#0362">V Sport 1 Sverige</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSport2Finland.fi" site_id="fi#488">V Sport 2 Suomi</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportFootball.se" site_id="fi#0269">V Sport Football</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportGolf.se" site_id="fi#0364">V Sport Golf</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportLive1.se" site_id="fi#0255">V Sport Live 1</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportLive2.se" site_id="fi#0256">V Sport Live 2</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportLive3.se" site_id="fi#0257">V Sport Live 3</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportLive4.se" site_id="fi#0258">V Sport Live 4</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportLive5.se" site_id="fi#0259">V Sport Live 5</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportPlusFinland.fi" site_id="fi#0369">V Sport + Suomi</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportPremium.se" site_id="fi#527">V Sport Premium</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportUltraHD.se" site_id="fi#418">V Sport Ultra HD</channel>
|
||||
<channel site="allente.se" lang="fi" xmltv_id="VSportVinter.se" site_id="fi#0363">V Sport Vinter</channel>
|
||||
</channels>
|
|
@ -1,75 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="allente.se">
|
||||
<channels>
|
||||
<channel lang="no" xmltv_id="AlJazeeraEnglish.qa" site_id="no#0344">Aljazeera</channel>
|
||||
<channel lang="no" xmltv_id="BBCWorldNewsEurope.uk" site_id="no#0016">BBC World News</channel>
|
||||
<channel lang="no" xmltv_id="CartoonNetworkScandinavia.uk" site_id="no#0028">Cartoon Network</channel>
|
||||
<channel lang="no" xmltv_id="CNBCEurope.uk" site_id="no#0032">CNBC</channel>
|
||||
<channel lang="no" xmltv_id="CNNInternationalEurope.us" site_id="no#0033">CNN</channel>
|
||||
<channel lang="no" xmltv_id="DiscoveryChannelNorway.no" site_id="no#532">Discovery Channel</channel>
|
||||
<channel lang="no" xmltv_id="DisneyChannelScandinavia.uk" site_id="no#0037">Disney Channel</channel>
|
||||
<channel lang="no" xmltv_id="DisneyJuniorScandinavia.uk" site_id="no#0307">Disney Junior</channel>
|
||||
<channel lang="no" xmltv_id="DR2.dk" site_id="no#0051">DR 2</channel>
|
||||
<channel lang="no" xmltv_id="EEurope.us" site_id="no#0052">E!</channel>
|
||||
<channel lang="no" xmltv_id="EuronewsEnglish.fr" site_id="no#0281">EuroNews</channel>
|
||||
<channel lang="no" xmltv_id="Eurosport1Norway.no" site_id="no#531">Eurosport 1</channel>
|
||||
<channel lang="no" xmltv_id="EurosportNorway.no" site_id="no#530">Eurosport</channel>
|
||||
<channel lang="no" xmltv_id="FEM.no" site_id="no#0056">FEM</channel>
|
||||
<channel lang="no" xmltv_id="Kunskapskanalen.se" site_id="no#0149">Kunskapskanalen</channel>
|
||||
<channel lang="no" xmltv_id="Matkanalen.no" site_id="no#565">Matkanalen</channel>
|
||||
<channel lang="no" xmltv_id="MAX.no" site_id="no#533">Max</channel>
|
||||
<channel lang="no" xmltv_id="MTV00s.uk" site_id="no#0246">MTV 00s</channel>
|
||||
<channel lang="no" xmltv_id="MTV80s.uk" site_id="no#604">MTV 80s</channel>
|
||||
<channel lang="no" xmltv_id="MTVHitsEurope.uk" site_id="no#0077">MTV Hits</channel>
|
||||
<channel lang="no" xmltv_id="MTVGlobal.uk" site_id="no#0080">MTV Nordic</channel>
|
||||
<channel lang="no" xmltv_id="NationalGeographicNorway.no" site_id="no#0316">National Geographic</channel>
|
||||
<channel lang="no" xmltv_id="NationalGeographicWildNorway.no" site_id="no#558">National Geographic Wild</channel>
|
||||
<channel lang="no" xmltv_id="NFLNetwork.us" site_id="no#556">NFL Network</channel>
|
||||
<channel lang="no" xmltv_id="NickelodeonNorway.no" site_id="no#0087">Nickelodeon</channel>
|
||||
<channel lang="no" xmltv_id="NickJrScandinavia.nl" site_id="no#0088">Nick Jr</channel>
|
||||
<channel lang="no" xmltv_id="NicktoonsScandinavia.nl" site_id="no#570">Nicktoons</channel>
|
||||
<channel lang="no" xmltv_id="NRK1.no" site_id="no#0090">NRK1</channel>
|
||||
<channel lang="no" xmltv_id="NRK2.no" site_id="no#0288">NRK2</channel>
|
||||
<channel lang="no" xmltv_id="NRK3.no" site_id="no#0289">NRK3</channel>
|
||||
<channel lang="no" xmltv_id="SkyNewsInternational.uk" site_id="no#0008">Sky News International</channel>
|
||||
<channel lang="no" xmltv_id="SVT1.se" site_id="no#0121">SVT 1</channel>
|
||||
<channel lang="no" xmltv_id="SVT2.se" site_id="no#0141">SVT 2</channel>
|
||||
<channel lang="no" xmltv_id="SVT24.se" site_id="no#598">SVT 24</channel>
|
||||
<channel lang="no" xmltv_id="SVTBarn.se" site_id="no#0147">SVT Barn</channel>
|
||||
<channel lang="no" xmltv_id="TV2.dk" site_id="no#0188">TV 2</channel>
|
||||
<channel lang="no" xmltv_id="TV2.no" site_id="no#0187">TV 2</channel>
|
||||
<channel lang="no" xmltv_id="TV2Livsstil.no" site_id="no#0277">TV 2 Livsstil</channel>
|
||||
<channel lang="no" xmltv_id="TV2Nyhetskanalen.no" site_id="no#457">TV 2 Nyhetskanalen</channel>
|
||||
<channel lang="no" xmltv_id="TV2Sport1.no" site_id="no#0199">TV 2 Sport 1</channel>
|
||||
<channel lang="no" xmltv_id="TV2Sport2.no" site_id="no#0406">TV 2 Sport 2</channel>
|
||||
<channel lang="no" xmltv_id="TV2SportPremium.no" site_id="no#0197">TV 2 Sport Premium</channel>
|
||||
<channel lang="no" xmltv_id="TV2Zebra.no" site_id="no#0405">TV 2 Zebra</channel>
|
||||
<channel lang="no" xmltv_id="TV3Danmark.dk" site_id="no#0359">TV 3 Danmark</channel>
|
||||
<channel lang="no" xmltv_id="TV3Norway.no" site_id="no#0298">TV 3 Norge</channel>
|
||||
<channel lang="no" xmltv_id="TV3.se" site_id="no#0222">TV 3 Sverige</channel>
|
||||
<channel lang="no" xmltv_id="TV6Norway.no" site_id="no#0206">TV 6 Norge</channel>
|
||||
<channel lang="no" xmltv_id="TV6Sweden.se" site_id="no#0360">TV 6 Sverige</channel>
|
||||
<channel lang="no" xmltv_id="TVNorge.no" site_id="no#534">TV Norge</channel>
|
||||
<channel lang="no" xmltv_id="V4.no" site_id="no#0361">V 4</channel>
|
||||
<channel lang="no" xmltv_id="VFilmAction.se" site_id="no#0299">V Film Action</channel>
|
||||
<channel lang="no" xmltv_id="VFilmFamily.se" site_id="no#0308">V Film Family</channel>
|
||||
<channel lang="no" xmltv_id="VFilmHits.se" site_id="no#0322">V Film Hits</channel>
|
||||
<channel lang="no" xmltv_id="VFilmPremiere.se" site_id="no#0321">V Film Premiere</channel>
|
||||
<channel lang="no" xmltv_id="ViasatExplore.se" site_id="no#0358">Viasat Explore</channel>
|
||||
<channel lang="no" xmltv_id="ViasatHistory.se" site_id="no#0357">Viasat History HD</channel>
|
||||
<channel lang="no" xmltv_id="ViasatNature.se" site_id="no#0250">Viasat Nature</channel>
|
||||
<channel lang="no" xmltv_id="VOX.no" site_id="no#535">Vox</channel>
|
||||
<channel lang="no" xmltv_id="VSeries.se" site_id="no#0320">V Series</channel>
|
||||
<channel lang="no" xmltv_id="VSport1Norway.no" site_id="no#0365">V Sport 1</channel>
|
||||
<channel lang="no" xmltv_id="VSport2.no" site_id="no#608">V Sport 2</channel>
|
||||
<channel lang="no" xmltv_id="VSport3.no" site_id="no#609">V Sport 3</channel>
|
||||
<channel lang="no" xmltv_id="VSportGolf.se" site_id="no#0364">V Sport Golf</channel>
|
||||
<channel lang="no" xmltv_id="VSportLive1.se" site_id="no#0255">V Sport Live 1</channel>
|
||||
<channel lang="no" xmltv_id="VSportLive2.se" site_id="no#0256">V Sport Live 2</channel>
|
||||
<channel lang="no" xmltv_id="VSportLive3.se" site_id="no#0257">V Sport Live 3</channel>
|
||||
<channel lang="no" xmltv_id="VSportLive4.se" site_id="no#0258">V Sport Live 4</channel>
|
||||
<channel lang="no" xmltv_id="VSportLive5.se" site_id="no#0259">V Sport Live 5</channel>
|
||||
<channel lang="no" xmltv_id="VSportPlus.no" site_id="no#0271">V Sport +</channel>
|
||||
<channel lang="no" xmltv_id="VSportUltraHD.se" site_id="no#418">V Sport Ultra HD</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="allente.se" lang="no" xmltv_id="AlJazeeraEnglish.qa" site_id="no#0344">Aljazeera</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="BBCWorldNewsEurope.uk" site_id="no#0016">BBC World News</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="CartoonNetworkScandinavia.uk" site_id="no#0028">Cartoon Network</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="CNBCEurope.uk" site_id="no#0032">CNBC</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="CNNInternationalEurope.us" site_id="no#0033">CNN</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="DiscoveryChannelNorway.no" site_id="no#532">Discovery Channel</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="DisneyChannelScandinavia.uk" site_id="no#0037">Disney Channel</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="DisneyJuniorScandinavia.uk" site_id="no#0307">Disney Junior</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="DR2.dk" site_id="no#0051">DR 2</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="EEurope.us" site_id="no#0052">E!</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="EuronewsEnglish.fr" site_id="no#0281">EuroNews</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="Eurosport1Norway.no" site_id="no#531">Eurosport 1</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="EurosportNorway.no" site_id="no#530">Eurosport</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="FEM.no" site_id="no#0056">FEM</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="Kunskapskanalen.se" site_id="no#0149">Kunskapskanalen</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="Matkanalen.no" site_id="no#565">Matkanalen</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="MAX.no" site_id="no#533">Max</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="MTV00s.uk" site_id="no#0246">MTV 00s</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="MTV80s.uk" site_id="no#604">MTV 80s</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="MTVHitsEurope.uk" site_id="no#0077">MTV Hits</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="MTVGlobal.uk" site_id="no#0080">MTV Nordic</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NationalGeographicNorway.no" site_id="no#0316">National Geographic</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NationalGeographicWildNorway.no" site_id="no#558">National Geographic Wild</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NFLNetwork.us" site_id="no#556">NFL Network</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NickelodeonNorway.no" site_id="no#0087">Nickelodeon</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NickJrScandinavia.nl" site_id="no#0088">Nick Jr</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NicktoonsScandinavia.nl" site_id="no#570">Nicktoons</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NRK1.no" site_id="no#0090">NRK1</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NRK2.no" site_id="no#0288">NRK2</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="NRK3.no" site_id="no#0289">NRK3</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="SkyNewsInternational.uk" site_id="no#0008">Sky News International</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="SVT1.se" site_id="no#0121">SVT 1</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="SVT2.se" site_id="no#0141">SVT 2</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="SVT24.se" site_id="no#598">SVT 24</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="SVTBarn.se" site_id="no#0147">SVT Barn</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV2.dk" site_id="no#0188">TV 2</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV2.no" site_id="no#0187">TV 2</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV2Livsstil.no" site_id="no#0277">TV 2 Livsstil</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV2Nyhetskanalen.no" site_id="no#457">TV 2 Nyhetskanalen</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV2Sport1.no" site_id="no#0199">TV 2 Sport 1</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV2Sport2.no" site_id="no#0406">TV 2 Sport 2</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV2SportPremium.no" site_id="no#0197">TV 2 Sport Premium</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV2Zebra.no" site_id="no#0405">TV 2 Zebra</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV3Danmark.dk" site_id="no#0359">TV 3 Danmark</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV3Norway.no" site_id="no#0298">TV 3 Norge</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV3.se" site_id="no#0222">TV 3 Sverige</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV6Norway.no" site_id="no#0206">TV 6 Norge</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TV6Sweden.se" site_id="no#0360">TV 6 Sverige</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="TVNorge.no" site_id="no#534">TV Norge</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="V4.no" site_id="no#0361">V 4</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VFilmAction.se" site_id="no#0299">V Film Action</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VFilmFamily.se" site_id="no#0308">V Film Family</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VFilmHits.se" site_id="no#0322">V Film Hits</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VFilmPremiere.se" site_id="no#0321">V Film Premiere</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="ViasatExplore.se" site_id="no#0358">Viasat Explore</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="ViasatHistory.se" site_id="no#0357">Viasat History HD</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="ViasatNature.se" site_id="no#0250">Viasat Nature</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VOX.no" site_id="no#535">Vox</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSeries.se" site_id="no#0320">V Series</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSport1Norway.no" site_id="no#0365">V Sport 1</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSport2.no" site_id="no#608">V Sport 2</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSport3.no" site_id="no#609">V Sport 3</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSportGolf.se" site_id="no#0364">V Sport Golf</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSportLive1.se" site_id="no#0255">V Sport Live 1</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSportLive2.se" site_id="no#0256">V Sport Live 2</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSportLive3.se" site_id="no#0257">V Sport Live 3</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSportLive4.se" site_id="no#0258">V Sport Live 4</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSportLive5.se" site_id="no#0259">V Sport Live 5</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSportPlus.no" site_id="no#0271">V Sport +</channel>
|
||||
<channel site="allente.se" lang="no" xmltv_id="VSportUltraHD.se" site_id="no#418">V Sport Ultra HD</channel>
|
||||
</channels>
|
|
@ -1,114 +1,112 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="allente.se">
|
||||
<channels>
|
||||
<channel lang="sv" xmltv_id="AlJazeeraEnglish.qa" site_id="se#0344">Aljazeera</channel>
|
||||
<channel lang="sv" xmltv_id="AnimalPlanetSweden.se" site_id="se#1005">Animal Planet Sverige</channel>
|
||||
<channel lang="sv" xmltv_id="ATGLive.se" site_id="se#1000">ATG Live</channel>
|
||||
<channel lang="sv" xmltv_id="BBCEarthNordic.uk" site_id="se#1018">BBC Earth HD</channel>
|
||||
<channel lang="sv" xmltv_id="BBCBritNordic.uk" site_id="se#1016">BBC Brit HD</channel>
|
||||
<channel lang="sv" xmltv_id="BBCWorldNewsEurope.uk" site_id="se#0016">BBC World News</channel>
|
||||
<channel lang="sv" xmltv_id="BloombergTVEurope.uk" site_id="se#1008">Bloomberg TV</channel>
|
||||
<channel lang="sv" xmltv_id="BoomerangNordic.uk" site_id="se#0017">Boomerang</channel>
|
||||
<channel lang="sv" xmltv_id="CartoonNetworkScandinavia.uk" site_id="se#0028">Cartoon Network</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreFirst.se" site_id="se#968">C More First</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreFotboll.se" site_id="se#657">C More Fotboll</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreHits.se" site_id="se#969">C More Hits</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreHockey.se" site_id="se#656">C More Hockey</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreLive.se" site_id="se#659">C More Live</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreLive2.se" site_id="se#660">C More Live 2</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreLive3.se" site_id="se#661">C More Live 3</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreLive4.se" site_id="se#662">C More Live 4</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreLive5.se" site_id="se#663">C More Live 5</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreMix.se" site_id="se#658">C More Mix</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreSeries.se" site_id="se#971">C More Series</channel>
|
||||
<channel lang="sv" xmltv_id="CMoreStars.se" site_id="se#970">C More Stars</channel>
|
||||
<channel lang="sv" xmltv_id="CNBCEurope.uk" site_id="se#0032">CNBC</channel>
|
||||
<channel lang="sv" xmltv_id="CNNInternationalEurope.us" site_id="se#0033">CNN</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryChannelSweden.se" site_id="se#493">Discovery Channel Sverige</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryScienceSweden.se" site_id="se#1006">Discovery Science Sverige</channel>
|
||||
<channel lang="sv" xmltv_id="DisneyChannelScandinavia.uk" site_id="se#0037">Disney Channel</channel>
|
||||
<channel lang="sv" xmltv_id="DisneyJuniorScandinavia.uk" site_id="se#0307">Disney Junior</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryPlusExtra1.se" site_id="se#637">Discovery+ Extra 1</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryPlusExtra2.se" site_id="se#638">Discovery+ Extra 2</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryPlusExtra3.se" site_id="se#639">Discovery+ Extra 3</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryPlusExtra4.se" site_id="se#640">Discovery+ Extra 4</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryPlusExtra5.se" site_id="se#641">Discovery+ Extra 5</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryPlusExtra6.se" site_id="se#642">Discovery+ Extra 6</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryPlusExtra7.se" site_id="se#643">Discovery+ Extra 7</channel>
|
||||
<channel lang="sv" xmltv_id="DiscoveryPlusExtra8.se" site_id="se#644">Discovery+ Extra 8</channel>
|
||||
<channel lang="sv" xmltv_id="DR1.dk" site_id="se#452">DR 1</channel>
|
||||
<channel lang="sv" xmltv_id="DR2.dk" site_id="se#0051">DR 2</channel>
|
||||
<channel lang="sv" xmltv_id="DRRamasjang.dk" site_id="se#0048">DR Ramasjang</channel>
|
||||
<channel lang="sv" xmltv_id="EEurope.us" site_id="se#0052">E!</channel>
|
||||
<channel lang="sv" xmltv_id="EuronewsEnglish.fr" site_id="se#0281">EuroNews</channel>
|
||||
<channel lang="sv" xmltv_id="Eurosport1.fr" site_id="se#1023">Eurosport 1</channel>
|
||||
<channel lang="sv" xmltv_id="Eurosport2.fr" site_id="se#1024">Eurosport 2</channel>
|
||||
<channel lang="sv" xmltv_id="Godare.se" site_id="se#722">Godare</channel>
|
||||
<channel lang="sv" xmltv_id="GodTV.uk" site_id="se#0058">God TV</channel>
|
||||
<channel lang="sv" xmltv_id="HistorySweden.se" site_id="se#652">History</channel>
|
||||
<channel lang="sv" xmltv_id="History2Nordic.us" site_id="se#1004">H2</channel>
|
||||
<channel lang="sv" xmltv_id="HorseCountryTV.uk" site_id="se#668">Horse & Country TV</channel>
|
||||
<channel lang="sv" xmltv_id="InvestigationDiscoverySweden.se" site_id="se#1039">Investigation Discovery Sverige</channel>
|
||||
<channel lang="sv" xmltv_id="Kanal5.se" site_id="se#0279">Kanal 5</channel>
|
||||
<channel lang="sv" xmltv_id="Kanal9.se" site_id="se#474">Kanal 9</channel>
|
||||
<channel lang="sv" xmltv_id="Kanal11.se" site_id="se#0235">Kanal 11</channel>
|
||||
<channel lang="sv" xmltv_id="Kunskapskanalen.se" site_id="se#0149">Kunskapskanalen</channel>
|
||||
<channel lang="sv" xmltv_id="MTV00s.uk" site_id="se#0246">MTV 00s</channel>
|
||||
<channel lang="sv" xmltv_id="MTV80s.uk" site_id="se#0099">MTV 80s</channel>
|
||||
<channel lang="sv" xmltv_id="MTVHitsEurope.uk" site_id="se#0077">MTV Hits</channel>
|
||||
<channel lang="sv" xmltv_id="MTVGlobal.uk" site_id="se#0080">MTV Nordic</channel>
|
||||
<channel lang="sv" xmltv_id="MotorvisionTV.de" site_id="se#1009">Motorvision</channel>
|
||||
<channel lang="sv" xmltv_id="NationalGeographicSweden.se" site_id="se#0084">National Geographic</channel>
|
||||
<channel lang="sv" xmltv_id="NationalGeographicWildSweden.se" site_id="se#0082">National Geographic Wild</channel>
|
||||
<channel lang="sv" xmltv_id="NFLNetwork.us" site_id="se#569">NFL Network</channel>
|
||||
<channel lang="sv" xmltv_id="NickelodeonScandinavia.nl" site_id="se#0086">Nickelodeon</channel>
|
||||
<channel lang="sv" xmltv_id="NickJrScandinavia.nl" site_id="se#0088">Nick Jr</channel>
|
||||
<channel lang="sv" xmltv_id="NicktoonsScandinavia.nl" site_id="se#570">Nicktoons</channel>
|
||||
<channel lang="sv" xmltv_id="NRK1.no" site_id="se#0090">NRK1</channel>
|
||||
<channel lang="sv" xmltv_id="NRK2.no" site_id="se#0288">NRK2</channel>
|
||||
<channel lang="sv" xmltv_id="NRK3.no" site_id="se#0289">NRK3</channel>
|
||||
<channel lang="sv" xmltv_id="ParamountNetworkSweden.se" site_id="se#0034">Paramount Network</channel>
|
||||
<channel lang="sv" xmltv_id="ParamountPlusMovies.se" site_id="se#1001">Paramount+ Movies</channel>
|
||||
<channel lang="sv" xmltv_id="ParamountPlusSeries.se" site_id="se#1002">Paramount+ Series</channel>
|
||||
<channel lang="sv" xmltv_id="SFkanalen.se" site_id="se#972">SF-kanalen</channel>
|
||||
<channel lang="sv" xmltv_id="Sjuan.se" site_id="se#0232">Sjuan</channel>
|
||||
<channel lang="sv" xmltv_id="SkyNewsInternational.uk" site_id="se#596">Sky News International</channel>
|
||||
<channel lang="sv" xmltv_id="Sportkanalen.se" site_id="se#0325">Sportkanalen</channel>
|
||||
<channel lang="sv" xmltv_id="SVT1.se" site_id="se#0148">SVT 1</channel>
|
||||
<channel lang="sv" xmltv_id="SVT2.se" site_id="se#0282">SVT 2</channel>
|
||||
<channel lang="sv" xmltv_id="SVT24.se" site_id="se#146">SVT 24</channel>
|
||||
<channel lang="sv" xmltv_id="SVTBarn.se" site_id="se#0147">SVT Barn</channel>
|
||||
<channel lang="sv" xmltv_id="TLCSweden.se" site_id="se#1038">TLC Sverige</channel>
|
||||
<channel lang="sv" xmltv_id="TV2.dk" site_id="se#0297">TV 2</channel>
|
||||
<channel lang="sv" xmltv_id="TV3.se" site_id="se#0290">TV 3</channel>
|
||||
<channel lang="sv" xmltv_id="TV4.se" site_id="se#0227">TV 4</channel>
|
||||
<channel lang="sv" xmltv_id="TV4Fakta.se" site_id="se#0228">TV 4 Fakta</channel>
|
||||
<channel lang="sv" xmltv_id="TV4Film.se" site_id="se#0229">TV 4 Film</channel>
|
||||
<channel lang="sv" xmltv_id="TV4Guld.se" site_id="se#0230">TV 4 Guld</channel>
|
||||
<channel lang="sv" xmltv_id="TV6Sweden.se" site_id="se#0360">TV 6</channel>
|
||||
<channel lang="sv" xmltv_id="TV8Sweden.se" site_id="se#666">TV 8</channel>
|
||||
<channel lang="sv" xmltv_id="TV10.se" site_id="se#667">TV 10</channel>
|
||||
<channel lang="sv" xmltv_id="TV12.se" site_id="se#664">TV 12</channel>
|
||||
<channel lang="sv" xmltv_id="VFilmAction.se" site_id="se#0299">V Film Action</channel>
|
||||
<channel lang="sv" xmltv_id="VFilmFamily.se" site_id="se#0308">V Film Family</channel>
|
||||
<channel lang="sv" xmltv_id="VFilmHits.se" site_id="se#0322">V Film Hits</channel>
|
||||
<channel lang="sv" xmltv_id="VFilmPremiere.se" site_id="se#0321">V Film Premiere</channel>
|
||||
<channel lang="sv" xmltv_id="ViasatExplore.se" site_id="se#0358">Viasat Explore</channel>
|
||||
<channel lang="sv" xmltv_id="ViasatHistory.se" site_id="se#0357">Viasat History HD</channel>
|
||||
<channel lang="sv" xmltv_id="ViasatNature.se" site_id="se#0356">Viasat Nature</channel>
|
||||
<channel lang="sv" xmltv_id="VSeries.se" site_id="se#0320">V Series</channel>
|
||||
<channel lang="sv" xmltv_id="VSport1Sweden.se" site_id="se#0362">V Sport 1</channel>
|
||||
<channel lang="sv" xmltv_id="VSportExtra.se" site_id="se#715">V Sport Extra</channel>
|
||||
<channel lang="sv" xmltv_id="VSportFootball.se" site_id="se#0269">V Sport Football</channel>
|
||||
<channel lang="sv" xmltv_id="VSportGolf.se" site_id="se#0364">V Sport Golf</channel>
|
||||
<channel lang="sv" xmltv_id="VSportLive1.se" site_id="se#0255">V Sport Live 1</channel>
|
||||
<channel lang="sv" xmltv_id="VSportLive2.se" site_id="se#0256">V Sport Live 2</channel>
|
||||
<channel lang="sv" xmltv_id="VSportLive3.se" site_id="se#0257">V Sport Live 3</channel>
|
||||
<channel lang="sv" xmltv_id="VSportLive4.se" site_id="se#0258">V Sport Live 4</channel>
|
||||
<channel lang="sv" xmltv_id="VSportLive5.se" site_id="se#0259">V Sport Live 5</channel>
|
||||
<channel lang="sv" xmltv_id="VSportMotor.se" site_id="se#0292">V Sport Motor</channel>
|
||||
<channel lang="sv" xmltv_id="VSportPremium.se" site_id="se#527">V Sport Premium</channel>
|
||||
<channel lang="sv" xmltv_id="VSportUltraHD.se" site_id="se#418">V Sport Ultra HD</channel>
|
||||
<channel lang="sv" xmltv_id="VSportVinter.se" site_id="se#0363">V Sport Vinter</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="AlJazeeraEnglish.qa" site_id="se#0344">Aljazeera</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="AnimalPlanetSweden.se" site_id="se#1005">Animal Planet Sverige</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="ATGLive.se" site_id="se#1000">ATG Live</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="BBCEarthNordic.uk" site_id="se#1018">BBC Earth HD</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="BBCBritNordic.uk" site_id="se#1016">BBC Brit HD</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="BBCWorldNewsEurope.uk" site_id="se#0016">BBC World News</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="BloombergTVEurope.uk" site_id="se#1008">Bloomberg TV</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="BoomerangNordic.uk" site_id="se#0017">Boomerang</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CartoonNetworkScandinavia.uk" site_id="se#0028">Cartoon Network</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreFirst.se" site_id="se#968">C More First</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreFotboll.se" site_id="se#657">C More Fotboll</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreHits.se" site_id="se#969">C More Hits</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreHockey.se" site_id="se#656">C More Hockey</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreLive.se" site_id="se#659">C More Live</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreLive2.se" site_id="se#660">C More Live 2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreLive3.se" site_id="se#661">C More Live 3</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreLive4.se" site_id="se#662">C More Live 4</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreLive5.se" site_id="se#663">C More Live 5</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreMix.se" site_id="se#658">C More Mix</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreSeries.se" site_id="se#971">C More Series</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CMoreStars.se" site_id="se#970">C More Stars</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CNBCEurope.uk" site_id="se#0032">CNBC</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="CNNInternationalEurope.us" site_id="se#0033">CNN</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryChannelSweden.se" site_id="se#493">Discovery Channel Sverige</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryScienceSweden.se" site_id="se#1006">Discovery Science Sverige</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DisneyChannelScandinavia.uk" site_id="se#0037">Disney Channel</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DisneyJuniorScandinavia.uk" site_id="se#0307">Disney Junior</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryPlusExtra1.se" site_id="se#637">Discovery+ Extra 1</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryPlusExtra2.se" site_id="se#638">Discovery+ Extra 2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryPlusExtra3.se" site_id="se#639">Discovery+ Extra 3</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryPlusExtra4.se" site_id="se#640">Discovery+ Extra 4</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryPlusExtra5.se" site_id="se#641">Discovery+ Extra 5</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryPlusExtra6.se" site_id="se#642">Discovery+ Extra 6</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryPlusExtra7.se" site_id="se#643">Discovery+ Extra 7</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DiscoveryPlusExtra8.se" site_id="se#644">Discovery+ Extra 8</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DR1.dk" site_id="se#452">DR 1</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DR2.dk" site_id="se#0051">DR 2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="DRRamasjang.dk" site_id="se#0048">DR Ramasjang</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="EEurope.us" site_id="se#0052">E!</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="EuronewsEnglish.fr" site_id="se#0281">EuroNews</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Eurosport1.fr" site_id="se#1023">Eurosport 1</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Eurosport2.fr" site_id="se#1024">Eurosport 2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Godare.se" site_id="se#722">Godare</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="GodTV.uk" site_id="se#0058">God TV</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="HistorySweden.se" site_id="se#652">History</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="History2Nordic.us" site_id="se#1004">H2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="HorseCountryTV.uk" site_id="se#668">Horse & Country TV</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="InvestigationDiscoverySweden.se" site_id="se#1039">Investigation Discovery Sverige</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Kanal5.se" site_id="se#0279">Kanal 5</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Kanal9.se" site_id="se#474">Kanal 9</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Kanal11.se" site_id="se#0235">Kanal 11</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Kunskapskanalen.se" site_id="se#0149">Kunskapskanalen</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="MTV00s.uk" site_id="se#0246">MTV 00s</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="MTV80s.uk" site_id="se#0099">MTV 80s</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="MTVHitsEurope.uk" site_id="se#0077">MTV Hits</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="MTVGlobal.uk" site_id="se#0080">MTV Nordic</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="MotorvisionTV.de" site_id="se#1009">Motorvision</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NationalGeographicSweden.se" site_id="se#0084">National Geographic</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NationalGeographicWildSweden.se" site_id="se#0082">National Geographic Wild</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NFLNetwork.us" site_id="se#569">NFL Network</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NickelodeonScandinavia.nl" site_id="se#0086">Nickelodeon</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NickJrScandinavia.nl" site_id="se#0088">Nick Jr</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NicktoonsScandinavia.nl" site_id="se#570">Nicktoons</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NRK1.no" site_id="se#0090">NRK1</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NRK2.no" site_id="se#0288">NRK2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="NRK3.no" site_id="se#0289">NRK3</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="ParamountNetworkSweden.se" site_id="se#0034">Paramount Network</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="ParamountPlusMovies.se" site_id="se#1001">Paramount+ Movies</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="ParamountPlusSeries.se" site_id="se#1002">Paramount+ Series</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="SFkanalen.se" site_id="se#972">SF-kanalen</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Sjuan.se" site_id="se#0232">Sjuan</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="SkyNewsInternational.uk" site_id="se#596">Sky News International</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="Sportkanalen.se" site_id="se#0325">Sportkanalen</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="SVT1.se" site_id="se#0148">SVT 1</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="SVT2.se" site_id="se#0282">SVT 2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="SVT24.se" site_id="se#146">SVT 24</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="SVTBarn.se" site_id="se#0147">SVT Barn</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TLCSweden.se" site_id="se#1038">TLC Sverige</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV2.dk" site_id="se#0297">TV 2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV3.se" site_id="se#0290">TV 3</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV4.se" site_id="se#0227">TV 4</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV4Fakta.se" site_id="se#0228">TV 4 Fakta</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV4Film.se" site_id="se#0229">TV 4 Film</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV4Guld.se" site_id="se#0230">TV 4 Guld</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV6Sweden.se" site_id="se#0360">TV 6</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV8Sweden.se" site_id="se#666">TV 8</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV10.se" site_id="se#667">TV 10</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="TV12.se" site_id="se#664">TV 12</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VFilmAction.se" site_id="se#0299">V Film Action</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VFilmFamily.se" site_id="se#0308">V Film Family</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VFilmHits.se" site_id="se#0322">V Film Hits</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VFilmPremiere.se" site_id="se#0321">V Film Premiere</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="ViasatExplore.se" site_id="se#0358">Viasat Explore</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="ViasatHistory.se" site_id="se#0357">Viasat History HD</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="ViasatNature.se" site_id="se#0356">Viasat Nature</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSeries.se" site_id="se#0320">V Series</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSport1Sweden.se" site_id="se#0362">V Sport 1</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportExtra.se" site_id="se#715">V Sport Extra</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportFootball.se" site_id="se#0269">V Sport Football</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportGolf.se" site_id="se#0364">V Sport Golf</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportLive1.se" site_id="se#0255">V Sport Live 1</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportLive2.se" site_id="se#0256">V Sport Live 2</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportLive3.se" site_id="se#0257">V Sport Live 3</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportLive4.se" site_id="se#0258">V Sport Live 4</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportLive5.se" site_id="se#0259">V Sport Live 5</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportMotor.se" site_id="se#0292">V Sport Motor</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportPremium.se" site_id="se#527">V Sport Premium</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportUltraHD.se" site_id="se#418">V Sport Ultra HD</channel>
|
||||
<channel site="allente.se" lang="sv" xmltv_id="VSportVinter.se" site_id="se#0363">V Sport Vinter</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="andorradifusio.ad">
|
||||
<channels>
|
||||
<channel lang="ca" xmltv_id="AndorraTV.ad" site_id="atv">Andorra TV</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="andorradifusio.ad" lang="ca" xmltv_id="AndorraTV.ad" site_id="atv">Andorra TV</channel>
|
||||
</channels>
|
|
@ -47,8 +47,8 @@ function parseItems(content, date) {
|
|||
.parent()
|
||||
.parent()
|
||||
const items = []
|
||||
const titles = column.find(`p`).toArray()
|
||||
column.find(`h4`).each((i, time) => {
|
||||
const titles = column.find('p').toArray()
|
||||
column.find('h4').each((i, time) => {
|
||||
items.push({
|
||||
time: $(time).text(),
|
||||
title: $(titles[i]).text()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// npx epg-grabber --config=sites/andorradifusio.ad/andorradifusio.ad.config.js --channels=sites/andorradifusio.ad/andorradifusio.ad.channels.xml --output=guide.xml
|
||||
// npm run grab -- --site=andorradifusio.ad
|
||||
|
||||
const { parser, url } = require('./andorradifusio.ad.config.js')
|
||||
const fs = require('fs')
|
||||
|
@ -30,20 +30,20 @@ it('can parse response', () => {
|
|||
expect(results[0]).toMatchObject({
|
||||
start: '2023-06-07T05:00:00.000Z',
|
||||
stop: '2023-06-07T06:00:00.000Z',
|
||||
title: `Club Piolet`
|
||||
title: 'Club Piolet'
|
||||
})
|
||||
|
||||
expect(results[20]).toMatchObject({
|
||||
start: '2023-06-07T23:00:00.000Z',
|
||||
stop: '2023-06-08T00:00:00.000Z',
|
||||
title: `Àrea Andorra Difusió`
|
||||
title: 'Àrea Andorra Difusió'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
date,
|
||||
content: `<!DOCTYPE html><html><head></head><body></body></html>`
|
||||
content: '<!DOCTYPE html><html><head></head><body></body></html>'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="arianaafgtv.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="ArianaAfghanistanInternationalTV.us" site_id="#">Ariana Afghanistan International TV</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="arianaafgtv.com" lang="en" xmltv_id="ArianaAfghanistanInternationalTV.us" site_id="#">Ariana Afghanistan International TV</channel>
|
||||
</channels>
|
|
@ -11,9 +11,7 @@ dayjs.extend(customParseFormat)
|
|||
module.exports = {
|
||||
site: 'arianaafgtv.com',
|
||||
days: 2,
|
||||
url() {
|
||||
return `https://www.arianaafgtv.com/index.html`
|
||||
},
|
||||
url: 'https://www.arianaafgtv.com/index.html',
|
||||
parser({ content, date }) {
|
||||
const programs = []
|
||||
const items = parseItems(content, date)
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="arianatelevision.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="ATNNational.af" site_id="#">Ariana TV National</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="arianatelevision.com" lang="en" xmltv_id="ATNNational.af" site_id="#">Ariana TV National</channel>
|
||||
</channels>
|
|
@ -4,7 +4,7 @@ const { DateTime } = require('luxon')
|
|||
module.exports = {
|
||||
site: 'arianatelevision.com',
|
||||
days: 2,
|
||||
url: `https://www.arianatelevision.com/program-schedule/`,
|
||||
url: 'https://www.arianatelevision.com/program-schedule/',
|
||||
parser({ content, date }) {
|
||||
const programs = []
|
||||
const items = parseItems(content, date)
|
||||
|
@ -37,8 +37,6 @@ function parseStart(item, date) {
|
|||
}
|
||||
|
||||
function parseItems(content, date) {
|
||||
const items = []
|
||||
const col = date.day()
|
||||
const $ = cheerio.load(content)
|
||||
const settings = $('#jtrt_table_settings_508').text()
|
||||
if (!settings) return []
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// npx epg-grabber --config=sites/arianatelevision.com/arianatelevision.com.config.js --channels=sites/arianatelevision.com/arianatelevision.com.channels.xml --output=guide.xml
|
||||
// npm run grab -- --site=arianatelevision.com
|
||||
|
||||
const { parser, url } = require('./arianatelevision.com.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
|
@ -18,7 +18,8 @@ it('can generate valid url', () => {
|
|||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `<!DOCTYPE html><html><head></head><body><textarea data-jtrt-table-id="508" id="jtrt_table_settings_508" cols="30" rows="10">[[["Start","Saturday","Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","",""],["7:00","City Report","ICC T20 Highlights","ICC T20 Highlights","ICC T20 Highlights","ICC T20 Highlights","ICC T20 Highlights","ICC T20 Highlights","",""],["7:30","ICC T20 Highlights","Sport ","Sport ","Sport ","Sport ","Sport ","Sport ","",""],["15:00","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","",""],["6:30","Quran and Hadis ","Falah","Falah","Falah","Falah","Falah","Falah","",""],["","\\n","","","","","","","",""]]]</textarea></body></html>`
|
||||
const content =
|
||||
'<!DOCTYPE html><html><head></head><body><textarea data-jtrt-table-id="508" id="jtrt_table_settings_508" cols="30" rows="10">[[["Start","Saturday","Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","",""],["7:00","City Report","ICC T20 Highlights","ICC T20 Highlights","ICC T20 Highlights","ICC T20 Highlights","ICC T20 Highlights","ICC T20 Highlights","",""],["7:30","ICC T20 Highlights","Sport ","Sport ","Sport ","Sport ","Sport ","Sport ","",""],["15:00","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","ICC T20 World Cup","",""],["6:30","Quran and Hadis ","Falah","Falah","Falah","Falah","Falah","Falah","",""],["","\\n","","","","","","","",""]]]</textarea></body></html>'
|
||||
const result = parser({ content, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
|
@ -29,22 +30,22 @@ it('can parse response', () => {
|
|||
{
|
||||
start: '2021-11-27T02:30:00.000Z',
|
||||
stop: '2021-11-27T03:00:00.000Z',
|
||||
title: `City Report`
|
||||
title: 'City Report'
|
||||
},
|
||||
{
|
||||
start: '2021-11-27T03:00:00.000Z',
|
||||
stop: '2021-11-27T10:30:00.000Z',
|
||||
title: `ICC T20 Highlights`
|
||||
title: 'ICC T20 Highlights'
|
||||
},
|
||||
{
|
||||
start: '2021-11-27T10:30:00.000Z',
|
||||
stop: '2021-11-28T02:00:00.000Z',
|
||||
title: `ICC T20 World Cup`
|
||||
title: 'ICC T20 World Cup'
|
||||
},
|
||||
{
|
||||
start: '2021-11-28T02:00:00.000Z',
|
||||
stop: '2021-11-28T02:30:00.000Z',
|
||||
title: `Quran and Hadis`
|
||||
title: 'Quran and Hadis'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
@ -53,7 +54,8 @@ it('can handle empty guide', () => {
|
|||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: `<!DOCTYPE html><html><head></head><body><textarea data-jtrt-table-id="508" id="jtrt_table_settings_508" cols="30" rows="10"></textarea></body></html>`
|
||||
content:
|
||||
'<!DOCTYPE html><html><head></head><body><textarea data-jtrt-table-id="508" id="jtrt_table_settings_508" cols="30" rows="10"></textarea></body></html>'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="arirang.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="ArirangTV.kr" site_id="CH_K" logo="https://i.imgur.com/Asu5pE9.png">Arirang TV</channel>
|
||||
<channel lang="en" xmltv_id="ArirangUN.kr" site_id="CH_Z" logo="https://i.imgur.com/Jdy3WNm.png">Arirang UN</channel>
|
||||
<channel lang="en" xmltv_id="ArirangWorld.kr" site_id="CH_W" logo="https://i.imgur.com/5Aoithj.png">Arirang World</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="arirang.com" lang="en" xmltv_id="ArirangTV.kr" site_id="CH_K">Arirang TV</channel>
|
||||
<channel site="arirang.com" lang="en" xmltv_id="ArirangUN.kr" site_id="CH_Z">Arirang UN</channel>
|
||||
<channel site="arirang.com" lang="en" xmltv_id="ArirangWorld.kr" site_id="CH_W">Arirang World</channel>
|
||||
</channels>
|
|
@ -8,132 +8,146 @@ dayjs.extend(timezone)
|
|||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'arirang.com',
|
||||
output: 'arirang.com.guide.xml',
|
||||
channels: 'arirang.com.channels.xml',
|
||||
lang: 'en',
|
||||
days: 7,
|
||||
delay: 5000,
|
||||
url: 'https://www.arirang.com/v1.0/open/external/proxy',
|
||||
site: 'arirang.com',
|
||||
output: 'arirang.com.guide.xml',
|
||||
channels: 'arirang.com.channels.xml',
|
||||
lang: 'en',
|
||||
days: 7,
|
||||
delay: 5000,
|
||||
url: 'https://www.arirang.com/v1.0/open/external/proxy',
|
||||
|
||||
request: {
|
||||
request: {
|
||||
method: 'POST',
|
||||
timeout: 5000,
|
||||
cache: { ttl: 60 * 60 * 1000 },
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
Origin: 'https://www.arirang.com',
|
||||
Referer: 'https://www.arirang.com/schedule',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
||||
},
|
||||
data: function (context) {
|
||||
const { channel, date } = context
|
||||
return {
|
||||
address: 'https://script.arirang.com/api/v1/bis/listScheduleV3.do',
|
||||
method: 'POST',
|
||||
timeout: 5000,
|
||||
cache: { ttl: 60 * 60 * 1000 },
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': 'https://www.arirang.com',
|
||||
'Referer': 'https://www.arirang.com/schedule',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
||||
},
|
||||
data: function (context) {
|
||||
const { channel, date } = context
|
||||
return {
|
||||
'address': 'https://script.arirang.com/api/v1/bis/listScheduleV3.do',
|
||||
'method': 'POST',
|
||||
'headers': {},
|
||||
'body': {
|
||||
'data': {
|
||||
'dmParam': {
|
||||
'chanId': channel.site_id,
|
||||
'broadYmd': dayjs.tz(date, 'Asia/Seoul').format('YYYYMMDD'),
|
||||
'planNo': '1'
|
||||
}
|
||||
}
|
||||
}
|
||||
headers: {},
|
||||
body: {
|
||||
data: {
|
||||
dmParam: {
|
||||
chanId: channel.site_id,
|
||||
broadYmd: dayjs.tz(date, 'Asia/Seoul').format('YYYYMMDD'),
|
||||
planNo: '1'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
logo: function (context) {
|
||||
return context.channel.logo
|
||||
},
|
||||
|
||||
async parser(context) {
|
||||
const programs = []
|
||||
const items = parseItems(context.content)
|
||||
|
||||
for (let item of items) {
|
||||
const programDetail = await parseProgramDetail(item)
|
||||
|
||||
programs.push({
|
||||
title: item.displayNm,
|
||||
start: parseStart(item),
|
||||
stop: parseStop(item),
|
||||
icon: parseIcon(programDetail),
|
||||
category: parseCategory(programDetail),
|
||||
description: parseDescription(programDetail)
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
logo: function (context) {
|
||||
return context.channel.logo
|
||||
},
|
||||
|
||||
async parser(context) {
|
||||
const programs = []
|
||||
const items = parseItems(context.content)
|
||||
|
||||
for (let item of items) {
|
||||
const programDetail = await parseProgramDetail(item)
|
||||
|
||||
programs.push({
|
||||
title: item.displayNm,
|
||||
start: parseStart(item),
|
||||
stop: parseStop(item),
|
||||
icon: parseIcon(programDetail),
|
||||
category: parseCategory(programDetail),
|
||||
description: parseDescription(programDetail)
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
|
||||
function parseItems(content) {
|
||||
if (content != '') {
|
||||
const data = JSON.parse(content)
|
||||
return (!data || !data.responseBody || !Array.isArray(data.responseBody.dsSchWeek)) ? [] : data.responseBody.dsSchWeek
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
if (content != '') {
|
||||
const data = JSON.parse(content)
|
||||
return !data || !data.responseBody || !Array.isArray(data.responseBody.dsSchWeek)
|
||||
? []
|
||||
: data.responseBody.dsSchWeek
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
function parseStart(item) {
|
||||
return dayjs.tz(item.broadYmd + ' ' + item.broadHm, 'YYYYMMDD HHmm', 'Asia/Seoul')
|
||||
return dayjs.tz(item.broadYmd + ' ' + item.broadHm, 'YYYYMMDD HHmm', 'Asia/Seoul')
|
||||
}
|
||||
|
||||
function parseStop(item) {
|
||||
return dayjs.tz(item.broadYmd + ' ' + item.broadHm, 'YYYYMMDD HHmm', 'Asia/Seoul').add(item.broadRun, 'minute')
|
||||
return dayjs
|
||||
.tz(item.broadYmd + ' ' + item.broadHm, 'YYYYMMDD HHmm', 'Asia/Seoul')
|
||||
.add(item.broadRun, 'minute')
|
||||
}
|
||||
|
||||
async function parseProgramDetail(item) {
|
||||
return axios.post(
|
||||
'https://www.arirang.com/v1.0/open/program/detail',
|
||||
{
|
||||
'bis_program_code': item.pgmCd
|
||||
return axios
|
||||
.post(
|
||||
'https://www.arirang.com/v1.0/open/program/detail',
|
||||
{
|
||||
bis_program_code: item.pgmCd
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
Origin: 'https://www.arirang.com',
|
||||
Referer: 'https://www.arirang.com/schedule',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': 'https://www.arirang.com',
|
||||
'Referer': 'https://www.arirang.com/schedule',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
||||
},
|
||||
timeout: 5000,
|
||||
cache: { ttl: 60 * 1000 },
|
||||
}
|
||||
).then(function (response) {
|
||||
return response.data
|
||||
}).catch(function (error) {
|
||||
// console.log(error)
|
||||
timeout: 5000,
|
||||
cache: { ttl: 60 * 1000 }
|
||||
}
|
||||
)
|
||||
.then(response => {
|
||||
return response.data
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
function parseIcon(programDetail) {
|
||||
if (programDetail && programDetail.image && programDetail.image[0].url) {
|
||||
return programDetail.image[0].url
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
if (programDetail && programDetail.image && programDetail.image[0].url) {
|
||||
return programDetail.image[0].url
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function parseCategory(programDetail) {
|
||||
if (programDetail && programDetail.category_Info && programDetail.category_Info[0].title) {
|
||||
return programDetail.category_Info[0].title
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
if (programDetail && programDetail.category_Info && programDetail.category_Info[0].title) {
|
||||
return programDetail.category_Info[0].title
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
function parseDescription(programDetail) {
|
||||
if (programDetail && programDetail.content && programDetail.content[0] && programDetail.content[0].text) {
|
||||
let description = programDetail.content[0].text
|
||||
let regex = /(<([^>]+)>)/ig
|
||||
return description.replace(regex, '')
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
if (
|
||||
programDetail &&
|
||||
programDetail.content &&
|
||||
programDetail.content[0] &&
|
||||
programDetail.content[0].text
|
||||
) {
|
||||
let description = programDetail.content[0].text
|
||||
let regex = /(<([^>]+)>)/gi
|
||||
return description.replace(regex, '')
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// npx epg-grabber --config=sites/arirang.com/arirang.com.config.js --channels=sites/arirang.com/arirang.com.channels.xml --output=guide.xml --days=2
|
||||
// npm run grab -- --site=arirang.com
|
||||
// npx jest arirang.com.test.js
|
||||
|
||||
const { url, parser } = require('./arirang.com.config.js')
|
||||
|
@ -7,53 +7,68 @@ const path = require('path')
|
|||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const { program } = require('commander')
|
||||
dayjs.extend(utc)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const date = dayjs.tz('2023-08-25', 'Asia/Seoul').startOf('d')
|
||||
const channel = { xmltv_id: 'ArirangWorld.kr', site_id: 'CH_W', name: 'Arirang World', lang: 'en', logo: 'https://i.imgur.com/5Aoithj.png' }
|
||||
const channel = {
|
||||
xmltv_id: 'ArirangWorld.kr',
|
||||
site_id: 'CH_W',
|
||||
name: 'Arirang World',
|
||||
lang: 'en',
|
||||
logo: 'https://i.imgur.com/5Aoithj.png'
|
||||
}
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/schedule.json'), 'utf8')
|
||||
const programDetail = fs.readFileSync(path.resolve(__dirname, '__data__/detail.json'), 'utf8')
|
||||
const context = { 'channel': channel, 'content': content, 'date': date }
|
||||
const context = { channel: channel, content: content, date: date }
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url).toBe('https://www.arirang.com/v1.0/open/external/proxy')
|
||||
expect(url).toBe('https://www.arirang.com/v1.0/open/external/proxy')
|
||||
})
|
||||
|
||||
it('can handle empty guide', async () => {
|
||||
const results = await parser({ 'channel': channel, 'content': '', 'date': date })
|
||||
expect(results).toMatchObject([])
|
||||
const results = await parser({ channel: channel, content: '', date: date })
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
|
||||
it('can parse response', async () => {
|
||||
axios.post.mockImplementation((url, data) => {
|
||||
if (url === 'https://www.arirang.com/v1.0/open/external/proxy' && JSON.stringify(data) === JSON.stringify({ "address": "https://script.arirang.com/api/v1/bis/listScheduleV3.do", "method": "POST", "headers": {}, "body": { "data": { "dmParam": { "chanId": "CH_W", "broadYmd": "20230825", "planNo": "1" } } } })) {
|
||||
return Promise.resolve({
|
||||
data: JSON.parse(content)
|
||||
})
|
||||
} else if (url === 'https://www.arirang.com/v1.0/open/program/detail' && JSON.stringify(data) === JSON.stringify({ "bis_program_code": "2023004T" })) {
|
||||
return Promise.resolve({
|
||||
data: JSON.parse(programDetail)
|
||||
})
|
||||
} else {
|
||||
return Promise.resolve({
|
||||
data: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
axios.post.mockImplementation((url, data) => {
|
||||
if (
|
||||
url === 'https://www.arirang.com/v1.0/open/external/proxy' &&
|
||||
JSON.stringify(data) ===
|
||||
JSON.stringify({
|
||||
address: 'https://script.arirang.com/api/v1/bis/listScheduleV3.do',
|
||||
method: 'POST',
|
||||
headers: {},
|
||||
body: { data: { dmParam: { chanId: 'CH_W', broadYmd: '20230825', planNo: '1' } } }
|
||||
})
|
||||
) {
|
||||
return Promise.resolve({
|
||||
data: JSON.parse(content)
|
||||
})
|
||||
} else if (
|
||||
url === 'https://www.arirang.com/v1.0/open/program/detail' &&
|
||||
JSON.stringify(data) === JSON.stringify({ bis_program_code: '2023004T' })
|
||||
) {
|
||||
return Promise.resolve({
|
||||
data: JSON.parse(programDetail)
|
||||
})
|
||||
} else {
|
||||
return Promise.resolve({
|
||||
data: ''
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const results = await parser(context)
|
||||
const results = await parser(context)
|
||||
|
||||
expect(results[0]).toMatchObject(
|
||||
{
|
||||
title: "WITHIN THE FRAME [R]",
|
||||
start: dayjs.tz(date, 'Asia/Seoul'),
|
||||
stop: dayjs.tz(date, 'Asia/Seoul').add(30, 'minute'),
|
||||
icon: "https://img.arirang.com/v1/AUTH_d52449c16d3b4bbca17d4fffd9fc44af/public/images/202308/2080840096998752900.png",
|
||||
description: "NEWS",
|
||||
category: "Current Affairs"
|
||||
}
|
||||
)
|
||||
})
|
||||
expect(results[0]).toMatchObject({
|
||||
title: 'WITHIN THE FRAME [R]',
|
||||
start: dayjs.tz(date, 'Asia/Seoul'),
|
||||
stop: dayjs.tz(date, 'Asia/Seoul').add(30, 'minute'),
|
||||
icon: 'https://img.arirang.com/v1/AUTH_d52449c16d3b4bbca17d4fffd9fc44af/public/images/202308/2080840096998752900.png',
|
||||
description: 'NEWS',
|
||||
category: 'Current Affairs'
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="artonline.tv">
|
||||
<channels>
|
||||
<channel lang="ar" xmltv_id="ARTAflam1.sa" site_id="">ART Aflam 1</channel>
|
||||
<channel lang="ar" xmltv_id="ARTAflam2.sa" site_id="Aflam2">ART Aflam 2</channel>
|
||||
<channel lang="ar" xmltv_id="ARTCinema.sa" site_id="Cinema">ART Cinema</channel>
|
||||
<channel lang="ar" xmltv_id="ARTHekayat.sa" site_id="Hekayat">ART Hekayat</channel>
|
||||
<channel lang="ar" xmltv_id="ARTHekayat2.sa" site_id="Hekayat2">ART Hekayat 2</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="artonline.tv" lang="ar" xmltv_id="ARTAflam1.sa" site_id="">ART Aflam 1</channel>
|
||||
<channel site="artonline.tv" lang="ar" xmltv_id="ARTAflam2.sa" site_id="Aflam2">ART Aflam 2</channel>
|
||||
<channel site="artonline.tv" lang="ar" xmltv_id="ARTCinema.sa" site_id="Cinema">ART Cinema</channel>
|
||||
<channel site="artonline.tv" lang="ar" xmltv_id="ARTHekayat.sa" site_id="Hekayat">ART Hekayat</channel>
|
||||
<channel site="artonline.tv" lang="ar" xmltv_id="ARTHekayat2.sa" site_id="Hekayat2">ART Hekayat 2</channel>
|
||||
</channels>
|
|
@ -51,14 +51,14 @@ module.exports = {
|
|||
}
|
||||
|
||||
function parseStart(item) {
|
||||
const [_, M, D, YYYY] = item.adddate.match(/(\d+)\/(\d+)\/(\d+) /)
|
||||
const [, M, D, YYYY] = item.adddate.match(/(\d+)\/(\d+)\/(\d+) /)
|
||||
const [HH, mm] = item.start_Time.split(':')
|
||||
|
||||
return dayjs.tz(`${YYYY}-${M}-${D}T${HH}:${mm}:00`, 'YYYY-M-DTHH:mm:ss', 'Asia/Riyadh')
|
||||
}
|
||||
|
||||
function parseDuration(item) {
|
||||
const [__, HH, mm, ss] = item.duration.match(/(\d+):(\d+):(\d+)/)
|
||||
const [, HH, mm, ss] = item.duration.match(/(\d+):(\d+):(\d+)/)
|
||||
|
||||
return parseInt(HH) * 3600 + parseInt(mm) * 60 + parseInt(ss)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// npx epg-grabber --config=sites/artonline.tv/artonline.tv.config.js --channels=sites/artonline.tv/artonline.tv.channels.xml --output=guide.xml --days=2
|
||||
// npm run grab -- --site=artonline.tv
|
||||
|
||||
const { parser, url, request } = require('./artonline.tv.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
|
@ -39,7 +39,8 @@ it('can generate valid request data for tomorrow', () => {
|
|||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `[{"id":158963,"eventid":null,"duration":"01:34:00","lang":"Arabic","title":"الراقصه و السياسي","description":"تقرر الراقصه سونيا انشاء دار حضانه للأطفال اليتامى و عندما تتقدم بمشورعها للمسئول يرفض فتتحداه ، تلجأ للوزير عبد الحميد رأفت تربطه بها علاقة قديمة ، يخشى على مركزه و يرفض مساعدتها فتقرر كتابة مذكراتها بمساعدة أحد الصحفيين ، يتخوف عبد الحميد و المسئولين ثم يفاجأ عبد الحميد بحصول سونيا على الموافقه للمشورع و البدء في تنفيذه و ذلك لعلاقتها بأحد كبار المسئولين .","thumbnail":"/UploadImages/Channel/ARTAFLAM1/03/AlRaqesaWaAlSeyasi.jpg","image":"0","start_Time":"00:30","adddate":"3/4/2022 12:00:00 AM","repeat1":null,"iD_genre":0,"iD_Show_Type":0,"iD_Channel":77,"iD_country":0,"iD_rating":0,"end_time":"02:04","season_Number":0,"epoisode_Number":0,"hasCatchup":0,"cmsid":0,"containerID":0,"imagePath":"../../UploadImages/Channel/ARTAFLAM1/3/","youtube":"0","published_at":"0","directed_by":"0","composition":"0","cast":"0","timeShow":null,"short_description":"تقرر الراقصه سونيا انشاء دار حضانه للأطفال اليتامى و عندما تتقدم بمشورعها للمسئول يرفض فتتحداه ، تلجأ للوزير عبد الحميد رأفت تربطه بها علاقة قديمة ، يخشى على مركزه و يرفض مساعدتها فتقرر كتابة مذكراتها بمساعدة أحد الصحفيين ، يتخوف عبد الحميد و المسئولين ثم يفاجأ عبد الحميد بحصول سونيا على الموافقه للمشورع و البدء في تنفيذه و ذلك لعلاقتها بأحد كبار المسئولين .","seOdescription":null,"tagseo":null,"channel_name":null,"pathimage":null,"pathThumbnail":null}]`
|
||||
const content =
|
||||
'[{"id":158963,"eventid":null,"duration":"01:34:00","lang":"Arabic","title":"الراقصه و السياسي","description":"تقرر الراقصه سونيا انشاء دار حضانه للأطفال اليتامى و عندما تتقدم بمشورعها للمسئول يرفض فتتحداه ، تلجأ للوزير عبد الحميد رأفت تربطه بها علاقة قديمة ، يخشى على مركزه و يرفض مساعدتها فتقرر كتابة مذكراتها بمساعدة أحد الصحفيين ، يتخوف عبد الحميد و المسئولين ثم يفاجأ عبد الحميد بحصول سونيا على الموافقه للمشورع و البدء في تنفيذه و ذلك لعلاقتها بأحد كبار المسئولين .","thumbnail":"/UploadImages/Channel/ARTAFLAM1/03/AlRaqesaWaAlSeyasi.jpg","image":"0","start_Time":"00:30","adddate":"3/4/2022 12:00:00 AM","repeat1":null,"iD_genre":0,"iD_Show_Type":0,"iD_Channel":77,"iD_country":0,"iD_rating":0,"end_time":"02:04","season_Number":0,"epoisode_Number":0,"hasCatchup":0,"cmsid":0,"containerID":0,"imagePath":"../../UploadImages/Channel/ARTAFLAM1/3/","youtube":"0","published_at":"0","directed_by":"0","composition":"0","cast":"0","timeShow":null,"short_description":"تقرر الراقصه سونيا انشاء دار حضانه للأطفال اليتامى و عندما تتقدم بمشورعها للمسئول يرفض فتتحداه ، تلجأ للوزير عبد الحميد رأفت تربطه بها علاقة قديمة ، يخشى على مركزه و يرفض مساعدتها فتقرر كتابة مذكراتها بمساعدة أحد الصحفيين ، يتخوف عبد الحميد و المسئولين ثم يفاجأ عبد الحميد بحصول سونيا على الموافقه للمشورع و البدء في تنفيذه و ذلك لعلاقتها بأحد كبار المسئولين .","seOdescription":null,"tagseo":null,"channel_name":null,"pathimage":null,"pathThumbnail":null}]'
|
||||
const result = parser({ content }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
|
|
|
@ -1,143 +1,141 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="astro.com.my">
|
||||
<channels>
|
||||
<channel lang="ms" xmltv_id="8TV.my" site_id="115">8TV</channel>
|
||||
<!-- <channel lang="ms" xmltv_id="" site_id="461">ABC</channel> -->
|
||||
<channel lang="ms" xmltv_id="AdithyaTV.in" site_id="67">Adithya TV</channel>
|
||||
<channel lang="ms" xmltv_id="AlJazeeraEnglish.qa" site_id="374">Aljazeera</channel>
|
||||
<channel lang="ms" xmltv_id="AnimalPlanetMalaysia.my" site_id="377">Animal Planet</channel>
|
||||
<channel lang="ms" xmltv_id="AsianFoodNetwork.sg" site_id="91">Asian Food Network</channel>
|
||||
<channel lang="ms" xmltv_id="AstroAEC.my" site_id="182">Astro AEC</channel>
|
||||
<channel lang="ms" xmltv_id="AstroAOD311.my" site_id="172">Astro AOD 311</channel>
|
||||
<channel lang="ms" xmltv_id="AstroAOD352.my" site_id="87">Astro AOD 352</channel>
|
||||
<channel lang="ms" xmltv_id="AstroAOD353.my" site_id="114">Astro AOD 353</channel>
|
||||
<channel lang="ms" xmltv_id="AstroAOD354.my" site_id="65">Astro AOD 354</channel>
|
||||
<channel lang="ms" xmltv_id="AstroAOD355.my" site_id="66">Astro AOD 355</channel>
|
||||
<channel lang="ms" xmltv_id="AstroArena.my" site_id="235">Astro Arena</channel>
|
||||
<channel lang="ms" xmltv_id="AstroArena2.my" site_id="457">Astro Arena 2</channel>
|
||||
<channel lang="ms" xmltv_id="AstroAura.my" site_id="400">Astro Aura</channel>
|
||||
<channel lang="ms" xmltv_id="AstroAwani.my" site_id="436">Astro Awani</channel>
|
||||
<channel lang="ms" xmltv_id="AstroBollyOneHD.my" site_id="178">Astro BollyOne HD</channel>
|
||||
<channel lang="ms" xmltv_id="AstroBoxOfficeTayanganHebat.my" site_id="176">Astro Box Office Tayangan Hebat</channel>
|
||||
<channel lang="ms" xmltv_id="AstroBoxOfficeThangathirai.my" site_id="177">Astro Box Office Thangathirai</channel>
|
||||
<channel lang="ms" xmltv_id="AstroCeria.my" site_id="386">Astro Ceria</channel>
|
||||
<channel lang="ms" xmltv_id="AstroCh100.my" site_id="471">Gemilang</channel>
|
||||
<channel lang="ms" xmltv_id="AstroCitra.my" site_id="301">Astro Citra</channel>
|
||||
<channel lang="ms" xmltv_id="AstroCricket.my" site_id="197">Astro Cricket</channel>
|
||||
<channel lang="ms" xmltv_id="AstroHuaHeeDai.my" site_id="162">Astro Hua Hee Dai</channel>
|
||||
<channel lang="ms" xmltv_id="AstroOasis.my" site_id="315">Astro Oasis</channel>
|
||||
<channel lang="ms" xmltv_id="AstroPrima.my" site_id="316">Astro Prima</channel>
|
||||
<channel lang="ms" xmltv_id="AstroQuanJiaHD.my" site_id="158">Astro Quan Jia HD</channel>
|
||||
<channel lang="ms" xmltv_id="AstroRania.my" site_id="401">Astro Rania</channel>
|
||||
<channel lang="ms" xmltv_id="AstroRia.my" site_id="193">Astro Ria</channel>
|
||||
<channel lang="ms" xmltv_id="AstroShuangXing.my" site_id="183">Astro Shuang Xing</channel>
|
||||
<channel lang="ms" xmltv_id="AstroSuperSport.my" site_id="154">Astro SuperSport</channel>
|
||||
<channel lang="ms" xmltv_id="AstroSuperSport2.my" site_id="138">Astro SuperSport 2</channel>
|
||||
<channel lang="ms" xmltv_id="AstroSuperSport3.my" site_id="164">Astro SuperSport 3</channel>
|
||||
<channel lang="ms" xmltv_id="AstroSuperSport4.my" site_id="241">Astro SuperSport 4</channel>
|
||||
<channel lang="ms" xmltv_id="AstroSuperSport5.my" site_id="455">Astro SuperSport 5</channel>
|
||||
<channel lang="ms" xmltv_id="AstroTutorTVPT3.my" site_id="410">Astro Tutor TV PT3</channel>
|
||||
<channel lang="ms" xmltv_id="AstroTutorTVSPM.my" site_id="411">Astro Tutor TV SPM</channel>
|
||||
<channel lang="ms" xmltv_id="AstroTutorTVUPSR.my" site_id="412">Astro Tutor TV UPSR</channel>
|
||||
<channel lang="ms" xmltv_id="AstroUHD.my" site_id="308">Astro UHD</channel>
|
||||
<channel lang="ms" xmltv_id="AstroVaanavil.my" site_id="397">Astro Vaanavil</channel>
|
||||
<channel lang="ms" xmltv_id="AstroVellithirai.my" site_id="399">Astro Vellithirai</channel>
|
||||
<channel lang="ms" xmltv_id="AstroVinmeenHD.my" site_id="167">Astro Vinmeen HD</channel>
|
||||
<channel lang="ms" xmltv_id="AstroWahLaiToi.my" site_id="129">Astro Wah Lai Toi</channel>
|
||||
<channel lang="ms" xmltv_id="AstroWarna.my" site_id="272">Astro Warna</channel>
|
||||
<channel lang="ms" xmltv_id="AstroXiaoTaiYang.my" site_id="387">Astro Xiao Tai Yang</channel>
|
||||
<channel lang="ms" xmltv_id="AwesomeTV.my" site_id="433">Awesome TV</channel>
|
||||
<channel lang="ms" xmltv_id="AXNMalaysia.my" site_id="131">AXN</channel>
|
||||
<channel lang="ms" xmltv_id="BBCEarthAsia.uk" site_id="452">BBC Earth</channel>
|
||||
<channel lang="ms" xmltv_id="BBCFirstAsia.uk" site_id="458">BBC First</channel>
|
||||
<channel lang="ms" xmltv_id="BBCLifestyleAsia.uk" site_id="451">BBC Lifestyle</channel>
|
||||
<channel lang="ms" xmltv_id="BBCWorldNewsAsiaPacific.uk" site_id="366">BBC World News</channel>
|
||||
<channel lang="ms" xmltv_id="beINSports.qa" site_id="236">beIN Sports HD</channel>
|
||||
<channel lang="ms" xmltv_id="beINSports2.qa" site_id="466">beIN Sports 2</channel>
|
||||
<channel lang="ms" xmltv_id="beINSports3.qa" site_id="313">beIN Sports 3</channel>
|
||||
<channel lang="ms" xmltv_id="BernamaTV.my" site_id="160">Bernama TV</channel>
|
||||
<channel lang="ms" xmltv_id="BloombergTVAsia.hk" site_id="422">Bloomberg TV</channel>
|
||||
<channel lang="ms" xmltv_id="Boo.my" site_id="251">Boo</channel>
|
||||
<channel lang="ms" xmltv_id="BoomerangSoutheastAsia.us" site_id="430">Boomerang</channel>
|
||||
<channel lang="ms" xmltv_id="CartoonNetworkAsia.sg" site_id="371">Cartoon Network HD</channel>
|
||||
<channel lang="ms" xmltv_id="CCTV4Asia.cn" site_id="385">CCTV 4</channel>
|
||||
<channel lang="ms" xmltv_id="CCM.hk" site_id="187">Celestial Classic Movies</channel>
|
||||
<channel lang="ms" xmltv_id="CelestialMoviesMalaysia.my" site_id="134">Celestial Movies</channel>
|
||||
<channel lang="ms" xmltv_id="CGTN.cn" site_id="426">CGTN</channel>
|
||||
<channel lang="ms" xmltv_id="ChuttiTVMalaysia.my" site_id="51">Chutti TV</channel>
|
||||
<channel lang="ms" xmltv_id="CinemaxAsia.sg" site_id="337">Cinemax</channel>
|
||||
<channel lang="ms" xmltv_id="CNA.sg" site_id="295">CNA</channel>
|
||||
<channel lang="ms" xmltv_id="CNBCAsia.sg" site_id="423">CNBC Asia-Pacific</channel>
|
||||
<channel lang="ms" xmltv_id="CNNInternationalAsiaPacific.hk" site_id="336">CNN</channel>
|
||||
<channel lang="ms" xmltv_id="Colors.in" site_id="365">Colors</channel>
|
||||
<channel lang="ms" xmltv_id="ColorsTamil.in" site_id="298">Colors Tamil</channel>
|
||||
<channel lang="ms" xmltv_id="CrimePlusInvestigationAsia.sg" site_id="369">Crime + Investigation</channel>
|
||||
<channel lang="ms" xmltv_id="CTiAsia.tw" site_id="424">CTI TV</channel>
|
||||
<channel lang="ms" xmltv_id="DiscoveryAsia.sg" site_id="136">Discovery Asia</channel>
|
||||
<channel lang="ms" xmltv_id="DiscoveryChannelIndonesia.id" site_id="376">Discovery Channel</channel>
|
||||
<channel lang="ms" xmltv_id="DMAXSoutheastAsia.sg" site_id="367">DMAX</channel>
|
||||
<channel lang="ms" xmltv_id="DWEnglish.de" site_id="287">DW English</channel>
|
||||
<channel lang="ms" xmltv_id="eGGNetwork.my" site_id="206">Egg Network</channel>
|
||||
<channel lang="ms" xmltv_id="EurosportAsia.fr" site_id="339">Eurosport</channel>
|
||||
<channel lang="ms" xmltv_id="FoodNetworkAsia.sg" site_id="153">Food Network</channel>
|
||||
<channel lang="ms" xmltv_id="France24English.fr" site_id="289">France 24 English</channel>
|
||||
<channel lang="ms" xmltv_id="GolfChannelMalaysia.my" site_id="189">Golf Channel</channel>
|
||||
<channel lang="ms" xmltv_id="GoShopChinese.my" site_id="202">Go Shop Chinese</channel>
|
||||
<channel lang="ms" xmltv_id="GoShopMalay111.my" site_id="403">Go Shop Malay 111</channel>
|
||||
<channel lang="ms" xmltv_id="GoShopMalay118.my" site_id="192">Go Shop Malay 118</channel>
|
||||
<channel lang="ms" xmltv_id="GoShopMalay120.my" site_id="294">Go Shop Malay 120</channel>
|
||||
<channel lang="ms" xmltv_id="HBOAsia.sg" site_id="143">HBO</channel>
|
||||
<channel lang="ms" xmltv_id="HBOFamilyAsia.sg" site_id="450">HBO Family</channel>
|
||||
<channel lang="ms" xmltv_id="HBOHitsAsia.sg" site_id="449">HBO Hits</channel>
|
||||
<channel lang="ms" xmltv_id="HGTVAsia.us" site_id="198">HGTV</channel>
|
||||
<channel lang="ms" xmltv_id="HistoryAsia.us" site_id="144">History</channel>
|
||||
<channel lang="ms" xmltv_id="HITS.sg" site_id="179">Hits</channel>
|
||||
<channel lang="ms" xmltv_id="HITSMovies.sg" site_id="391">Hits Movies</channel>
|
||||
<channel lang="ms" xmltv_id="iQIYI.cn" site_id="355">Iqiyi</channel>
|
||||
<channel lang="ms" xmltv_id="KBSWorld.kr" site_id="161">KBS World</channel>
|
||||
<channel lang="ms" xmltv_id="KIX.hk" site_id="157">Kix</channel>
|
||||
<channel lang="ms" xmltv_id="KPlus.sg" site_id="266">K+</channel>
|
||||
<channel lang="ms" xmltv_id="LifetimeAsia.us" site_id="447">Lifetime</channel>
|
||||
<channel lang="ms" xmltv_id="MoonbugKids.uk" site_id="465">Moonbug Kids</channel>
|
||||
<channel lang="ms" xmltv_id="MTVAsia.sg" site_id="420">MTV</channel>
|
||||
<channel lang="ms" xmltv_id="NatGeoPeopleMalaysia.my" site_id="199">Nat Geo People</channel>
|
||||
<channel lang="ms" xmltv_id="NationalGeographicMalaysia.my" site_id="140">National Geographic</channel>
|
||||
<channel lang="ms" xmltv_id="NationalGeographicWildMalaysia.my" site_id="322">National Geographic Wild</channel>
|
||||
<channel lang="ms" xmltv_id="NHKWorldPremium.jp" site_id="428">NHK World Premium</channel>
|
||||
<channel lang="ms" xmltv_id="NickelodeonAsia.sg" site_id="370">Nickelodeon</channel>
|
||||
<channel lang="ms" xmltv_id="NickJrAsia.sg" site_id="392">Nick Jr</channel>
|
||||
<channel lang="ms" xmltv_id="NjoiTV.my" site_id="302">Njoi TV</channel>
|
||||
<channel lang="ms" xmltv_id="NTV7.my" site_id="93">NTV 7</channel>
|
||||
<channel lang="ms" xmltv_id="OneTVAsia.sg" site_id="133">One</channel>
|
||||
<channel lang="ms" xmltv_id="ParamountNetworkMalaysia.my" site_id="448">Paramount Network</channel>
|
||||
<channel lang="ms" xmltv_id="PhoenixChineseChannel.hk" site_id="382">Phoenix Chinese Channel</channel>
|
||||
<channel lang="ms" xmltv_id="PhoenixInfoNewsChannel.hk" site_id="43">Phoenix InfoNews Channel</channel>
|
||||
<channel lang="ms" xmltv_id="PremierSports1Asia.ie" site_id="393">Premier Sports</channel>
|
||||
<channel lang="ms" xmltv_id="PRIMEtime.my" site_id="453">PRIMEtime</channel>
|
||||
<channel lang="ms" xmltv_id="TV1.my" site_id="395">RTM TV 1</channel>
|
||||
<channel lang="ms" xmltv_id="TV2.my" site_id="396">RTM TV2</channel>
|
||||
<channel lang="ms" xmltv_id="Okey.my" site_id="97">RTM TV Okey</channel>
|
||||
<channel lang="ms" xmltv_id="ShowcaseMovies.my" site_id="454">Showcase Movies</channel>
|
||||
<channel lang="ms" xmltv_id="SkyNews.uk" site_id="155">Sky News UK</channel>
|
||||
<channel lang="ms" xmltv_id="SPOTV.kr" site_id="456">SPOTV</channel>
|
||||
<channel lang="ms" xmltv_id="StarVijay.in" site_id="357">Star Vijay</channel>
|
||||
<channel lang="ms" xmltv_id="SunMusic.in" site_id="417">Sun Music</channel>
|
||||
<channel lang="ms" xmltv_id="SunTVMalaysia.my" site_id="358">Sun TV</channel>
|
||||
<channel lang="ms" xmltv_id="TADAA.my" site_id="432">Ta-Daa!</channel>
|
||||
<channel lang="ms" xmltv_id="TLCSoutheastAsia.sg" site_id="338">TLC</channel>
|
||||
<channel lang="ms" xmltv_id="TV3.my" site_id="106">TV 3</channel>
|
||||
<channel lang="ms" xmltv_id="TV9.my" site_id="48">TV 9</channel>
|
||||
<channel lang="ms" xmltv_id="TVAlhijrah.my" site_id="149">TV Alhijrah</channel>
|
||||
<channel lang="ms" xmltv_id="TVBClassic.hk" site_id="425">TVB Classic</channel>
|
||||
<channel lang="ms" xmltv_id="EntertainmentNews.hk" site_id="427">TVB Entertainment News</channel>
|
||||
<channel lang="ms" xmltv_id="Jade.hk" site_id="203">TVB Jade</channel>
|
||||
<channel lang="ms" xmltv_id="TVBSAsia.tw" site_id="384">TVBS Asia</channel>
|
||||
<channel lang="ms" xmltv_id="TVBXingHe.hk" site_id="383">TVB Xing He</channel>
|
||||
<channel lang="ms" xmltv_id="tvNAsia.hk" site_id="190">TVN HD</channel>
|
||||
<channel lang="ms" xmltv_id="tvNMoviesAsia.hk" site_id="274">TVN Movies</channel>
|
||||
<channel lang="ms" xmltv_id="TVS.my" site_id="429">TVS</channel>
|
||||
<channel lang="ms" xmltv_id="WarnerTVAsia.us" site_id="270">Warner TV</channel>
|
||||
<channel lang="ms" xmltv_id="WWENetwork.us" site_id="194">WWE Network</channel>
|
||||
<channel lang="ms" xmltv_id="ZeeTamil.in" site_id="297">Zee Tamil</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<!-- <channel site="astro.com.my" lang="ms" xmltv_id="" site_id="461">ABC</channel> -->
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="8TV.my" site_id="115">8TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AdithyaTV.in" site_id="67">Adithya TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AlJazeeraEnglish.qa" site_id="374">Aljazeera</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AnimalPlanetMalaysia.my" site_id="377">Animal Planet</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AsianFoodNetwork.sg" site_id="91">Asian Food Network</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroAEC.my" site_id="182">Astro AEC</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroAOD311.my" site_id="172">Astro AOD 311</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroAOD352.my" site_id="87">Astro AOD 352</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroAOD353.my" site_id="114">Astro AOD 353</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroAOD354.my" site_id="65">Astro AOD 354</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroAOD355.my" site_id="66">Astro AOD 355</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroArena.my" site_id="235">Astro Arena</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroArena2.my" site_id="457">Astro Arena 2</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroAura.my" site_id="400">Astro Aura</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroAwani.my" site_id="436">Astro Awani</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroBollyOneHD.my" site_id="178">Astro BollyOne HD</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroBoxOfficeTayanganHebat.my" site_id="176">Astro Box Office Tayangan Hebat</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroBoxOfficeThangathirai.my" site_id="177">Astro Box Office Thangathirai</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroCeria.my" site_id="386">Astro Ceria</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroCh100.my" site_id="471">Gemilang</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroCitra.my" site_id="301">Astro Citra</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroCricket.my" site_id="197">Astro Cricket</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroHuaHeeDai.my" site_id="162">Astro Hua Hee Dai</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroOasis.my" site_id="315">Astro Oasis</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroPrima.my" site_id="316">Astro Prima</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroQuanJiaHD.my" site_id="158">Astro Quan Jia HD</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroRania.my" site_id="401">Astro Rania</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroRia.my" site_id="193">Astro Ria</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroShuangXing.my" site_id="183">Astro Shuang Xing</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroSuperSport.my" site_id="154">Astro SuperSport</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroSuperSport2.my" site_id="138">Astro SuperSport 2</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroSuperSport3.my" site_id="164">Astro SuperSport 3</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroSuperSport4.my" site_id="241">Astro SuperSport 4</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroSuperSport5.my" site_id="455">Astro SuperSport 5</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroTutorTVPT3.my" site_id="410">Astro Tutor TV PT3</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroTutorTVSPM.my" site_id="411">Astro Tutor TV SPM</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroTutorTVUPSR.my" site_id="412">Astro Tutor TV UPSR</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroUHD.my" site_id="308">Astro UHD</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroVaanavil.my" site_id="397">Astro Vaanavil</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroVellithirai.my" site_id="399">Astro Vellithirai</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroVinmeenHD.my" site_id="167">Astro Vinmeen HD</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroWahLaiToi.my" site_id="129">Astro Wah Lai Toi</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroWarna.my" site_id="272">Astro Warna</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AstroXiaoTaiYang.my" site_id="387">Astro Xiao Tai Yang</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AwesomeTV.my" site_id="433">Awesome TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="AXNMalaysia.my" site_id="131">AXN</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="BBCEarthAsia.uk" site_id="452">BBC Earth</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="BBCFirstAsia.uk" site_id="458">BBC First</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="BBCLifestyleAsia.uk" site_id="451">BBC Lifestyle</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="BBCWorldNewsAsiaPacific.uk" site_id="366">BBC World News</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="beINSports.qa" site_id="236">beIN Sports HD</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="beINSports2.qa" site_id="466">beIN Sports 2</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="beINSports3.qa" site_id="313">beIN Sports 3</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="BernamaTV.my" site_id="160">Bernama TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="BloombergTVAsia.hk" site_id="422">Bloomberg TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="Boo.my" site_id="251">Boo</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="BoomerangSoutheastAsia.us" site_id="430">Boomerang</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CartoonNetworkAsia.sg" site_id="371">Cartoon Network HD</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CCM.hk" site_id="187">Celestial Classic Movies</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CCTV4Asia.cn" site_id="385">CCTV 4</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CelestialMoviesMalaysia.my" site_id="134">Celestial Movies</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CGTN.cn" site_id="426">CGTN</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="ChuttiTVMalaysia.my" site_id="51">Chutti TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CinemaxAsia.sg" site_id="337">Cinemax</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CNA.sg" site_id="295">CNA</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CNBCAsia.sg" site_id="423">CNBC Asia-Pacific</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CNNInternationalAsiaPacific.hk" site_id="336">CNN</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="Colors.in" site_id="365">Colors</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="ColorsTamil.in" site_id="298">Colors Tamil</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CrimePlusInvestigationAsia.sg" site_id="369">Crime + Investigation</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="CTiAsia.tw" site_id="424">CTI TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="DiscoveryAsia.sg" site_id="136">Discovery Asia</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="DiscoveryChannelIndonesia.id" site_id="376">Discovery Channel</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="DMAXSoutheastAsia.sg" site_id="367">DMAX</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="DWEnglish.de" site_id="287">DW English</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="eGGNetwork.my" site_id="206">Egg Network</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="EntertainmentNews.hk" site_id="427">TVB Entertainment News</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="EurosportAsia.fr" site_id="339">Eurosport</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="FoodNetworkAsia.sg" site_id="153">Food Network</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="France24English.fr" site_id="289">France 24 English</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="GolfChannelMalaysia.my" site_id="189">Golf Channel</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="GoShopChinese.my" site_id="202">Go Shop Chinese</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="GoShopMalay111.my" site_id="403">Go Shop Malay 111</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="GoShopMalay118.my" site_id="192">Go Shop Malay 118</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="GoShopMalay120.my" site_id="294">Go Shop Malay 120</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="HBOAsia.sg" site_id="143">HBO</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="HBOFamilyAsia.sg" site_id="450">HBO Family</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="HBOHitsAsia.sg" site_id="449">HBO Hits</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="HGTVAsia.us" site_id="198">HGTV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="HistoryAsia.us" site_id="144">History</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="HITS.sg" site_id="179">Hits</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="HITSMovies.sg" site_id="391">Hits Movies</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="iQIYI.cn" site_id="355">Iqiyi</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="Jade.hk" site_id="203">TVB Jade</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="KBSWorld.kr" site_id="161">KBS World</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="KIX.hk" site_id="157">Kix</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="KPlus.sg" site_id="266">K+</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="LifetimeAsia.us" site_id="447">Lifetime</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="MoonbugKids.uk" site_id="465">Moonbug Kids</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="MTVAsia.sg" site_id="420">MTV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="NatGeoPeopleMalaysia.my" site_id="199">Nat Geo People</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="NationalGeographicMalaysia.my" site_id="140">National Geographic</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="NationalGeographicWildMalaysia.my" site_id="322">National Geographic Wild</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="NHKWorldPremium.jp" site_id="428">NHK World Premium</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="NickelodeonAsia.sg" site_id="370">Nickelodeon</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="NickJrAsia.sg" site_id="392">Nick Jr</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="NjoiTV.my" site_id="302">Njoi TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="NTV7.my" site_id="93">NTV 7</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="Okey.my" site_id="97">RTM TV Okey</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="OneTVAsia.sg" site_id="133">One</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="ParamountNetworkMalaysia.my" site_id="448">Paramount Network</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="PhoenixChineseChannel.hk" site_id="382">Phoenix Chinese Channel</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="PhoenixInfoNewsChannel.hk" site_id="43">Phoenix InfoNews Channel</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="PremierSports1Asia.ie" site_id="393">Premier Sports</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="PRIMEtime.my" site_id="453">PRIMEtime</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="ShowcaseMovies.my" site_id="454">Showcase Movies</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="SkyNews.uk" site_id="155">Sky News UK</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="SPOTV.kr" site_id="456">SPOTV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="StarVijay.in" site_id="357">Star Vijay</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="SunMusic.in" site_id="417">Sun Music</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="SunTVMalaysia.my" site_id="358">Sun TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TADAA.my" site_id="432">Ta-Daa!</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TLCSoutheastAsia.sg" site_id="338">TLC</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TV1.my" site_id="395">RTM TV 1</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TV2.my" site_id="396">RTM TV2</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TV3.my" site_id="106">TV 3</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TV9.my" site_id="48">TV 9</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TVAlhijrah.my" site_id="149">TV Alhijrah</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TVBClassic.hk" site_id="425">TVB Classic</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TVBSAsia.tw" site_id="384">TVBS Asia</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TVBXingHe.hk" site_id="383">TVB Xing He</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="tvNAsia.hk" site_id="190">TVN HD</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="tvNMoviesAsia.hk" site_id="274">TVN Movies</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="TVS.my" site_id="429">TVS</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="WarnerTVAsia.us" site_id="270">Warner TV</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="WWENetwork.us" site_id="194">WWE Network</channel>
|
||||
<channel site="astro.com.my" lang="ms" xmltv_id="ZeeTamil.in" site_id="297">Zee Tamil</channel>
|
||||
</channels>
|
||||
|
|
|
@ -4,7 +4,7 @@ const utc = require('dayjs/plugin/utc')
|
|||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const API_ENDPOINT = `https://contenthub-api.eco.astro.com.my`
|
||||
const API_ENDPOINT = 'https://contenthub-api.eco.astro.com.my'
|
||||
|
||||
module.exports = {
|
||||
site: 'astro.com.my',
|
||||
|
@ -41,13 +41,13 @@ module.exports = {
|
|||
}
|
||||
|
||||
function parseEpisode(item) {
|
||||
const [_, number] = item.title.match(/Ep(\d+)$/) || [null, null]
|
||||
const [, number] = item.title.match(/Ep(\d+)$/) || [null, null]
|
||||
|
||||
return number ? parseInt(number) : null
|
||||
}
|
||||
|
||||
function parseSeason(details) {
|
||||
const [_, season] = details.title ? details.title.match(/ S(\d+)/) || [null, null] : [null, null]
|
||||
const [, season] = details.title ? details.title.match(/ S(\d+)/) || [null, null] : [null, null]
|
||||
|
||||
return season ? parseInt(season) : null
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ async function loadProgramDetails(item) {
|
|||
const data = await axios
|
||||
.get(url)
|
||||
.then(r => r.data)
|
||||
.catch(err => {})
|
||||
.catch(error => console.log(error.message))
|
||||
if (!data) return {}
|
||||
|
||||
return data.response || {}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// npx epg-grabber --config=sites/astro.com.my/astro.com.my.config.js --channels=sites/astro.com.my/astro.com.my.channels.xml --output=guide.xml --timeout=30000 --days=2
|
||||
// npm run grab -- --site=astro.com.my
|
||||
|
||||
const { parser, url } = require('./astro.com.my.config.js')
|
||||
const fs = require('fs')
|
||||
|
|
|
@ -1,162 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="bein.com">
|
||||
<channels>
|
||||
<!-- If updating, note that the English and Arabic channels are in a different order so have different IDs -->
|
||||
<channel lang="en" xmltv_id="beINSportsNews.qa" site_id="sports#1">BeIn Sports News</channel>
|
||||
<channel lang="en" xmltv_id="beINSports.qa" site_id="sports#2">BeIn Sports</channel>
|
||||
<channel lang="en" xmltv_id="beINSports1.qa" site_id="sports#3">BeIn Sports 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSports2.qa" site_id="sports#4">BeIn Sports 2</channel>
|
||||
<channel lang="en" xmltv_id="beINSports3.qa" site_id="sports#5">BeIn Sports 3</channel>
|
||||
<channel lang="en" xmltv_id="beINSports4.qa" site_id="sports#6">BeIn Sports 4</channel>
|
||||
<channel lang="en" xmltv_id="beINSports5.qa" site_id="sports#7">BeIn Sports 5</channel>
|
||||
<channel lang="en" xmltv_id="beINSports6.qa" site_id="sports#8">BeIn Sports 6</channel>
|
||||
<channel lang="en" xmltv_id="beINSports7.qa" site_id="sports#9">BeIn Sports 7</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsPremium1.qa" site_id="sports#10">BeIn Sports Premium 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsPremium2.qa" site_id="sports#11">BeIn Sports Premium 2</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsPremium3.qa" site_id="sports#12">BeIn Sports Premium 3</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsXtra1.qa" site_id="sports#13">BeIn Sports Xtra 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsXtra2.qa" site_id="sports#14">BeIn Sports Xtra 2</channel>
|
||||
<channel lang="en" xmltv_id="beIN4K.qa" site_id="sports#15">BeIn 4K</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsAFC.qa" site_id="sports#16">BeIN Sports AFC</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsAFC1.qa" site_id="sports#17">BeIN Sports AFC 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsAFC2.qa" site_id="sports#18">BeIN Sports AFC 2</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsAFC3.qa" site_id="sports#19">BeIN Sports AFC 3</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsEnglish1.qa" site_id="sports#20">BeIn Sports English 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsEnglish2.qa" site_id="sports#21">BeIn Sports English 2</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsEnglish3.qa" site_id="sports#22">BeIn Sports English 3</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsNBA.qa" site_id="sports#23">BeIn NBA</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsFrench1.qa" site_id="sports#24">BeIn Sports French 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsFrench2.qa" site_id="sports#25">BeIn Sports French 2</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsFrench3.qa" site_id="sports#26">BeIn Sports French 3</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsMax1.qa" site_id="sports#27">beIN Sports Max 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsMax2.qa" site_id="sports#28">beIN Sports Max 2</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsMax3.qa" site_id="sports#29">beIN Sports Max 3</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsMax4.qa" site_id="sports#30">beIN Sports Max 4</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsMax5.qa" site_id="sports#31">beIN Sports Max 5</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsMax6.qa" site_id="sports#32">beIN Sports Max 6</channel>
|
||||
<channel lang="en" xmltv_id="AlkassOne.qa" site_id="sports#33">Alkass One</channel>
|
||||
<channel lang="en" xmltv_id="AlkassTwo.qa" site_id="sports#34">Alkass Two</channel>
|
||||
<channel lang="en" xmltv_id="AlkassThree.qa" site_id="sports#35">Alkass Three</channel>
|
||||
<channel lang="en" xmltv_id="AlkassFour.qa" site_id="sports#36">Alkass Four</channel>
|
||||
<channel lang="en" xmltv_id="AlkassFive.qa" site_id="sports#37">Alkass Five</channel>
|
||||
<channel lang="en" xmltv_id="AlkassSix.qa" site_id="sports#38">Alkass Six</channel>
|
||||
<channel lang="en" xmltv_id="AlkassSeven.qa" site_id="sports#39">Alkass Seven</channel>
|
||||
<channel lang="en" xmltv_id="AlkassEight.qa" site_id="sports#40">Alkass Eight</channel>
|
||||
|
||||
<channel lang="ar" xmltv_id="beINSportsNews.qa" site_id="sports#1">BeIn Sports News</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports.qa" site_id="sports#2">BeIn Sports</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports1.qa" site_id="sports#3">BeIn Sports 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports2.qa" site_id="sports#4">BeIn Sports 2</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports3.qa" site_id="sports#5">BeIn Sports 3</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports4.qa" site_id="sports#6">BeIn Sports 4</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports5.qa" site_id="sports#7">BeIn Sports 5</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports6.qa" site_id="sports#8">BeIn Sports 6</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports7.qa" site_id="sports#9">BeIn Sports 7</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsPremium1.qa" site_id="sports#10">BeIn Sports Premium 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsPremium2.qa" site_id="sports#11">BeIn Sports Premium 2</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsPremium3.qa" site_id="sports#12">BeIn Sports Premium 3</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsXtra1.qa" site_id="sports#13">BeIn Sports Xtra 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsXtra2.qa" site_id="sports#14">BeIn Sports Xtra 2</channel>
|
||||
<channel lang="ar" xmltv_id="beIN4K.qa" site_id="sports#15">BeIn 4K</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsAFC.qa" site_id="sports#16">BeIN Sports AFC</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsAFC1.qa" site_id="sports#17">BeIN Sports AFC 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsAFC2.qa" site_id="sports#18">BeIN Sports AFC 2</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsAFC3.qa" site_id="sports#19">BeIN Sports AFC 3</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsEnglish1.qa" site_id="sports#20">BeIn Sports English 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsEnglish2.qa" site_id="sports#21">BeIn Sports English 2</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsEnglish3.qa" site_id="sports#22">BeIn Sports English 3</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsNBA.qa" site_id="sports#23">BeIn NBA</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsFrench1.qa" site_id="sports#24">BeIn Sports French 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsFrench2.qa" site_id="sports#25">BeIn Sports French 2</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsFrench3.qa" site_id="sports#26">BeIn Sports French 3</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsMax1.qa" site_id="sports#27">beIN Sports Max 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsMax2.qa" site_id="sports#28">beIN Sports Max 2</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsMax3.qa" site_id="sports#29">beIN Sports Max 3</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsMax4.qa" site_id="sports#30">beIN Sports Max 4</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsMax5.qa" site_id="sports#31">beIN Sports Max 5</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsMax6.qa" site_id="sports#32">beIN Sports Max 6</channel>
|
||||
<channel lang="ar" xmltv_id="AlkassThree.qa" site_id="sports#33">Alkass Three</channel>
|
||||
<channel lang="ar" xmltv_id="AlkassOne.qa" site_id="sports#34">Alkass One</channel>
|
||||
<channel lang="ar" xmltv_id="AlkassTwo.qa" site_id="sports#35">Alkass Two</channel>
|
||||
<channel lang="ar" xmltv_id="AlkassFour.qa" site_id="sports#36">Alkass Four</channel>
|
||||
<channel lang="ar" xmltv_id="AlkassFive.qa" site_id="sports#37">Alkass Five</channel>
|
||||
<channel lang="ar" xmltv_id="AlkassSix.qa" site_id="sports#38">Alkass Six</channel>
|
||||
<channel lang="ar" xmltv_id="AlkassSeven.qa" site_id="sports#39">Alkass Seven</channel>
|
||||
<channel lang="ar" xmltv_id="AlkassEight.qa" site_id="sports#40">Alkass Eight</channel>
|
||||
|
||||
<channel lang="en" xmltv_id="beINMovies1Premiere.qa" site_id="entertainment#1">beIN Movies Premiere</channel>
|
||||
<channel lang="en" xmltv_id="beINMovies2Action.qa" site_id="entertainment#2">beIN Movies Action</channel>
|
||||
<channel lang="en" xmltv_id="beINMovies3Drama.qa" site_id="entertainment#3">bein Movies Drama</channel>
|
||||
<channel lang="en" xmltv_id="beINMovies4Family.qa" site_id="entertainment#4">beIN Movies Family</channel>
|
||||
<channel lang="en" xmltv_id="FoxMoviesMiddleEast.us" site_id="entertainment#5">FOX Movies</channel>
|
||||
<channel lang="en" xmltv_id="FoxActionMoviesMiddleEast.hk" site_id="entertainment#6">FOX Action Movies</channel>
|
||||
<channel lang="en" xmltv_id="StarMoviesMiddleEast.ae" site_id="entertainment#7">Star Movies</channel>
|
||||
<channel lang="en" xmltv_id="beINSeries1.qa" site_id="entertainment#8">beIN Series 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSeries2.qa" site_id="entertainment#9">beIN Series 2</channel>
|
||||
<channel lang="en" xmltv_id="beINDrama1.qa" site_id="entertainment#10">beIN Drama 1</channel>
|
||||
<channel lang="en" xmltv_id="beINGourmet.qa" site_id="entertainment#11">beIN Gourmet</channel>
|
||||
<channel lang="en" xmltv_id="TravelChannelEMEA.uk" site_id="entertainment#12">Travel Channel</channel>
|
||||
<channel lang="en" xmltv_id="FoxArabia.ae" site_id="entertainment#13">FOX</channel>
|
||||
<channel lang="en" xmltv_id="FoodNetworkEMEA.us" site_id="entertainment#14">Food Network</channel>
|
||||
<channel lang="en" xmltv_id="HGTVArabia.us" site_id="entertainment#15">HGTV</channel>
|
||||
<channel lang="en" xmltv_id="StarWorldMiddleEast.ae" site_id="entertainment#16">Star World</channel>
|
||||
<channel lang="en" xmltv_id="Fatafeat.ae" site_id="entertainment#17">Fatafeat</channel>
|
||||
<channel lang="en" xmltv_id="FoxLifeMiddleEast.ae" site_id="entertainment#18">FOX Life</channel>
|
||||
<channel lang="en" xmltv_id="MTV80s.uk" site_id="entertainment#19">MTV 80s</channel>
|
||||
<channel lang="en" xmltv_id="MTV90s.uk" site_id="entertainment#20">MTV 90s</channel>
|
||||
<channel lang="en" xmltv_id="ClubMTVEurope.uk" site_id="entertainment#21">Club MTV</channel>
|
||||
<channel lang="en" xmltv_id="BloombergTVMiddleEast.ae" site_id="entertainment#22">Bloomberg TV</channel>
|
||||
<channel lang="en" xmltv_id="NationalGeographicMiddleEast.uk" site_id="entertainment#23">National Geographic</channel>
|
||||
<channel lang="en" xmltv_id="NationalGeographicWildMiddleEast.uk" site_id="entertainment#24">National Geographic Wild</channel>
|
||||
<channel lang="en" xmltv_id="BBCEarthMiddleEast.uk" site_id="entertainment#25">BBC Earth</channel>
|
||||
<channel lang="en" xmltv_id="CNNArabic.ae" site_id="entertainment#26">CNN</channel>
|
||||
<channel lang="en" xmltv_id="EuronewsEnglish.fr" site_id="entertainment#27">EuroNews</channel>
|
||||
<channel lang="en" xmltv_id="DiscoveryChannelMiddleEastAfrica.us" site_id="entertainment#28">Discovery</channel>
|
||||
<channel lang="en" xmltv_id="BeJunior.qa" site_id="entertainment#29">be Junior</channel>
|
||||
<channel lang="en" xmltv_id="JeemTV.qa" site_id="entertainment#30">Jeem</channel>
|
||||
<channel lang="en" xmltv_id="Baraem.qa" site_id="entertainment#31">Baraem</channel>
|
||||
<channel lang="en" xmltv_id="CartoonNetworkMENA.uk" site_id="entertainment#32">Cartoon Network</channel>
|
||||
<channel lang="en" xmltv_id="CartoonNetworkArabic.ae" site_id="entertainment#33">Cartoon Network Arabic</channel>
|
||||
<channel lang="en" xmltv_id="CartoonNetworkHindi.in" site_id="entertainment#34">Cartoon Network Hindi</channel>
|
||||
<channel lang="en" xmltv_id="BabyTV.uk" site_id="entertainment#35">Baby TV</channel>
|
||||
<channel lang="en" xmltv_id="CBeebiesMiddleEast.uk" site_id="entertainment#36">CBeebies</channel>
|
||||
<channel lang="en" xmltv_id="DreamWorksChannelMiddleEast.us" site_id="entertainment#37">DreamWorks</channel>
|
||||
|
||||
<channel lang="ar" xmltv_id="beINMovies1Premiere.qa" site_id="entertainment#1">beIN Movies Premiere</channel>
|
||||
<channel lang="ar" xmltv_id="beINMovies2Action.qa" site_id="entertainment#2">beIN Movies Action</channel>
|
||||
<channel lang="ar" xmltv_id="beINMovies3Drama.qa" site_id="entertainment#3">bein Movies Drama</channel>
|
||||
<channel lang="ar" xmltv_id="beINMovies4Family.qa" site_id="entertainment#4">beIN Movies Family</channel>
|
||||
<channel lang="ar" xmltv_id="FoxMoviesMiddleEast.us" site_id="entertainment#5">FOX Movies</channel>
|
||||
<channel lang="ar" xmltv_id="FoxActionMoviesMiddleEast.hk" site_id="entertainment#6">FOX Action Movies</channel>
|
||||
<channel lang="ar" xmltv_id="StarMoviesMiddleEast.ae" site_id="entertainment#7">Star Movies</channel>
|
||||
<channel lang="ar" xmltv_id="beINSeries1.qa" site_id="entertainment#8">beIN Series 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSeries2.qa" site_id="entertainment#9">beIN Series 2</channel>
|
||||
<channel lang="ar" xmltv_id="beINDrama1.qa" site_id="entertainment#10">beIN Drama 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINGourmet.qa" site_id="entertainment#11">beIN Gourmet</channel>
|
||||
<channel lang="ar" xmltv_id="TravelChannelEMEA.uk" site_id="entertainment#12">Travel Channel</channel>
|
||||
<channel lang="ar" xmltv_id="FoxArabia.ae" site_id="entertainment#13">FOX</channel>
|
||||
<channel lang="ar" xmltv_id="FoodNetworkEMEA.us" site_id="entertainment#14">Food Network</channel>
|
||||
<channel lang="ar" xmltv_id="HGTVArabia.us" site_id="entertainment#15">HGTV</channel>
|
||||
<channel lang="ar" xmltv_id="StarWorldMiddleEast.ae" site_id="entertainment#16">Star World</channel>
|
||||
<channel lang="ar" xmltv_id="Fatafeat.ae" site_id="entertainment#17">Fatafeat</channel>
|
||||
<channel lang="ar" xmltv_id="MTV80s.uk" site_id="entertainment#18">MTV 80s</channel>
|
||||
<channel lang="ar" xmltv_id="MTV90s.uk" site_id="entertainment#19">MTV 90s</channel>
|
||||
<channel lang="ar" xmltv_id="ClubMTVEurope.uk" site_id="entertainment#20">Club MTV</channel>
|
||||
<channel lang="ar" xmltv_id="BeJunior.qa" site_id="entertainment#21">be Junior</channel>
|
||||
<channel lang="ar" xmltv_id="BloombergTVMiddleEast.ae" site_id="entertainment#22">Bloomberg TV</channel>
|
||||
<channel lang="ar" xmltv_id="NationalGeographicMiddleEast.uk" site_id="entertainment#23">National Geographic</channel>
|
||||
<channel lang="ar" xmltv_id="NationalGeographicWildMiddleEast.uk" site_id="entertainment#24">National Geographic Wild</channel>
|
||||
<channel lang="ar" xmltv_id="BBCEarthMiddleEast.uk" site_id="entertainment#25">BBC Earth</channel>
|
||||
<channel lang="ar" xmltv_id="AlJazeeraDocumentary.qa" site_id="entertainment#26">Al Jazeera Documentary</channel>
|
||||
<channel lang="ar" xmltv_id="CNNArabic.ae" site_id="entertainment#27">CNN</channel>
|
||||
<channel lang="ar" xmltv_id="EuronewsEnglish.fr" site_id="entertainment#28">EuroNews</channel>
|
||||
<channel lang="ar" xmltv_id="JeemTV.qa" site_id="entertainment#29">Jeem</channel>
|
||||
<channel lang="ar" xmltv_id="Baraem.qa" site_id="entertainment#30">Baraem</channel>
|
||||
<channel lang="ar" xmltv_id="CBeebiesMiddleEast.uk" site_id="entertainment#31">CBeebies</channel>
|
||||
<channel lang="ar" xmltv_id="BabyTV.uk" site_id="entertainment#32">Baby TV</channel>
|
||||
<channel lang="ar" xmltv_id="CartoonNetworkMENA.uk" site_id="entertainment#33">Cartoon Network</channel>
|
||||
<channel lang="ar" xmltv_id="CartoonNetworkArabic.ae" site_id="entertainment#34">Cartoon Network Arabic</channel>
|
||||
<channel lang="ar" xmltv_id="CartoonNetworkHindi.in" site_id="entertainment#35">Cartoon Network Hindi</channel>
|
||||
<channel lang="ar" xmltv_id="DreamWorksChannelMiddleEast.us" site_id="entertainment#36">DreamWorks</channel>
|
||||
</channels>
|
||||
</site>
|
|
@ -4,7 +4,6 @@ const { DateTime } = require('luxon')
|
|||
module.exports = {
|
||||
site: 'bein.com',
|
||||
days: 2,
|
||||
timeout: 30000, // 30 seconds
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
|
@ -63,7 +62,7 @@ function parseCategory($item) {
|
|||
}
|
||||
|
||||
function parseTime($item, date) {
|
||||
let [_, time] = $item('.time')
|
||||
let [, time] = $item('.time')
|
||||
.text()
|
||||
.match(/^(\d{2}:\d{2})/) || [null, null]
|
||||
if (!time) return null
|
||||
|
@ -73,7 +72,7 @@ function parseTime($item, date) {
|
|||
}
|
||||
|
||||
function parseItems(content, channel) {
|
||||
const [_, channelId] = channel.site_id.split('#')
|
||||
const [, channelId] = channel.site_id.split('#')
|
||||
const $ = cheerio.load(content)
|
||||
|
||||
return $(`#channels_${channelId} .slider > ul:first-child > li`).toArray()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// npx epg-grabber --config=sites/bein.com/bein.com.config.js --channels=sites/bein.com/bein.com.channels.xml --output=guide.xml
|
||||
// npm run grab -- --site=bein.com
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
|
79
sites/bein.com/bein.com_ar.channels.xml
Normal file
79
sites/bein.com/bein.com_ar.channels.xml
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channels>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlJazeeraDocumentary.qa" site_id="entertainment#26">Al Jazeera Documentary</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlkassEight.qa" site_id="sports#40">Alkass Eight</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlkassFive.qa" site_id="sports#37">Alkass Five</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlkassFour.qa" site_id="sports#36">Alkass Four</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlkassOne.qa" site_id="sports#34">Alkass One</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlkassSeven.qa" site_id="sports#39">Alkass Seven</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlkassSix.qa" site_id="sports#38">Alkass Six</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlkassThree.qa" site_id="sports#33">Alkass Three</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="AlkassTwo.qa" site_id="sports#35">Alkass Two</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="BabyTV.uk" site_id="entertainment#32">Baby TV</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="Baraem.qa" site_id="entertainment#30">Baraem</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="BBCEarthMiddleEast.uk" site_id="entertainment#25">BBC Earth</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beIN4K.qa" site_id="sports#15">BeIn 4K</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINDrama1.qa" site_id="entertainment#10">beIN Drama 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINGourmet.qa" site_id="entertainment#11">beIN Gourmet</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINMovies1Premiere.qa" site_id="entertainment#1">beIN Movies Premiere</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINMovies2Action.qa" site_id="entertainment#2">beIN Movies Action</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINMovies3Drama.qa" site_id="entertainment#3">bein Movies Drama</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINMovies4Family.qa" site_id="entertainment#4">beIN Movies Family</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSeries1.qa" site_id="entertainment#8">beIN Series 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSeries2.qa" site_id="entertainment#9">beIN Series 2</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSports.qa" site_id="sports#2">BeIn Sports</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSports1.qa" site_id="sports#3">BeIn Sports 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSports2.qa" site_id="sports#4">BeIn Sports 2</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSports3.qa" site_id="sports#5">BeIn Sports 3</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSports4.qa" site_id="sports#6">BeIn Sports 4</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSports5.qa" site_id="sports#7">BeIn Sports 5</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSports6.qa" site_id="sports#8">BeIn Sports 6</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSports7.qa" site_id="sports#9">BeIn Sports 7</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsAFC.qa" site_id="sports#16">BeIN Sports AFC</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsAFC1.qa" site_id="sports#17">BeIN Sports AFC 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsAFC2.qa" site_id="sports#18">BeIN Sports AFC 2</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsAFC3.qa" site_id="sports#19">BeIN Sports AFC 3</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsEnglish1.qa" site_id="sports#20">BeIn Sports English 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsEnglish2.qa" site_id="sports#21">BeIn Sports English 2</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsEnglish3.qa" site_id="sports#22">BeIn Sports English 3</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsFrench1.qa" site_id="sports#24">BeIn Sports French 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsFrench2.qa" site_id="sports#25">BeIn Sports French 2</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsFrench3.qa" site_id="sports#26">BeIn Sports French 3</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsMax1.qa" site_id="sports#27">beIN Sports Max 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsMax2.qa" site_id="sports#28">beIN Sports Max 2</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsMax3.qa" site_id="sports#29">beIN Sports Max 3</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsMax4.qa" site_id="sports#30">beIN Sports Max 4</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsMax5.qa" site_id="sports#31">beIN Sports Max 5</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsMax6.qa" site_id="sports#32">beIN Sports Max 6</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsNBA.qa" site_id="sports#23">BeIn NBA</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsNews.qa" site_id="sports#1">BeIn Sports News</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsPremium1.qa" site_id="sports#10">BeIn Sports Premium 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsPremium2.qa" site_id="sports#11">BeIn Sports Premium 2</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsPremium3.qa" site_id="sports#12">BeIn Sports Premium 3</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsXtra1.qa" site_id="sports#13">BeIn Sports Xtra 1</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="beINSportsXtra2.qa" site_id="sports#14">BeIn Sports Xtra 2</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="BeJunior.qa" site_id="entertainment#21">be Junior</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="BloombergTVMiddleEast.ae" site_id="entertainment#22">Bloomberg TV</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="CartoonNetworkArabic.ae" site_id="entertainment#34">Cartoon Network Arabic</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="CartoonNetworkHindi.in" site_id="entertainment#35">Cartoon Network Hindi</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="CartoonNetworkMENA.uk" site_id="entertainment#33">Cartoon Network</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="CBeebiesMiddleEast.uk" site_id="entertainment#31">CBeebies</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="ClubMTVEurope.uk" site_id="entertainment#20">Club MTV</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="CNNArabic.ae" site_id="entertainment#27">CNN</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="DreamWorksChannelMiddleEast.us" site_id="entertainment#36">DreamWorks</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="EuronewsEnglish.fr" site_id="entertainment#28">EuroNews</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="Fatafeat.ae" site_id="entertainment#17">Fatafeat</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="FoodNetworkEMEA.us" site_id="entertainment#14">Food Network</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="FoxActionMoviesMiddleEast.hk" site_id="entertainment#6">FOX Action Movies</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="FoxArabia.ae" site_id="entertainment#13">FOX</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="FoxMoviesMiddleEast.us" site_id="entertainment#5">FOX Movies</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="HGTVArabia.us" site_id="entertainment#15">HGTV</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="JeemTV.qa" site_id="entertainment#29">Jeem</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="MTV80s.uk" site_id="entertainment#18">MTV 80s</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="MTV90s.uk" site_id="entertainment#19">MTV 90s</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="NationalGeographicMiddleEast.uk" site_id="entertainment#23">National Geographic</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="NationalGeographicWildMiddleEast.uk" site_id="entertainment#24">National Geographic Wild</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="StarMoviesMiddleEast.ae" site_id="entertainment#7">Star Movies</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="StarWorldMiddleEast.ae" site_id="entertainment#16">Star World</channel>
|
||||
<channel site="bein.com" lang="ar" xmltv_id="TravelChannelEMEA.uk" site_id="entertainment#12">Travel Channel</channel>
|
||||
</channels>
|
80
sites/bein.com/bein.com_en.channels.xml
Normal file
80
sites/bein.com/bein.com_en.channels.xml
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channels>
|
||||
<channel site="bein.com" lang="en" xmltv_id="AlkassEight.qa" site_id="sports#40">Alkass Eight</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="AlkassFive.qa" site_id="sports#37">Alkass Five</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="AlkassFour.qa" site_id="sports#36">Alkass Four</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="AlkassOne.qa" site_id="sports#33">Alkass One</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="AlkassSeven.qa" site_id="sports#39">Alkass Seven</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="AlkassSix.qa" site_id="sports#38">Alkass Six</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="AlkassThree.qa" site_id="sports#35">Alkass Three</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="AlkassTwo.qa" site_id="sports#34">Alkass Two</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="BabyTV.uk" site_id="entertainment#35">Baby TV</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="Baraem.qa" site_id="entertainment#31">Baraem</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="BBCEarthMiddleEast.uk" site_id="entertainment#25">BBC Earth</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beIN4K.qa" site_id="sports#15">BeIn 4K</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINDrama1.qa" site_id="entertainment#10">beIN Drama 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINGourmet.qa" site_id="entertainment#11">beIN Gourmet</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINMovies1Premiere.qa" site_id="entertainment#1">beIN Movies Premiere</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINMovies2Action.qa" site_id="entertainment#2">beIN Movies Action</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINMovies3Drama.qa" site_id="entertainment#3">bein Movies Drama</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINMovies4Family.qa" site_id="entertainment#4">beIN Movies Family</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSeries1.qa" site_id="entertainment#8">beIN Series 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSeries2.qa" site_id="entertainment#9">beIN Series 2</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSports.qa" site_id="sports#2">BeIn Sports</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSports1.qa" site_id="sports#3">BeIn Sports 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSports2.qa" site_id="sports#4">BeIn Sports 2</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSports3.qa" site_id="sports#5">BeIn Sports 3</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSports4.qa" site_id="sports#6">BeIn Sports 4</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSports5.qa" site_id="sports#7">BeIn Sports 5</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSports6.qa" site_id="sports#8">BeIn Sports 6</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSports7.qa" site_id="sports#9">BeIn Sports 7</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsAFC.qa" site_id="sports#16">BeIN Sports AFC</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsAFC1.qa" site_id="sports#17">BeIN Sports AFC 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsAFC2.qa" site_id="sports#18">BeIN Sports AFC 2</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsAFC3.qa" site_id="sports#19">BeIN Sports AFC 3</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsEnglish1.qa" site_id="sports#20">BeIn Sports English 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsEnglish2.qa" site_id="sports#21">BeIn Sports English 2</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsEnglish3.qa" site_id="sports#22">BeIn Sports English 3</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsFrench1.qa" site_id="sports#24">BeIn Sports French 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsFrench2.qa" site_id="sports#25">BeIn Sports French 2</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsFrench3.qa" site_id="sports#26">BeIn Sports French 3</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsMax1.qa" site_id="sports#27">beIN Sports Max 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsMax2.qa" site_id="sports#28">beIN Sports Max 2</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsMax3.qa" site_id="sports#29">beIN Sports Max 3</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsMax4.qa" site_id="sports#30">beIN Sports Max 4</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsMax5.qa" site_id="sports#31">beIN Sports Max 5</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsMax6.qa" site_id="sports#32">beIN Sports Max 6</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsNBA.qa" site_id="sports#23">BeIn NBA</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsNews.qa" site_id="sports#1">BeIn Sports News</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsPremium1.qa" site_id="sports#10">BeIn Sports Premium 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsPremium2.qa" site_id="sports#11">BeIn Sports Premium 2</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsPremium3.qa" site_id="sports#12">BeIn Sports Premium 3</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsXtra1.qa" site_id="sports#13">BeIn Sports Xtra 1</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="beINSportsXtra2.qa" site_id="sports#14">BeIn Sports Xtra 2</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="BeJunior.qa" site_id="entertainment#29">be Junior</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="BloombergTVMiddleEast.ae" site_id="entertainment#22">Bloomberg TV</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="CartoonNetworkArabic.ae" site_id="entertainment#33">Cartoon Network Arabic</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="CartoonNetworkHindi.in" site_id="entertainment#34">Cartoon Network Hindi</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="CartoonNetworkMENA.uk" site_id="entertainment#32">Cartoon Network</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="CBeebiesMiddleEast.uk" site_id="entertainment#36">CBeebies</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="ClubMTVEurope.uk" site_id="entertainment#21">Club MTV</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="CNNArabic.ae" site_id="entertainment#26">CNN</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="DiscoveryChannelMiddleEastAfrica.us" site_id="entertainment#28">Discovery</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="DreamWorksChannelMiddleEast.us" site_id="entertainment#37">DreamWorks</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="EuronewsEnglish.fr" site_id="entertainment#27">EuroNews</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="Fatafeat.ae" site_id="entertainment#17">Fatafeat</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="FoodNetworkEMEA.us" site_id="entertainment#14">Food Network</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="FoxActionMoviesMiddleEast.hk" site_id="entertainment#6">FOX Action Movies</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="FoxArabia.ae" site_id="entertainment#13">FOX</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="FoxLifeMiddleEast.ae" site_id="entertainment#18">FOX Life</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="FoxMoviesMiddleEast.us" site_id="entertainment#5">FOX Movies</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="HGTVArabia.us" site_id="entertainment#15">HGTV</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="JeemTV.qa" site_id="entertainment#30">Jeem</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="MTV80s.uk" site_id="entertainment#19">MTV 80s</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="MTV90s.uk" site_id="entertainment#20">MTV 90s</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="NationalGeographicMiddleEast.uk" site_id="entertainment#23">National Geographic</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="NationalGeographicWildMiddleEast.uk" site_id="entertainment#24">National Geographic Wild</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="StarMoviesMiddleEast.ae" site_id="entertainment#7">Star Movies</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="StarWorldMiddleEast.ae" site_id="entertainment#16">Star World</channel>
|
||||
<channel site="bein.com" lang="en" xmltv_id="TravelChannelEMEA.uk" site_id="entertainment#12">Travel Channel</channel>
|
||||
</channels>
|
|
@ -26,7 +26,7 @@ module.exports = {
|
|||
'YYYY-MM-DD'
|
||||
)}`
|
||||
},
|
||||
parser: function ({ content, channel, date, cached }) {
|
||||
parser: function ({ content, channel, date }) {
|
||||
let programs = []
|
||||
const items = parseItems(content, channel)
|
||||
let i = 0
|
||||
|
@ -68,15 +68,15 @@ module.exports = {
|
|||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
const $ = cheerio.load(content)
|
||||
const items = $(`.container > div, #epg_div > div`).toArray()
|
||||
const items = $('.container > div, #epg_div > div').toArray()
|
||||
return items
|
||||
.map(item => {
|
||||
const $item = cheerio.load(item)
|
||||
const id = $item('*').attr('id')
|
||||
if (!/^channels\_[0-9]+$/.test(id)) return null
|
||||
if (!/^channels_[0-9]+$/.test(id)) return null
|
||||
const channelId = id.replace('channels_', '')
|
||||
const imgSrc = $item('img').attr('src')
|
||||
const [_, __, name] = imgSrc.match(/(\/|)([a-z0-9-_.]+)(.png|.svg)$/i) || [null, null, '']
|
||||
const [, , name] = imgSrc.match(/(\/|)([a-z0-9-_.]+)(.png|.svg)$/i) || [null, null, '']
|
||||
|
||||
return {
|
||||
lang,
|
||||
|
@ -103,7 +103,7 @@ function parseCategory($item) {
|
|||
function parseStart($item, date) {
|
||||
let time = $item('.time').text()
|
||||
if (!time) return null
|
||||
let [_, start, period] = time.match(/^(\d{2}:\d{2})( AM| PM|)/) || [null, null, null]
|
||||
let [, start, period] = time.match(/^(\d{2}:\d{2})( AM| PM|)/) || [null, null, null]
|
||||
if (!start) return null
|
||||
start = `${date.format('YYYY-MM-DD')} ${start}${period}`
|
||||
const format = period ? 'YYYY-MM-DD hh:mm A' : 'YYYY-MM-DD HH:mm'
|
||||
|
@ -114,7 +114,7 @@ function parseStart($item, date) {
|
|||
function parseStop($item, date) {
|
||||
let time = $item('.time').text()
|
||||
if (!time) return null
|
||||
let [_, stop, period] = time.match(/(\d{2}:\d{2})( AM| PM|)$/) || [null, null, null]
|
||||
let [, stop, period] = time.match(/(\d{2}:\d{2})( AM| PM|)$/) || [null, null, null]
|
||||
if (!stop) return null
|
||||
stop = `${date.format('YYYY-MM-DD')} ${stop}${period}`
|
||||
const format = period ? 'YYYY-MM-DD hh:mm A' : 'YYYY-MM-DD HH:mm'
|
||||
|
@ -123,7 +123,7 @@ function parseStop($item, date) {
|
|||
}
|
||||
|
||||
function parseItems(content, channel) {
|
||||
const [_, channelId] = channel.site_id.split('#')
|
||||
const [, channelId] = channel.site_id.split('#')
|
||||
const $ = cheerio.load(content)
|
||||
|
||||
return $(`#channels_${channelId} .slider > ul:first-child > li`).toArray()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// npm run channels:parse -- --config=./sites/beinsports.com/beinsports.com.config.js --output=./sites/beinsports.com/beinsports.com_qa-ar.channels.xml --set=lang:ar --set=region:ar
|
||||
// npx epg-grabber --config=sites/beinsports.com/beinsports.com.config.js --channels=sites/beinsports.com/beinsports.com_qa-en.channels.xml --output=guide.xml --timeout=30000 --days=2
|
||||
// npx epg-grabber --config=sites/beinsports.com/beinsports.com.config.js --channels=sites/beinsports.com/beinsports.com_us-en.channels.xml --output=guide.xml --timeout=30000 --days=2
|
||||
// npm run grab -- --site=beinsports.com
|
||||
// npm run grab -- --site=beinsports.com
|
||||
|
||||
const { parser, url } = require('./beinsports.com.config.js')
|
||||
const fs = require('fs')
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="beINSports1Australia.au" site_id="au#1">BeIn Sports 1 Australia</channel>
|
||||
<channel lang="en" xmltv_id="beINSports2Australia.au" site_id="au#2">BeIn Sports 2 Australia</channel>
|
||||
<channel lang="en" xmltv_id="beINSports3Australia.au" site_id="au#3">BeIn Sports 3 Australia</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports1Australia.au" site_id="au#1">BeIn Sports 1 Australia</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports2Australia.au" site_id="au#2">BeIn Sports 2 Australia</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports3Australia.au" site_id="au#3">BeIn Sports 3 Australia</channel>
|
||||
</channels>
|
|
@ -1,15 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="fr" xmltv_id="beINSports1France.fr" site_id="france#1">BeIN Sports 1 HD France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSports2France.fr" site_id="france#2">BeIN Sports 2 HD France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSports3France.fr" site_id="france#3">BeIN Sports 3 France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsMax4France.fr" site_id="france#4">BeIN Sports Max 4 France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsMax5France.fr" site_id="france#5">BeIN Sports Max 5 France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsMax6France.fr" site_id="france#6">BeIN Sports Max 6 France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsMax7France.fr" site_id="france#7">BeIN Sports Max 7 France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsMax8France.fr" site_id="france#8">BeIN Sports Max 8 France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsMax9France.fr" site_id="france#9">BeIN Sports Max 9 France</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsMax10France.fr" site_id="france#10">BeIN Sports Max 10 France</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSports1France.fr" site_id="france#1">BeIN Sports 1 HD France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSports2France.fr" site_id="france#2">BeIN Sports 2 HD France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSports3France.fr" site_id="france#3">BeIN Sports 3 France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsMax4France.fr" site_id="france#4">BeIN Sports Max 4 France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsMax5France.fr" site_id="france#5">BeIN Sports Max 5 France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsMax6France.fr" site_id="france#6">BeIN Sports Max 6 France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsMax7France.fr" site_id="france#7">BeIN Sports Max 7 France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsMax8France.fr" site_id="france#8">BeIN Sports Max 8 France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsMax9France.fr" site_id="france#9">BeIN Sports Max 9 France</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsMax10France.fr" site_id="france#10">BeIN Sports Max 10 France</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="beINSports1HongKong.hk" site_id="hk#1">BeIN Sports 1 Hong Kong</channel>
|
||||
<channel lang="en" xmltv_id="beINSports2HongKong.hk" site_id="hk#2">BeIN Sports 2 Hong Kong</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports1HongKong.hk" site_id="hk#1">BeIN Sports 1 Hong Kong</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports2HongKong.hk" site_id="hk#2">BeIN Sports 2 Hong Kong</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="beINSports1Indonesia.id" site_id="id#1">BeIN Sports 1 Indonesia</channel>
|
||||
<channel lang="en" xmltv_id="beINSports3Indonesia.id" site_id="id#2">BeIN Sports 3 Indonesia</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports1Indonesia.id" site_id="id#1">BeIN Sports 1 Indonesia</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports3Indonesia.id" site_id="id#2">BeIN Sports 3 Indonesia</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="beINSports1MalaysiaSingapore.my" site_id="my#1">BeIN Sports 1 Malaysia & Singapore</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports1MalaysiaSingapore.my" site_id="my#1">BeIN Sports 1 Malaysia & Singapore</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="beINSports1Philippines.ph" site_id="id#1">BeIN Sports 1 Philippines</channel>
|
||||
<channel lang="en" xmltv_id="beINSports3Philippines.ph" site_id="id#2">BeIN Sports 3 Philippines</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports1Philippines.ph" site_id="id#1">BeIN Sports 1 Philippines</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports3Philippines.ph" site_id="id#2">BeIN Sports 3 Philippines</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="ar" xmltv_id="beINSports.qa" site_id="ar#1">beIN SPORTS FTA</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsNews.qa" site_id="ar#2">beIN SPORTS News</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports2.qa" site_id="ar#3">beIN SPORTS2</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports3.qa" site_id="ar#4">beIN SPORTS3</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports4.qa" site_id="ar#5">beIN SPORTS4</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports5.qa" site_id="ar#6">beIN SPORTS5</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports6.qa" site_id="ar#7">beIN SPORTS6</channel>
|
||||
<channel lang="ar" xmltv_id="beINSports7.qa" site_id="ar#8">beIN SPORTS7</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsPremium3.qa" site_id="ar#9">beIN SPORTS3 PREMIUM</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsXtra1.qa" site_id="ar#10">beIN SPORTS XTRA1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsXtra2.qa" site_id="ar#11">beIN SPORTS XTRA2</channel>
|
||||
<channel lang="ar" xmltv_id="beIN4K.qa" site_id="ar#12">beIN 4k</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsAFC.qa" site_id="ar#13">beIN SPORTS AFC</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsAFC1.qa" site_id="ar#14">beIN SPORTS AFC1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsAFC2.qa" site_id="ar#15">beIN SPORTS AFC2</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsAFC3.qa" site_id="ar#16">beIN SPORTS AFC3</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsNBA.qa" site_id="ar#17">beIN SPORTS NBA</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsEnglish1.qa" site_id="ar#18">beIN SPORTS1 ENGLISH</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsEnglish2.qa" site_id="ar#19">beIN SPORTS2 ENGLISH</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsEnglish3.qa" site_id="ar#20">beIN SPORTS3 ENGLISH</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsFrench1.qa" site_id="ar#21">beIN SPORTS1 FRENCH</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsFrench2.qa" site_id="ar#22">beIN SPORTS2 FRENCH</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsFrench3.qa" site_id="ar#23">beIN SPORTS3 FRENCH</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsMax1.qa" site_id="ar#24">beIN SPORTS MAX 1</channel>
|
||||
<channel lang="ar" xmltv_id="beINSportsMax2.qa" site_id="ar#25">beIN SPORTS MAX 2</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSports.qa" site_id="ar#1">beIN SPORTS FTA</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsNews.qa" site_id="ar#2">beIN SPORTS News</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSports2.qa" site_id="ar#3">beIN SPORTS2</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSports3.qa" site_id="ar#4">beIN SPORTS3</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSports4.qa" site_id="ar#5">beIN SPORTS4</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSports5.qa" site_id="ar#6">beIN SPORTS5</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSports6.qa" site_id="ar#7">beIN SPORTS6</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSports7.qa" site_id="ar#8">beIN SPORTS7</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsPremium3.qa" site_id="ar#9">beIN SPORTS3 PREMIUM</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsXtra1.qa" site_id="ar#10">beIN SPORTS XTRA1</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsXtra2.qa" site_id="ar#11">beIN SPORTS XTRA2</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beIN4K.qa" site_id="ar#12">beIN 4k</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsAFC.qa" site_id="ar#13">beIN SPORTS AFC</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsAFC1.qa" site_id="ar#14">beIN SPORTS AFC1</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsAFC2.qa" site_id="ar#15">beIN SPORTS AFC2</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsAFC3.qa" site_id="ar#16">beIN SPORTS AFC3</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsNBA.qa" site_id="ar#17">beIN SPORTS NBA</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsEnglish1.qa" site_id="ar#18">beIN SPORTS1 ENGLISH</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsEnglish2.qa" site_id="ar#19">beIN SPORTS2 ENGLISH</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsEnglish3.qa" site_id="ar#20">beIN SPORTS3 ENGLISH</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsFrench1.qa" site_id="ar#21">beIN SPORTS1 FRENCH</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsFrench2.qa" site_id="ar#22">beIN SPORTS2 FRENCH</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsFrench3.qa" site_id="ar#23">beIN SPORTS3 FRENCH</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsMax1.qa" site_id="ar#24">beIN SPORTS MAX 1</channel>
|
||||
<channel site="beinsports.com" lang="ar" xmltv_id="beINSportsMax2.qa" site_id="ar#25">beIN SPORTS MAX 2</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="beINSports.qa" site_id="#1">BeIn Sports</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsNews.qa" site_id="#2">BeIn Sports News</channel>
|
||||
<channel lang="en" xmltv_id="beINSports2.qa" site_id="#3">BeIn Sports 2</channel>
|
||||
<channel lang="en" xmltv_id="beINSports3.qa" site_id="#4">BeIn Sports 3</channel>
|
||||
<channel lang="en" xmltv_id="beINSports4.qa" site_id="#5">BeIn Sports 4</channel>
|
||||
<channel lang="en" xmltv_id="beINSports5.qa" site_id="#6">BeIn Sports 5</channel>
|
||||
<channel lang="en" xmltv_id="beINSports6.qa" site_id="#7">BeIn Sports 6</channel>
|
||||
<channel lang="en" xmltv_id="beINSports7.qa" site_id="#8">BeIn Sports 7</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsPremium3.qa" site_id="#9">BeIn Sports Premium 3</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsXtra1.qa" site_id="#10">BeIn Sports Xtra 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsXtra2.qa" site_id="#11">BeIn Sports Xtra 2</channel>
|
||||
<channel lang="en" xmltv_id="beIN4K.qa" site_id="#12">BeIn 4K</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsNBA.qa" site_id="#13">BeIn NBA</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsEnglish1.qa" site_id="#14">BeIn Sports English 1</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsEnglish2.qa" site_id="#15">BeIn Sports English 2</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsEnglish3.qa" site_id="#16">BeIn Sports English 3</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsFrench1.qa" site_id="#17">BeIn Sports French 1</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsFrench2.qa" site_id="#18">BeIn Sports French 2</channel>
|
||||
<channel lang="fr" xmltv_id="beINSportsFrench3.qa" site_id="#19">BeIn Sports French 3</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports.qa" site_id="#1">BeIn Sports</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsNews.qa" site_id="#2">BeIn Sports News</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports2.qa" site_id="#3">BeIn Sports 2</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports3.qa" site_id="#4">BeIn Sports 3</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports4.qa" site_id="#5">BeIn Sports 4</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports5.qa" site_id="#6">BeIn Sports 5</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports6.qa" site_id="#7">BeIn Sports 6</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports7.qa" site_id="#8">BeIn Sports 7</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsPremium3.qa" site_id="#9">BeIn Sports Premium 3</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsXtra1.qa" site_id="#10">BeIn Sports Xtra 1</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsXtra2.qa" site_id="#11">BeIn Sports Xtra 2</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beIN4K.qa" site_id="#12">BeIn 4K</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsNBA.qa" site_id="#13">BeIn NBA</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsEnglish1.qa" site_id="#14">BeIn Sports English 1</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsEnglish2.qa" site_id="#15">BeIn Sports English 2</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsEnglish3.qa" site_id="#16">BeIn Sports English 3</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsFrench1.qa" site_id="#17">BeIn Sports French 1</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsFrench2.qa" site_id="#18">BeIn Sports French 2</channel>
|
||||
<channel site="beinsports.com" lang="fr" xmltv_id="beINSportsFrench3.qa" site_id="#19">BeIn Sports French 3</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="beINSports1Thailand.th" site_id="th#1">BeIN Sports 1 Thailand</channel>
|
||||
<channel lang="en" xmltv_id="beINSports3Thailand.th" site_id="th#2">BeIN Sports 3 Thailand</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports1Thailand.th" site_id="th#1">BeIN Sports 1 Thailand</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports3Thailand.th" site_id="th#2">BeIN Sports 3 Thailand</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="en" xmltv_id="beINSportsUSA.us" site_id="us#1">BeIN Sports USA</channel>
|
||||
<channel lang="en" xmltv_id="beINSportsenEspanol.us" site_id="us#2">BeIN Sports en Español</channel>
|
||||
<channel lang="en" xmltv_id="beINSPORTSXTRA.us" site_id="us#3">BeIN Sports Xtra USA</channel>
|
||||
<channel lang="en" xmltv_id="beINSPORTSXTRAenEspanol.us" site_id="us#4">BeIN Sports Xtra en Español</channel>
|
||||
<channel lang="en" xmltv_id="beINSports3USA.us" site_id="us#5">BeIN Sports 3 USA</channel>
|
||||
<channel lang="en" xmltv_id="beINSports4USA.us" site_id="us#6">BeIN Sports 4 USA</channel>
|
||||
<channel lang="en" xmltv_id="beINSports5USA.us" site_id="us#7">BeIN Sports 5 USA</channel>
|
||||
<channel lang="en" xmltv_id="beINSports6USA.us" site_id="us#8">BeIN Sports 6 USA</channel>
|
||||
<channel lang="en" xmltv_id="beINSports7USA.us" site_id="us#9">BeIN Sports 7 USA</channel>
|
||||
<channel lang="en" xmltv_id="beINSports8USA.us" site_id="us#10">BeIN Sports 8 USA</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsUSA.us" site_id="us#1">BeIN Sports USA</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSportsenEspanol.us" site_id="us#2">BeIN Sports en Español</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSPORTSXTRA.us" site_id="us#3">BeIN Sports Xtra USA</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSPORTSXTRAenEspanol.us" site_id="us#4">BeIN Sports Xtra en Español</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports3USA.us" site_id="us#5">BeIN Sports 3 USA</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports4USA.us" site_id="us#6">BeIN Sports 4 USA</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports5USA.us" site_id="us#7">BeIN Sports 5 USA</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports6USA.us" site_id="us#8">BeIN Sports 6 USA</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports7USA.us" site_id="us#9">BeIN Sports 7 USA</channel>
|
||||
<channel site="beinsports.com" lang="en" xmltv_id="beINSports8USA.us" site_id="us#10">BeIN Sports 8 USA</channel>
|
||||
</channels>
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="beinsports.com">
|
||||
<channels>
|
||||
<channel lang="es" xmltv_id="beINSportsUSA.us" site_id="us_es#1">BeIN Sports USA</channel>
|
||||
<channel lang="es" xmltv_id="beINSportsenEspanol.us" site_id="us_es#2">BeIN Sports en Español</channel>
|
||||
<channel lang="es" xmltv_id="beINSPORTSXTRA.us" site_id="us_es#3">BeIN Sports Xtra USA</channel>
|
||||
<channel lang="es" xmltv_id="beINSPORTSXTRAenEspanol.us" site_id="us_es#4">BeIN Sports Xtra en Español</channel>
|
||||
<channel lang="es" xmltv_id="beINSports3USA.us" site_id="us_es#5">BeIN Sports 3 USA</channel>
|
||||
<channel lang="es" xmltv_id="beINSports4USA.us" site_id="us_es#6">BeIN Sports 4 USA</channel>
|
||||
<channel lang="es" xmltv_id="beINSports5USA.us" site_id="us_es#7">BeIN Sports 5 USA</channel>
|
||||
<channel lang="es" xmltv_id="beINSports6USA.us" site_id="us_es#8">BeIN Sports 6 USA</channel>
|
||||
<channel lang="es" xmltv_id="beINSports7USA.us" site_id="us_es#9">BeIN Sports 7 USA</channel>
|
||||
<channel lang="es" xmltv_id="beINSports8USA.us" site_id="us_es#10">BeIN Sports 8 USA</channel>
|
||||
</channels>
|
||||
</site>
|
||||
<channels>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSportsUSA.us" site_id="us_es#1">BeIN Sports USA</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSportsenEspanol.us" site_id="us_es#2">BeIN Sports en Español</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSPORTSXTRA.us" site_id="us_es#3">BeIN Sports Xtra USA</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSPORTSXTRAenEspanol.us" site_id="us_es#4">BeIN Sports Xtra en Español</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSports3USA.us" site_id="us_es#5">BeIN Sports 3 USA</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSports4USA.us" site_id="us_es#6">BeIN Sports 4 USA</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSports5USA.us" site_id="us_es#7">BeIN Sports 5 USA</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSports6USA.us" site_id="us_es#8">BeIN Sports 6 USA</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSports7USA.us" site_id="us_es#9">BeIN Sports 7 USA</channel>
|
||||
<channel site="beinsports.com" lang="es" xmltv_id="beINSports8USA.us" site_id="us_es#10">BeIN Sports 8 USA</channel>
|
||||
</channels>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue