Merge branch 'master' into pr/17426

This commit is contained in:
freearhey 2025-04-14 20:12:20 +03:00
commit c80ff4ae3c
89 changed files with 55424 additions and 15011 deletions

View file

@ -1,39 +0,0 @@
{
"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"
]
}
}

View file

@ -62,24 +62,6 @@ body:
description: Name of the city from which the channel is transmitted
placeholder: 'Hefei'
- type: input
id: broadcast_area
attributes:
label: Broadcast Area
description: List of codes describing the broadcasting area of the channel separated by `;`. Any combination of `r/<region_code>`, `c/<country_code>`, `s/<subdivision_code>`
placeholder: 'c/CN;r/EUR'
validations:
required: true
- type: input
id: languages
attributes:
label: Languages
description: List of languages in which the channel is broadcast separated by `;`. A list of all supported languages and their codes can be found in [data/languages.csv](https://github.com/iptv-org/database/blob/master/data/languages.csv)
placeholder: 'zho;eng'
validations:
required: true
- type: input
id: categories
attributes:
@ -135,7 +117,71 @@ body:
validations:
required: true
- type: markdown
attributes:
value: |
## Main Feed
Description of the main feed of the channel
- type: input
id: feed_name
attributes:
label: Feed Name
description: "Unique for this channel feed name in English. For example: `HD`, `East`, `French`, `+1`, etc. May include: `a-z`, `0-9`, `space`, `-`, `!`, `:`, `&`, `.`, `+`, `'`, `/`, `»`, `#`, `%`, `°`, `$`, `@`, `?`, `|`, `¡`"
placeholder: 'SD'
value: 'SD'
validations:
required: true
- type: input
id: broadcast_area
attributes:
label: Broadcast Area
description: "List of codes describing the broadcasting area of the feed separated by `;`. Any combination of `r/<region_code>`, `c/<country_code>`, `s/<subdivision_code>`. A full list of supported codes can be found here: [countries](https://github.com/iptv-org/database/blob/master/data/countries.csv), [subdivisions](https://github.com/iptv-org/database/blob/master/data/subdivisions.csv), [regions](https://github.com/iptv-org/database/blob/master/data/regions.csv)"
placeholder: 'c/CN'
validations:
required: true
- type: input
id: timezones
attributes:
label: Timezones
description: List of broadcast time zones separated by `;`. A list of all supported timezones and their codes can be found in [data/timezones.csv](https://github.com/iptv-org/database/blob/master/data/timezones.csv)
placeholder: 'Asia/Shanghai'
validations:
required: true
- type: input
id: languages
attributes:
label: Languages
description: List of languages in which the feed is broadcast separated by `;`. A list of all supported languages and their codes can be found in [data/languages.csv](https://github.com/iptv-org/database/blob/master/data/languages.csv)
placeholder: 'zho;eng'
validations:
required: true
- type: dropdown
id: video_format
attributes:
label: Format
description: Video format of the broadcast
default: 6
options:
- '4320p'
- '2160p'
- '1080p'
- '1080i'
- '720p'
- '576p'
- '576i'
- '480p'
- '480i'
- '360p'
- '240p'
validations:
required: true
- type: textarea
attributes:
label: Notes
description: 'Anything else we should know about this channel?'
description: 'Anything else we should know?'

View file

@ -4,6 +4,11 @@ title: 'Edit: '
labels: ['channels:edit']
body:
- type: markdown
attributes:
value: |
Please specify exactly what should be changed. To delete an existing value without replacement use the `~` symbol.
- type: input
id: id
attributes:
@ -13,11 +18,6 @@ body:
validations:
required: true
- type: markdown
attributes:
value: |
Please specify exactly what should be changed. To delete an existing value without replacement use the `~` symbol.
- type: input
id: name
attributes:
@ -67,20 +67,6 @@ body:
description: Name of the city from which the channel is transmitted
placeholder: 'Hefei'
- type: input
id: broadcast_area
attributes:
label: Broadcast Area
description: List of codes describing the broadcasting area of the channel separated by `;`. Any combination of `r/<region_code>`, `c/<country_code>`, `s/<subdivision_code>`
placeholder: 'c/CN;r/EUR'
- type: input
id: languages
attributes:
label: Languages
description: List of languages in which the channel is broadcast separated by `;`. A list of all supported languages and their codes can be found in [data/languages.csv](https://github.com/iptv-org/database/blob/master/data/languages.csv)
placeholder: 'zho;eng'
- type: input
id: categories
attributes:
@ -100,21 +86,21 @@ body:
- type: input
id: launched
attributes:
label: Launched
label: Launched (optional)
description: Launch date of the channel (`YYYY-MM-DD`)
placeholder: '2016-07-28'
- type: input
id: closed
attributes:
label: Closed
label: Closed (optional)
description: Date on which the channel closed (`YYYY-MM-DD`)
placeholder: '2020-05-31'
- type: input
id: replaced_by
attributes:
label: Replaced By
label: Replaced By (optional)
description: The ID of the channel that this channel was replaced by
placeholder: 'CCTV1.cn'

View file

@ -4,17 +4,24 @@ title: 'Remove: '
labels: ['channels:remove']
body:
- type: input
- type: markdown
attributes:
label: Channel ID (required)
value: |
Deleting a channel will also delete all associated feeds and records in the blocklist.
- type: input
id: id
attributes:
label: Channel ID
description: The ID of the channel that should be removed
placeholder: 'AnhuiTV.cn'
validations:
required: true
- type: dropdown
id: reason
attributes:
label: Reason (required)
label: Reason
description: Select the reason for removal from the list below
options:
- 'Duplicate'
@ -24,6 +31,7 @@ body:
required: true
- type: textarea
id: notes
attributes:
label: Notes
description: 'Any additional information'
label: Notes (optional)
description: 'Anything else we should know?'

92
.github/ISSUE_TEMPLATE/4_feeds_add.yml vendored Normal file
View file

@ -0,0 +1,92 @@
name: Add feed
description: Request to add a channel feed into the database
title: 'Add: '
labels: ['feeds:add']
body:
- type: markdown
attributes:
value: |
Please fill out the issue form as much as you can so we could efficiently process your request.
- type: input
id: channel_id
attributes:
label: Channel ID (required)
description: ID of the channel to which this feed belongs
placeholder: 'HBO.us'
validations:
required: true
- type: input
id: feed_name
attributes:
label: Feed Name
description: "Unique for this channel feed name in English. For example: `HD`, `East`, `French`, `+1`, etc. May include: `a-z`, `0-9`, `space`, `-`, `!`, `:`, `&`, `.`, `+`, `'`, `/`, `»`, `#`, `%`, `°`, `$`, `@`, `?`, `|`, `¡`"
placeholder: 'SD'
validations:
required: true
- type: dropdown
id: is_main
attributes:
label: Main Feed
description: Indicates if this feed is the main for the channel
options:
- 'FALSE'
- 'TRUE'
validations:
required: true
- type: input
id: broadcast_area
attributes:
label: Broadcast Area
description: "List of codes describing the broadcasting area of the feed separated by `;`. Any combination of `r/<region_code>`, `c/<country_code>`, `s/<subdivision_code>`. A full list of supported codes can be found here: [data/countries.csv](https://github.com/iptv-org/database/blob/master/data/countries.csv), [data/subdivisions.csv](https://github.com/iptv-org/database/blob/master/data/subdivisions.csv), [data/regions.csv](https://github.com/iptv-org/database/blob/master/data/regions.csv)"
placeholder: 'c/CN'
validations:
required: true
- type: input
id: timezones
attributes:
label: Timezones
description: List of broadcast time zones separated by `;`. A list of all supported timezones and their codes can be found in [data/timezones.csv](https://github.com/iptv-org/database/blob/master/data/timezones.csv)
placeholder: 'Asia/Shanghai'
validations:
required: true
- type: input
id: languages
attributes:
label: Languages
description: List of languages in which the feed is broadcast separated by `;`. A list of all supported languages and their codes can be found in [data/languages.csv](https://github.com/iptv-org/database/blob/master/data/languages.csv)
placeholder: 'zho;eng'
validations:
required: true
- type: dropdown
id: video_format
attributes:
label: Format
description: Video format of the broadcast
default: 6
options:
- '4320p'
- '2160p'
- '1080p'
- '1080i'
- '720p'
- '576p'
- '576i'
- '480p'
- '480i'
- '360p'
- '240p'
validations:
required: true
- type: textarea
attributes:
label: Notes
description: 'Anything else we should know?'

88
.github/ISSUE_TEMPLATE/5_feeds_edit.yml vendored Normal file
View file

@ -0,0 +1,88 @@
name: ✏️ Edit feed
description: Request to edit feed description
title: 'Edit: '
labels: ['feeds:edit']
body:
- type: markdown
attributes:
value: |
Please specify exactly what should be changed. To delete an existing value without replacement use the `~` symbol.
- type: input
id: channel_id
attributes:
label: Channel ID (required)
description: ID of the channel to which this feed belongs
placeholder: 'HBO.us'
validations:
required: true
- type: input
id: feed_id
attributes:
label: Feed ID (required)
description: The ID of the feed that should be updated
placeholder: 'West'
validations:
required: true
- type: input
id: feed_name
attributes:
label: Feed Name
description: "Unique for this channel feed name in English. For example: `HD`, `East`, `French`, `+1`, etc. May include: `a-z`, `0-9`, `space`, `-`, `!`, `:`, `&`, `.`, `+`, `'`, `/`, `»`, `#`, `%`, `°`, `$`, `@`, `?`, `|`, `¡`"
placeholder: 'West HD'
- type: dropdown
id: is_main
attributes:
label: Main Feed
description: Indicates if this feed is the main for the channel
options:
- 'FALSE'
- 'TRUE'
- type: input
id: broadcast_area
attributes:
label: Broadcast Area
description: "List of codes describing the broadcasting area of the feed separated by `;`. Any combination of `r/<region_code>`, `c/<country_code>`, `s/<subdivision_code>`. A full list of supported codes can be found here: [countries](https://github.com/iptv-org/database/blob/master/data/countries.csv), [subdivisions](https://github.com/iptv-org/database/blob/master/data/subdivisions.csv), [regions](https://github.com/iptv-org/database/blob/master/data/regions.csv)"
placeholder: 'c/CN'
- type: input
id: timezones
attributes:
label: Timezones
description: List of broadcast time zones separated by `;`. A list of all supported timezones and their codes can be found in [data/timezones.csv](https://github.com/iptv-org/database/blob/master/data/timezones.csv)
placeholder: 'Asia/Shanghai'
- type: input
id: languages
attributes:
label: Languages
description: List of languages in which the feed is broadcast separated by `;`. A list of all supported languages and their codes can be found in [data/languages.csv](https://github.com/iptv-org/database/blob/master/data/languages.csv)
placeholder: 'zho;eng'
- type: dropdown
id: video_format
attributes:
label: Format
description: Video format of the broadcast
options:
- '4320p'
- '2160p'
- '1080p'
- '1080i'
- '720p'
- '576p'
- '576i'
- '480p'
- '480i'
- '360p'
- '240p'
- type: textarea
attributes:
label: Notes
description: 'Anything else we should know?'

View file

@ -0,0 +1,38 @@
name: 🗑️ Remove feed
description: Request to remove a feed from the database
title: 'Remove: '
labels: ['feeds:remove']
body:
- type: input
id: channel_id
attributes:
label: Channel ID
description: ID of the channel to which this feed belongs
placeholder: 'HBO.us'
validations:
required: true
- type: input
id: feed_id
attributes:
label: Feed ID
description: The ID of the feed that should be updated
placeholder: 'West'
validations:
required: true
- type: dropdown
attributes:
label: Reason
description: Select the reason for removal from the list below
options:
- 'Duplicate'
- 'Other'
validations:
required: true
- type: textarea
attributes:
label: Notes (optional)
description: 'Anything else we should know?'

View file

@ -5,7 +5,7 @@ labels: ['blocklist:add']
body:
- type: input
id: id
id: channel
attributes:
label: Channel ID
description: The ID of the channel that should be blocked
@ -13,11 +13,22 @@ body:
validations:
required: true
- type: dropdown
id: reason
attributes:
label: Reason
description: Reason for blocking the channel
options:
- 'DMCA'
- 'NSFW'
validations:
required: true
- type: input
id: ref
attributes:
label: Reference
description: Link to the official request for channel removal
description: Link to DMCA notice or approved channel removal request
placeholder: 'https://github.com/iptv-org/iptv/issues/1831'
validations:
required: true
@ -25,4 +36,4 @@ body:
- type: textarea
attributes:
label: Notes (optional)
description: 'Any additional information'
description: 'Anything else we should know?'

View file

@ -5,24 +5,17 @@ labels: ['blocklist:remove']
body:
- type: input
id: channel
attributes:
label: Channel ID (required)
label: Channel ID
description: The ID of the channel that should be removed
placeholder: 'AnhuiTV.cn'
validations:
required: true
- type: dropdown
attributes:
label: Reason (required)
description: Select the reason for removal from the list below
options:
- 'Invalid channel id'
- 'Other'
validations:
required: true
- type: textarea
attributes:
label: Notes
description: 'Any additional information'
description: 'Describe in detail the reason for removing the channel from the blocklist'
validations:
required: true

View file

@ -13,13 +13,21 @@ jobs:
- uses: actions/setup-node@v3
if: ${{ !env.ACT }}
with:
node-version: '18.18.2'
node-version: '22.12.0'
cache: 'npm'
- uses: tj-actions/changed-files@v35
- name: changed files
id: files
with:
files: data/*.csv
run: |
FILES=data/*.csv
ANY_CHANGED=false
ALL_CHANGED_FILES=$(git diff --name-only "${FILES}" | tr '\n' ' ')
if [ -n "${ALL_CHANGED_FILES}" ]; then
ANY_CHANGED=true
fi
echo "all_changed_files=$ALL_CHANGED_FILES" >> "$GITHUB_OUTPUT"
echo "any_changed=$ANY_CHANGED" >> "$GITHUB_OUTPUT"
- name: install dependencies
if: steps.files.outputs.any_changed == 'true'
run: npm install
- name: validate
if: steps.files.outputs.any_changed == 'true'

View file

@ -12,7 +12,7 @@ jobs:
- uses: actions/setup-node@v3
if: ${{ !env.ACT }}
with:
node-version: '18.18.2'
node-version: '22.12.0'
cache: 'npm'
- name: install dependencies
run: npm install

View file

@ -21,7 +21,7 @@ jobs:
- uses: actions/setup-node@v3
if: ${{ !env.ACT }}
with:
node-version: '18.18.2'
node-version: '22.12.0'
cache: 'npm'
- name: install dependencies
run: npm install

1
.husky/pre-commit Normal file
View file

@ -0,0 +1 @@
npm run db:validate

View file

@ -1,34 +1,94 @@
# Contributing Guide
- [How to?](#how-to)
- [Data Scheme](#data-scheme)
- [Channel Logo Guidelines](#channel-logo-guidelines)
- [Project Structure](#project-structure)
- [Scripts](#scripts)
- [Workflows](#workflows)
## How to?
### How to add a new channel to the database?
The easiest way is to send a request through this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=channels%3Aadd&projects=&template=1_channels_add.yml&title=Add%3A+). Just fill in all the information you know about the channel and press send. Once your request is approved, the channel will automatically be added to the database.
If you want to add more than one channel, you can do it directly by editing the [data/channels.csv](data/channels.csv) file in any text editor. After that, just [commit](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/about-commits) all changes and send us a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests).
**IMPORTANT:** Before sending the request, make sure that the number of columns in the file has not changed and that all rows end with [CRLF](https://developer.mozilla.org/en-US/docs/Glossary/CRLF). Otherwise we will not be able to review this request.
### How to edit channel description?
As with adding a channel, this can be done in several ways.
The first option is to send a request through this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=channels%3Aedit&projects=&template=2_channels_edit.yml&title=Edit%3A+). Just specify the ID of the channel you want to edit and the new data. To delete a value, insert `~` in the desired field. After your request is approved, the channel description will be automatically updated.
The second option is to edit the [data/channels.csv](data/channels.csv) file using a text editor and then send us a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests).
**IMPORTANT:** Before sending the request, make sure that the number of columns in the file has not changed and that all rows end with [CRLF](https://developer.mozilla.org/en-US/docs/Glossary/CRLF). Otherwise we will not be able to review this request.
### How to remove a channel from the database?
To remove a channel fill out this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=channels%3Aremove&projects=&template=3_channels_remove.yml&title=Remove%3A+) with channel ID and the reason for deletion.
**NOTE:** Closing a channel is not a reason to remove it from the database.
### How to mark a channel as closed?
To do this, use this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=channels%3Aedit&projects=&template=2_channels_edit.yml&title=Edit%3A+). In it, in the "Closed" field you will need to specify at least the approximate date of closing. And there you can also specify the ID of the channel that replaced it, if necessary.
### How to add a new feed to the database?
_Option 1:_ Send a request through this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=feeds%3Aadd&projects=&template=4_feeds_add.yml&title=Add%3A+). Fill in all the information you know about the feed and press send. Once your request is approved, the feed will automatically be added to the database.
_Option 2:_ Edit the [data/feeds.csv](data/feeds.csv) file using a text editor and then send us a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests).
### How to edit feed description?
_Option 1:_ Send a request through this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=feeds%3Aedit&projects=&template=5_feeds_edit.yml&title=Edit%3A+). Specify the ID of the channel and feed you want to edit and the new data. To delete a value, insert `~` in the desired field. After your request is approved, the feed description will be automatically updated.
_Option 2:_ Edit the [data/feeds.csv](data/feeds.csv) file using a text editor and then send us a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests).
### How to remove a feed from the database?
Just fill out this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=feeds%3Aremove&projects=&template=6_feeds_remove.yml&title=Remove%3A+).
## Data Scheme
### channels
| Field | Description | Required | Example |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------ |
| id | Unique channel ID derived from the `name` and `country` separated by dot. May only contain Latin letters, numbers and dot. | Required | `AnhuiTV.cn` |
| name | Official channel name in English or call sign. May include: `a-z`, `0-9`, `space`, `-`, `!`, `:`, `&`, `.`, `+`, `'`, `/`, `»`, `#`, `%`, `°`, `$`, `@`, `?`, <code>\|</code>, `¡`, ``. | Required | `Anhui TV` |
| alt_names | List of alternative channel names separated by `;`. May contain any characters except `,` and `"`. | Optional | `安徽卫视;AHTV` |
| network | Network of which this channel is a part. May contain any characters except `,` and `"`. | Optional | `Anhui` |
| owners | List of channel owners separated by `;`. May contain any characters except `,` and `"`. | Optional | `China Central Television` |
| country | Country code from which the channel is transmitted. A list of all supported countries and their codes can be found in [data/countries.csv](data/countries.csv) | Required | `CN` |
| subdivision | Code of the subdivision (e.g., provinces or states) from which the broadcast is transmitted. A list of all supported subdivisions and their codes can be found in [data/subdivisions.csv](data/subdivisions.csv). | Optional | `CN-AH` |
| city | The name of the city in English from which the channel is broadcast. May contain any characters except `,` and `"`. | Optional | `Hefei` |
| broadcast_area | List of codes describing the broadcasting area of the channel separated by `;`. Any combination of `r/<region_code>`, `c/<country_code>`, `s/<subdivision_code>`. | Required | `c/CN;r/ASIA` |
| languages | List of languages in which the channel is broadcast separated by `;`. A list of all supported languages and their codes can be found in [data/languages.csv](data/languages.csv). | Required | `zho;eng` |
| categories | List of categories to which this channel belongs separated by `;`. A list of all supported categories can be found in [data/categories.csv](data/categories.csv). | Optional | `animation;kids` |
| is_nsfw | Indicates whether the channel broadcasts adult content (`TRUE` or `FALSE`). | Required | `FALSE` |
| launched | Launch date of the channel (`YYYY-MM-DD`). | Optional | `2016-07-28` |
| closed | Date on which the channel closed (`YYYY-MM-DD`). | Optional | `2020-05-31` |
| replaced_by | The ID of the channel that this channel was replaced by. | Optional | `CCTV1.cn` |
| website | Official website URL. | Optional | `http://www.ahtv.cn/` |
| logo | Logo URL. Only URL with [HTTPS](https://ru.wikipedia.org/wiki/HTTPS) protocol are allowed. Supported image types: `PNG`, `JPEG`. Max size: 512x512 pixels. The link should not be [geo-blocked](https://en.wikipedia.org/wiki/Geo-blocking). | Required | `https://example.com/logo.png` |
| Field | Description | Required | Example |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------ |
| id | Unique channel ID derived from the `name` and `country` separated by dot. May only contain Latin letters, numbers and dot. | Required | `AnhuiTV.cn` |
| name | Official channel name in English or call sign. May include: `a-z`, `0-9`, `space`, `-`, `!`, `:`, `&`, `.`, `+`, `'`, `/`, `»`, `#`, `%`, `°`, `$`, `@`, `?`, <code>\|</code>, `¡`. | Required | `Anhui TV` |
| alt_names | List of alternative channel names separated by `;`. May contain any characters except `,` and `"`. | Optional | `安徽卫视;AHTV` |
| network | Network of which this channel is a part. May contain any characters except `,` and `"`. | Optional | `Anhui` |
| owners | List of channel owners separated by `;`. May contain any characters except `,` and `"`. | Optional | `China Central Television` |
| country | Country code from which the channel is transmitted. A list of all supported countries and their codes can be found in [data/countries.csv](data/countries.csv) | Required | `CN` |
| subdivision | Code of the subdivision (e.g., provinces or states) from which the broadcast is transmitted. A list of all supported subdivisions and their codes can be found in [data/subdivisions.csv](data/subdivisions.csv). | Optional | `CN-AH` |
| city | The name of the city in English from which the channel is broadcast. May contain any characters except `,` and `"`. | Optional | `Hefei` |
| broadcast_area | [DEPRECATED] List of codes describing the broadcasting area of the channel separated by `;`. Any combination of `r/<region_code>`, `c/<country_code>`, `s/<subdivision_code>`. | Required | `c/CN;r/ASIA` |
| languages | [DEPRECATED] List of languages in which the channel is broadcast separated by `;`. A list of all supported languages and their codes can be found in [data/languages.csv](data/languages.csv). | Required | `zho;eng` |
| categories | List of categories to which this channel belongs separated by `;`. A list of all supported categories can be found in [data/categories.csv](data/categories.csv). | Optional | `animation;kids` |
| is_nsfw | Indicates whether the channel broadcasts adult content (`TRUE` or `FALSE`). | Required | `FALSE` |
| launched | Launch date of the channel (`YYYY-MM-DD`). | Optional | `2016-07-28` |
| closed | Date on which the channel closed (`YYYY-MM-DD`). | Optional | `2020-05-31` |
| replaced_by | The ID of the channel that this channel was replaced by. | Optional | `CCTV1.cn` |
| website | Official website URL. | Optional | `http://www.ahtv.cn/` |
| logo | Logo URL. Only URL with [HTTPS](https://ru.wikipedia.org/wiki/HTTPS) protocol are allowed. Supported image types: `PNG`, `JPEG`. Max size: 512x512 pixels. The link should not be [geo-blocked](https://en.wikipedia.org/wiki/Geo-blocking). May contain any characters except `,` and `"`. | Required | `https://example.com/logo.png` |
### feeds
| Field | Description | Required | Example |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------------------- |
| channel | ID of the channel to which this feed belongs. | Required | `HBO.us` |
| id | Unique feed ID derived from the `name`. May only contain Latin letters and numbers. | Required | `West` |
| name | Name of the feed in English. May include: `a-z`, `0-9`, `space`, `-`, `!`, `:`, `&`, `.`, `+`, `'`, `/`, `»`, `#`, `%`, `°`, `$`, `@`, `?`, <code>\|</code>, `¡`. | Required | `West` |
| is_main | Indicates if this feed is the main for the channel (`TRUE` or `FALSE`). | Required | `FALSE` |
| broadcast_area | List of codes describing the broadcasting area of the feed separated by `;`. Any combination of `r/<region_code>`, `c/<country_code>`, `s/<subdivision_code>` is allowed. A full list of supported codes can be found here: [data/countries.csv](https://github.com/iptv-org/database/blob/master/data/countries.csv), [data/subdivisions.csv](https://github.com/iptv-org/database/blob/master/data/subdivisions.csv), [data/regions.csv](https://github.com/iptv-org/database/blob/master/data/regions.csv). | Required | `s/US-CA;s/US-ID` |
| timezones | List of timezones in which the feed is broadcast separated by `;`. A list of all supported timezones and their codes can be found in [data/timezones.csv](data/timezones.csv). | Required | `America/Los_Angeles` |
| languages | List of languages in which the feed is broadcast separated by `;`. A list of all supported languages and their codes can be found in [data/languages.csv](data/languages.csv). | Required | `eng;spa` |
| video_format | Video format of the feed. | Required | `1080i` |
### categories
@ -55,11 +115,11 @@
### subdivisions
| Field | Description | Required | Example |
| ------- | ------------------------------------------------------------------------------ | -------- | ------------------ |
| country | Country code of the division | Required | `CA` |
| name | Official subdivision name | Required | `British Columbia` |
| code | [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) code of the subdivision | Required | `CA-BC` |
| Field | Description | Required | Example |
| ------- | ------------------------------------------------------------------------------------------ | -------- | ------------------ |
| country | [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code of the country | Required | `CA` |
| name | Official subdivision name | Required | `British Columbia` |
| code | [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) code of the subdivision | Required | `CA-BC` |
### regions
@ -69,6 +129,14 @@
| code | Abbreviated designation for the region. May only contain Latin letters in upper case. The minimum length is 3 letters. | Required | `CAS` |
| countries | List of country codes in the region | Required | `KG;KZ;TJ;TM;UZ` |
### timezones
| Field | Description | Required | Example |
| ---------- | ------------------------------------------------------------------------- | -------- | --------------------- |
| id | Timezone ID from [tz database](https://en.wikipedia.org/wiki/Tz_database) | Required | `Africa/Johannesburg` |
| utc_offset | [UTC offset](https://en.wikipedia.org/wiki/UTC_offset) for this time zone | Required | `+02:00` |
| countries | List of countries included in this time zone | Required | `ZA;LS;SZ` |
### blocklist
List of channels blocked at the request of copyright holders.
@ -76,6 +144,7 @@ List of channels blocked at the request of copyright holders.
| Field | Description | Required | Example |
| ------- | ----------------------------------------------- | -------- | --------------------------------- |
| channel | Channel ID | Required | `AnimalPlanetAfrica.us` |
| reason | Reason for blocking | Required | `dmca` |
| ref | Link to removal request or DMCA takedown notice | Required | `https://example.com/issues/0000` |
## Channel Logo Guidelines
@ -106,17 +175,27 @@ Since finding a suitable logo for the channel is not always possible, this list
## Project Structure
- `.github/`
- `ISSUE_TEMPLATE/`: issue templates for the repository.
- `workflows`: contains [GitHub actions](https://docs.github.com/en/actions/quickstart) workflows.
- `CODE_OF_CONDUCT.md`: rules you shouldn't break if you don't want to get banned.
- `.readme/`
- `preview.png`: image displayed in the `README.md`.
- `data/`: contains all data.
- `scripts/`: contains all scripts used in the repository.
- `tests/`: contains tests to check the scripts.
- `CONTRIBUTING.md`: file you are currently reading.
- `README.md`: project description displayed on the home page.
```
database/
├── .github/
| ├── ISSUE_TEMPLATE # issue templates for the repository
| ├── workflows # contains GitHub actions workflows
| ├── CODE_OF_CONDUCT.md # rules you shouldn't break if you don't want to get banned
├── .husky/
| ├── pre-commit # commands to run before each commit
├── .readme/
| ├── preview.png # image displayed in the README.md
├── data/ # contains all data
├── scripts/ # contains all scripts used in the repository
├── tests/ # contains tests to check the scripts
├── .prettierrc.js # configuration file for Prettier
├── eslint.config.mjs # configuration file for ESLint
├── package.json # project manifest file
├── tsconfig.json # configuration file for TypeScript
├── LICENSE # license text
├── CONTRIBUTING.md # file you are currently reading
├── README.md # project description displayed on the home page
```
## Scripts

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
name,code,languages,flag
Afghanistan,AF,prs;pus;tuk,🇦🇫
Aland,AX,swe,🇦🇽
Albania,AL,sqi,🇦🇱
Algeria,DZ,ara,🇩🇿
American Samoa,AS,eng;smo,🇦🇸
@ -148,7 +149,7 @@ Montenegro,ME,cnr;srp;bos;sqi;hrv,🇲🇪
Montserrat,MS,eng,🇲🇸
Morocco,MA,ara;zgh,🇲🇦
Mozambique,MZ,por,🇲🇿
Myanmar (Burma),MM,mya,🇲🇲
Myanmar,MM,mya,🇲🇲
Namibia,NA,afr;deu;eng;her;hgm;kwn;loz;ndo;tsn,🇳🇦
Nauru,NR,eng;nau,🇳🇷
Nepal,NP,nep,🇳🇵
@ -182,7 +183,7 @@ Republic of the Congo,CG,fra;kon;lin,🇨🇬
Romania,RO,ron,🇷🇴
Russia,RU,rus,🇷🇺
Rwanda,RW,eng;fra;kin,🇷🇼
Réunion,RE,fra,🇷🇪
Reunion,RE,fra,🇷🇪
Saint Barthélemy,BL,fra,🇧🇱
Saint Helena,SH,eng,🇸🇭
Saint Kitts and Nevis,KN,eng,🇰🇳
@ -216,7 +217,7 @@ Swaziland,SZ,eng;ssw,🇸🇿
Sweden,SE,swe,🇸🇪
Switzerland,CH,deu;fra;ita,🇨🇭
Syria,SY,ara,🇸🇾
São Tomé and Príncipe,ST,por,🇸🇹
Sao Tome and Principe,ST,por,🇸🇹
Taiwan,TW,zho,🇹🇼
Tajikistan,TJ,rus;tgk,🇹🇯
Tanzania,TZ,eng;swa,🇹🇿
@ -248,4 +249,3 @@ Western Sahara,EH,zgh;mey;spa,🇪🇭
Yemen,YE,ara,🇾🇪
Zambia,ZM,eng,🇿🇲
Zimbabwe,ZW,bwg;eng;kck;hio;ndc;nde;nya;sna;sot;toi;tsn;tso;ven;xho;zib,🇿🇼
Åland,AX,swe,🇦🇽
1 name code languages flag
2 Afghanistan AF prs;pus;tuk 🇦🇫
3 Aland AX swe 🇦🇽
4 Albania AL sqi 🇦🇱
5 Algeria DZ ara 🇩🇿
6 American Samoa AS eng;smo 🇦🇸
149 Montserrat MS eng 🇲🇸
150 Morocco MA ara;zgh 🇲🇦
151 Mozambique MZ por 🇲🇿
152 Myanmar (Burma) Myanmar MM mya 🇲🇲
153 Namibia NA afr;deu;eng;her;hgm;kwn;loz;ndo;tsn 🇳🇦
154 Nauru NR eng;nau 🇳🇷
155 Nepal NP nep 🇳🇵
183 Romania RO ron 🇷🇴
184 Russia RU rus 🇷🇺
185 Rwanda RW eng;fra;kin 🇷🇼
186 Réunion Reunion RE fra 🇷🇪
187 Saint Barthélemy BL fra 🇧🇱
188 Saint Helena SH eng 🇸🇭
189 Saint Kitts and Nevis KN eng 🇰🇳
217 Sweden SE swe 🇸🇪
218 Switzerland CH deu;fra;ita 🇨🇭
219 Syria SY ara 🇸🇾
220 São Tomé and Príncipe Sao Tome and Principe ST por 🇸🇹
221 Taiwan TW zho 🇹🇼
222 Tajikistan TJ rus;tgk 🇹🇯
223 Tanzania TZ eng;swa 🇹🇿
249 Yemen YE ara 🇾🇪
250 Zambia ZM eng 🇿🇲
251 Zimbabwe ZW bwg;eng;kck;hio;ndc;nde;nya;sna;sot;toi;tsn;tso;ven;xho;zib 🇿🇼
Åland AX swe 🇦🇽

39576
data/feeds.csv Normal file

File diff suppressed because it is too large Load diff

View file

@ -3198,7 +3198,6 @@ UK,Wales,GB-WLS
UM,Palmyra Atoll,UM-95
US,Alabama,US-AL
US,Alaska,US-AK
US,American Samoa,US-AS
US,Arizona,US-AZ
US,Arkansas,US-AR
US,California,US-CA
@ -3208,7 +3207,6 @@ US,Delaware,US-DE
US,District of Columbia,US-DC
US,Florida,US-FL
US,Georgia,US-GA
US,Guam,US-GU
US,Hawaii,US-HI
US,Idaho,US-ID
US,Illinois,US-IL
@ -3233,18 +3231,15 @@ US,New Mexico,US-NM
US,New York,US-NY
US,North Carolina,US-NC
US,North Dakota,US-ND
US,Northern Mariana Islands,US-MP
US,Ohio,US-OH
US,Oklahoma,US-OK
US,Oregon,US-OR
US,Pennsylvania,US-PA
US,Puerto Rico,US-PR
US,Rhode Island,US-RI
US,South Carolina,US-SC
US,South Dakota,US-SD
US,Tennessee,US-TN
US,Texas,US-TX
US,U.S. Virgin Islands,US-VI
US,United States Minor Outlying Islands,US-UM
US,Utah,US-UT
US,Vermont,US-VT

1 country name code
3198 UM Palmyra Atoll UM-95
3199 US Alabama US-AL
3200 US Alaska US-AK
US American Samoa US-AS
3201 US Arizona US-AZ
3202 US Arkansas US-AR
3203 US California US-CA
3207 US District of Columbia US-DC
3208 US Florida US-FL
3209 US Georgia US-GA
US Guam US-GU
3210 US Hawaii US-HI
3211 US Idaho US-ID
3212 US Illinois US-IL
3231 US New York US-NY
3232 US North Carolina US-NC
3233 US North Dakota US-ND
US Northern Mariana Islands US-MP
3234 US Ohio US-OH
3235 US Oklahoma US-OK
3236 US Oregon US-OR
3237 US Pennsylvania US-PA
US Puerto Rico US-PR
3238 US Rhode Island US-RI
3239 US South Carolina US-SC
3240 US South Dakota US-SD
3241 US Tennessee US-TN
3242 US Texas US-TX
US U.S. Virgin Islands US-VI
3243 US United States Minor Outlying Islands US-UM
3244 US Utah US-UT
3245 US Vermont US-VT

417
data/timezones.csv Normal file
View file

@ -0,0 +1,417 @@
id,utc_offset,countries
Africa/Abidjan,+00:00,CI;BF;GH;GM;GN;IS;ML;MR;SH;SL;SN;TG
Africa/Accra,+00:00,GH
Africa/Addis_Ababa,+03:00,ET
Africa/Algiers,+01:00,DZ
Africa/Asmara,+03:00,ER
Africa/Bamako,+00:00,ML
Africa/Bangui,+01:00,CF
Africa/Banjul,+00:00,GM
Africa/Bissau,+00:00,GW
Africa/Blantyre,+02:00,MW
Africa/Brazzaville,+01:00,CG
Africa/Bujumbura,+02:00,BI
Africa/Cairo,+02:00,EG
Africa/Casablanca,+00:00,MA
Africa/Ceuta,+01:00,ES
Africa/Conakry,+00:00,GN
Africa/Dakar,+00:00,SN
Africa/Dar_es_Salaam,+03:00,TZ
Africa/Djibouti,+03:00,DJ
Africa/Douala,+01:00,CM
Africa/El_Aaiun,+00:00,EH
Africa/Freetown,+00:00,SL
Africa/Gaborone,+02:00,BW
Africa/Harare,+02:00,ZW
Africa/Johannesburg,+02:00,ZA;LS;SZ
Africa/Juba,+02:00,SS
Africa/Kampala,+03:00,UG
Africa/Khartoum,+02:00,SD
Africa/Kigali,+02:00,RW
Africa/Kinshasa,+01:00,CD
Africa/Lagos,+01:00,NG;AO;BJ;CD;CF;CG;CM;GA;GQ;NE
Africa/Libreville,+01:00,GA
Africa/Lome,+00:00,TG
Africa/Luanda,+01:00,AO
Africa/Lubumbashi,+02:00,CD
Africa/Lusaka,+02:00,ZM
Africa/Malabo,+01:00,GQ
Africa/Maputo,+02:00,MZ;BI;BW;CD;MW;RW;ZM;ZW
Africa/Maseru,+02:00,LS
Africa/Mbabane,+02:00,SZ
Africa/Mogadishu,+03:00,SO
Africa/Monrovia,+00:00,LR
Africa/Nairobi,+03:00,KE;DJ;ER;ET;KM;MG;SO;TZ;UG;YT
Africa/Ndjamena,+01:00,TD
Africa/Niamey,+01:00,NE
Africa/Nouakchott,+00:00,MR
Africa/Ouagadougou,+00:00,BF
Africa/Porto-Novo,+01:00,BJ
Africa/Sao_Tome,+00:00,ST
Africa/Tripoli,+02:00,LY
Africa/Tunis,+01:00,TN
Africa/Windhoek,+01:00,NA
America/Adak,-10:00,US
America/Anchorage,-09:00,US
America/Anguilla,-04:00,AI
America/Antigua,-04:00,AG
America/Araguaina,-03:00,BR
America/Argentina/Buenos_Aires,-03:00,AR
America/Argentina/Catamarca,-03:00,AR
America/Argentina/Cordoba,-03:00,AR
America/Argentina/Jujuy,-03:00,AR
America/Argentina/La_Rioja,-03:00,AR
America/Argentina/Mendoza,-03:00,AR
America/Argentina/Rio_Gallegos,-03:00,AR
America/Argentina/Salta,-03:00,AR
America/Argentina/San_Juan,-03:00,AR
America/Argentina/San_Luis,-03:00,AR
America/Argentina/Tucuman,-03:00,AR
America/Argentina/Ushuaia,-03:00,AR
America/Aruba,-04:00,AW
America/Asuncion,-04:00,PY
America/Atikokan,-05:00,CA
America/Bahia,-03:00,BR
America/Bahia_Banderas,-06:00,MX
America/Barbados,-04:00,BB
America/Belem,-03:00,BR
America/Belize,-06:00,BZ
America/Blanc-Sablon,-04:00,CA
America/Boa_Vista,-04:00,BR
America/Bogota,-05:00,CO
America/Boise,-07:00,US
America/Cambridge_Bay,-07:00,CA
America/Campo_Grande,-04:00,BR
America/Cancun,-05:00,MX
America/Caracas,-04:00,VE
America/Cayenne,-03:00,GF
America/Cayman,-05:00,KY
America/Chicago,-06:00,US
America/Chihuahua,-07:00,MX
America/Costa_Rica,-06:00,CR
America/Creston,-07:00,CA
America/Cuiaba,-04:00,BR
America/Curacao,-04:00,CW
America/Danmarkshavn,+00:00,GL
America/Dawson,-07:00,CA
America/Dawson_Creek,-07:00,CA
America/Denver,-07:00,US
America/Detroit,-05:00,US
America/Dominica,-04:00,DM
America/Edmonton,-07:00,CA
America/Eirunepe,-05:00,BR
America/El_Salvador,-06:00,SV
America/Fort_Nelson,-07:00,CA
America/Fortaleza,-03:00,BR
America/Glace_Bay,-04:00,CA
America/Goose_Bay,-04:00,CA
America/Grand_Turk,-05:00,TC
America/Grenada,-04:00,GD
America/Guadeloupe,-04:00,GP
America/Guatemala,-06:00,GT
America/Guayaquil,-05:00,EC
America/Guyana,-04:00,GY
America/Halifax,-04:00,CA
America/Havana,-05:00,CU
America/Hermosillo,-07:00,MX
America/Indiana/Indianapolis,-05:00,US
America/Indiana/Knox,-06:00,US
America/Indiana/Marengo,-05:00,US
America/Indiana/Petersburg,-05:00,US
America/Indiana/Tell_City,-06:00,US
America/Indiana/Vevay,-05:00,US
America/Indiana/Vincennes,-05:00,US
America/Indiana/Winamac,-05:00,US
America/Inuvik,-07:00,CA
America/Iqaluit,-05:00,CA
America/Jamaica,-05:00,JM
America/Juneau,-09:00,US
America/Kentucky/Louisville,-05:00,US
America/Kentucky/Monticello,-05:00,US
America/Kralendijk,-04:00,BQ
America/La_Paz,-04:00,BO
America/Lima,-05:00,PE
America/Los_Angeles,-08:00,US
America/Lower_Princes,-04:00,SX
America/Maceio,-03:00,BR
America/Managua,-06:00,NI
America/Manaus,-04:00,BR
America/Marigot,-04:00,MF
America/Martinique,-04:00,MQ
America/Matamoros,-06:00,MX
America/Mazatlan,-07:00,MX
America/Menominee,-06:00,US
America/Merida,-06:00,MX
America/Metlakatla,-09:00,US
America/Mexico_City,-06:00,MX
America/Miquelon,-03:00,PM
America/Moncton,-04:00,CA
America/Monterrey,-06:00,MX
America/Montevideo,-03:00,UY
America/Montserrat,-04:00,MS
America/Nassau,-05:00,BS
America/New_York,-05:00,US
America/Nipigon,-05:00,CA
America/Nome,-09:00,US
America/Noronha,-02:00,BR
America/North_Dakota/Beulah,-06:00,US
America/North_Dakota/Center,-06:00,US
America/North_Dakota/New_Salem,-06:00,US
America/Nuuk,-02:00,GL
America/Ojinaga,-07:00,MX
America/Panama,-05:00,PA;CA;KY
America/Paramaribo,-03:00,SR
America/Phoenix,-07:00,US;CA
America/Port-au-Prince,-05:00,HT
America/Port_of_Spain,-04:00,TT
America/Porto_Velho,-04:00,BR
America/Puerto_Rico,-04:00,PR;AG;CA;AI;AW;BL;BQ;CW;DM;GD;GP;KN;LC;MF;MS;SX;TT;VC;VG;VI
America/Punta_Arenas,-03:00,CL
America/Rankin_Inlet,-06:00,CA
America/Recife,-03:00,BR
America/Regina,-06:00,CA
America/Resolute,-06:00,CA
America/Rio_Branco,-05:00,BR
America/Santarem,-03:00,BR
America/Santiago,-04:00,CL
America/Santo_Domingo,-04:00,DO
America/Sao_Paulo,-03:00,BR
America/Scoresbysund,-01:00,GL
America/Sitka,-09:00,US
America/St_Barthelemy,-04:00,BL
America/St_Johns,-03:30,CA
America/St_Kitts,-04:00,KN
America/St_Lucia,-04:00,LC
America/St_Thomas,-04:00,VI
America/St_Vincent,-04:00,VC
America/Swift_Current,-06:00,CA
America/Tegucigalpa,-06:00,HN
America/Thule,-04:00,GL
America/Thunder_Bay,-05:00,CA
America/Tijuana,-08:00,MX
America/Toronto,-05:00,CA;BS
America/Tortola,-04:00,VG
America/Vancouver,-08:00,CA
America/Whitehorse,-07:00,CA
America/Winnipeg,-06:00,CA
America/Yakutat,-09:00,US
Antarctica/Casey,+08:00,AQ
Antarctica/Davis,+07:00,AQ
Antarctica/DumontDUrville,+10:00,AQ
Antarctica/Macquarie,+10:00,AU
Antarctica/Mawson,+05:00,AQ
Antarctica/McMurdo,+12:00,AQ
Antarctica/Palmer,-03:00,AQ
Antarctica/Rothera,-03:00,AQ
Antarctica/Syowa,+03:00,AQ
Antarctica/Troll,+00:00,AQ
Antarctica/Vostok,+06:00,AQ
Arctic/Longyearbyen,+01:00,SJ
Asia/Aden,+03:00,YE
Asia/Almaty,+05:00,KZ
Asia/Amman,+02:00,JO
Asia/Anadyr,+12:00,RU
Asia/Aqtau,+05:00,KZ
Asia/Aqtobe,+05:00,KZ
Asia/Ashgabat,+05:00,TM
Asia/Atyrau,+05:00,KZ
Asia/Baghdad,+03:00,IQ
Asia/Bahrain,+03:00,BH
Asia/Baku,+04:00,AZ
Asia/Bangkok,+07:00,TH;CX;KH;LA;VN
Asia/Barnaul,+07:00,RU
Asia/Beirut,+02:00,LB
Asia/Bishkek,+06:00,KG
Asia/Brunei,+08:00,BN
Asia/Chita,+09:00,RU
Asia/Colombo,+05:30,LK
Asia/Damascus,+02:00,SY
Asia/Dhaka,+06:00,BD
Asia/Dili,+09:00,TL
Asia/Dubai,+04:00,AE;OM;RE;SC;TF
Asia/Dushanbe,+05:00,TJ
Asia/Famagusta,+02:00,CY
Asia/Gaza,+02:00,PS
Asia/Hebron,+02:00,PS
Asia/Ho_Chi_Minh,+07:00,VN
Asia/Hong_Kong,+08:00,HK
Asia/Hovd,+07:00,MN
Asia/Irkutsk,+08:00,RU
Asia/Jakarta,+07:00,ID
Asia/Jayapura,+09:00,ID
Asia/Jerusalem,+02:00,IL
Asia/Kabul,+04:30,AF
Asia/Kamchatka,+12:00,RU
Asia/Karachi,+05:00,PK
Asia/Kathmandu,+05:45,NP
Asia/Khandyga,+09:00,RU
Asia/Kolkata,+05:30,IN
Asia/Krasnoyarsk,+07:00,RU
Asia/Kuala_Lumpur,+08:00,MY
Asia/Kuching,+08:00,MY;BN
Asia/Kuwait,+03:00,KW
Asia/Macau,+08:00,MO
Asia/Magadan,+11:00,RU
Asia/Makassar,+08:00,ID
Asia/Manila,+08:00,PH
Asia/Muscat,+04:00,OM
Asia/Nicosia,+02:00,CY
Asia/Novokuznetsk,+07:00,RU
Asia/Novosibirsk,+07:00,RU
Asia/Omsk,+06:00,RU
Asia/Oral,+05:00,KZ
Asia/Phnom_Penh,+07:00,KH
Asia/Pontianak,+07:00,ID
Asia/Pyongyang,+09:00,KP
Asia/Qatar,+03:00,QA;BH
Asia/Qostanay,+06:00,KZ
Asia/Qyzylorda,+05:00,KZ
Asia/Riyadh,+03:00,SA;AQ;KW;YE
Asia/Sakhalin,+11:00,RU
Asia/Samarkand,+05:00,UZ
Asia/Seoul,+09:00,KR
Asia/Shanghai,+08:00,CN
Asia/Singapore,+08:00,SG;MY
Asia/Srednekolymsk,+11:00,RU
Asia/Taipei,+08:00,TW
Asia/Tashkent,+05:00,UZ
Asia/Tbilisi,+04:00,GE
Asia/Tehran,+03:30,IR
Asia/Thimphu,+06:00,BT
Asia/Tokyo,+09:00,JP
Asia/Tomsk,+07:00,RU
Asia/Ulaanbaatar,+08:00,MN
Asia/Urumqi,+06:00,CN
Asia/Ust-Nera,+10:00,RU
Asia/Vientiane,+07:00,LA
Asia/Vladivostok,+10:00,RU
Asia/Yakutsk,+09:00,RU
Asia/Yangon,+06:30,MM;CC
Asia/Yekaterinburg,+05:00,RU
Asia/Yerevan,+04:00,AM
Atlantic/Azores,-01:00,PT
Atlantic/Bermuda,-04:00,BM
Atlantic/Canary,+00:00,ES
Atlantic/Cape_Verde,-01:00,CV
Atlantic/Faroe,+00:00,FO
Atlantic/Madeira,+00:00,PT
Atlantic/Reykjavik,+00:00,IS
Atlantic/South_Georgia,-02:00,GS
Atlantic/St_Helena,+00:00,SH
Atlantic/Stanley,-03:00,FK
Australia/Adelaide,+09:30,AU
Australia/Brisbane,+10:00,AU
Australia/Broken_Hill,+09:30,AU
Australia/Darwin,+09:30,AU
Australia/Eucla,+08:45,AU
Australia/Hobart,+10:00,AU
Australia/Lindeman,+10:00,AU
Australia/Lord_Howe,+10:30,AU
Australia/Melbourne,+10:00,AU
Australia/Perth,+08:00,AU
Australia/Sydney,+10:00,AU
Europe/Amsterdam,+01:00,NL
Europe/Andorra,+01:00,AD
Europe/Astrakhan,+04:00,RU
Europe/Athens,+02:00,GR
Europe/Belgrade,+01:00,RS;BA;HR;ME;MK;SI
Europe/Berlin,+01:00,DE;DK;NO;SE;SJ
Europe/Bratislava,+01:00,SK
Europe/Brussels,+01:00,BE;LU;NL
Europe/Bucharest,+02:00,RO
Europe/Budapest,+01:00,HU
Europe/Chisinau,+02:00,MD
Europe/Copenhagen,+01:00,DK
Europe/Dublin,+00:00,IE
Europe/Gibraltar,+01:00,GI
Europe/Guernsey,+00:00,GG
Europe/Helsinki,+02:00,FI;AX
Europe/Isle_of_Man,+00:00,IM
Europe/Istanbul,+03:00,TR
Europe/Jersey,+00:00,JE
Europe/Kaliningrad,+02:00,RU
Europe/Kirov,+03:00,RU
Europe/Kyiv,+02:00,UA
Europe/Lisbon,+00:00,PT
Europe/Ljubljana,+01:00,SI
Europe/London,+00:00,UK;GG;IM;JE
Europe/Luxembourg,+01:00,LU
Europe/Madrid,+01:00,ES
Europe/Malta,+01:00,MT
Europe/Mariehamn,+02:00,AX
Europe/Minsk,+03:00,BY
Europe/Monaco,+01:00,MC
Europe/Moscow,+03:00,RU
Europe/Oslo,+01:00,NO
Europe/Paris,+01:00,FR;MC
Europe/Podgorica,+01:00,ME
Europe/Prague,+01:00,CZ;SK
Europe/Riga,+02:00,LV
Europe/Rome,+01:00,IT;SM;VA
Europe/Samara,+04:00,RU
Europe/San_Marino,+01:00,SM
Europe/Sarajevo,+01:00,BA
Europe/Saratov,+04:00,RU
Europe/Simferopol,+03:00,RU;UA
Europe/Skopje,+01:00,MK
Europe/Sofia,+02:00,BG
Europe/Stockholm,+01:00,SE
Europe/Tallinn,+02:00,EE
Europe/Tirane,+01:00,AL
Europe/Ulyanovsk,+04:00,RU
Europe/Vaduz,+01:00,LI
Europe/Vatican,+01:00,VA
Europe/Vienna,+01:00,AT
Europe/Vilnius,+02:00,LT
Europe/Volgograd,+03:00,RU
Europe/Warsaw,+01:00,PL
Europe/Zagreb,+01:00,HR
Europe/Zurich,+01:00,CH;DE;LI
Indian/Antananarivo,+03:00,MG
Indian/Chagos,+06:00,IO
Indian/Christmas,+07:00,CX
Indian/Cocos,+06:30,CC
Indian/Comoro,+03:00,KM
Indian/Kerguelen,+05:00,TF
Indian/Mahe,+04:00,SC
Indian/Maldives,+05:00,MV;TF
Indian/Mauritius,+04:00,MU
Indian/Mayotte,+03:00,YT
Indian/Reunion,+04:00,RE
Pacific/Apia,+13:00,WS
Pacific/Auckland,+12:00,NZ;AQ
Pacific/Bougainville,+11:00,PG
Pacific/Chatham,+12:45,NZ
Pacific/Chuuk,+10:00,FM
Pacific/Easter,-06:00,CL
Pacific/Efate,+11:00,VU
Pacific/Fakaofo,+13:00,TK
Pacific/Fiji,+12:00,FJ
Pacific/Funafuti,+12:00,TV
Pacific/Galapagos,-06:00,EC
Pacific/Gambier,-09:00,PF
Pacific/Guadalcanal,+11:00,SB;FM
Pacific/Guam,+10:00,GU;MP
Pacific/Honolulu,-10:00,US
Pacific/Kiritimati,+14:00,KI
Pacific/Kosrae,+11:00,FM
Pacific/Kwajalein,+12:00,MH
Pacific/Majuro,+12:00,MH
Pacific/Marquesas,-09:30,PF
Pacific/Midway,-11:00,UM
Pacific/Nauru,+12:00,NR
Pacific/Niue,-11:00,NU
Pacific/Norfolk,+11:00,NF
Pacific/Noumea,+11:00,NC
Pacific/Pago_Pago,-11:00,AS;UM
Pacific/Palau,+09:00,PW
Pacific/Pitcairn,-08:00,PN
Pacific/Pohnpei,+11:00,FM
Pacific/Port_Moresby,+10:00,PG;AQ;FM
Pacific/Rarotonga,-10:00,CK
Pacific/Saipan,+10:00,MP
Pacific/Tahiti,-10:00,PF
Pacific/Tarawa,+12:00,KI;MH;TV;UM;WF
Pacific/Tongatapu,+13:00,TO
Pacific/Wake,+12:00,UM
Pacific/Wallis,+12:00,WF
1 id utc_offset countries
2 Africa/Abidjan +00:00 CI;BF;GH;GM;GN;IS;ML;MR;SH;SL;SN;TG
3 Africa/Accra +00:00 GH
4 Africa/Addis_Ababa +03:00 ET
5 Africa/Algiers +01:00 DZ
6 Africa/Asmara +03:00 ER
7 Africa/Bamako +00:00 ML
8 Africa/Bangui +01:00 CF
9 Africa/Banjul +00:00 GM
10 Africa/Bissau +00:00 GW
11 Africa/Blantyre +02:00 MW
12 Africa/Brazzaville +01:00 CG
13 Africa/Bujumbura +02:00 BI
14 Africa/Cairo +02:00 EG
15 Africa/Casablanca +00:00 MA
16 Africa/Ceuta +01:00 ES
17 Africa/Conakry +00:00 GN
18 Africa/Dakar +00:00 SN
19 Africa/Dar_es_Salaam +03:00 TZ
20 Africa/Djibouti +03:00 DJ
21 Africa/Douala +01:00 CM
22 Africa/El_Aaiun +00:00 EH
23 Africa/Freetown +00:00 SL
24 Africa/Gaborone +02:00 BW
25 Africa/Harare +02:00 ZW
26 Africa/Johannesburg +02:00 ZA;LS;SZ
27 Africa/Juba +02:00 SS
28 Africa/Kampala +03:00 UG
29 Africa/Khartoum +02:00 SD
30 Africa/Kigali +02:00 RW
31 Africa/Kinshasa +01:00 CD
32 Africa/Lagos +01:00 NG;AO;BJ;CD;CF;CG;CM;GA;GQ;NE
33 Africa/Libreville +01:00 GA
34 Africa/Lome +00:00 TG
35 Africa/Luanda +01:00 AO
36 Africa/Lubumbashi +02:00 CD
37 Africa/Lusaka +02:00 ZM
38 Africa/Malabo +01:00 GQ
39 Africa/Maputo +02:00 MZ;BI;BW;CD;MW;RW;ZM;ZW
40 Africa/Maseru +02:00 LS
41 Africa/Mbabane +02:00 SZ
42 Africa/Mogadishu +03:00 SO
43 Africa/Monrovia +00:00 LR
44 Africa/Nairobi +03:00 KE;DJ;ER;ET;KM;MG;SO;TZ;UG;YT
45 Africa/Ndjamena +01:00 TD
46 Africa/Niamey +01:00 NE
47 Africa/Nouakchott +00:00 MR
48 Africa/Ouagadougou +00:00 BF
49 Africa/Porto-Novo +01:00 BJ
50 Africa/Sao_Tome +00:00 ST
51 Africa/Tripoli +02:00 LY
52 Africa/Tunis +01:00 TN
53 Africa/Windhoek +01:00 NA
54 America/Adak -10:00 US
55 America/Anchorage -09:00 US
56 America/Anguilla -04:00 AI
57 America/Antigua -04:00 AG
58 America/Araguaina -03:00 BR
59 America/Argentina/Buenos_Aires -03:00 AR
60 America/Argentina/Catamarca -03:00 AR
61 America/Argentina/Cordoba -03:00 AR
62 America/Argentina/Jujuy -03:00 AR
63 America/Argentina/La_Rioja -03:00 AR
64 America/Argentina/Mendoza -03:00 AR
65 America/Argentina/Rio_Gallegos -03:00 AR
66 America/Argentina/Salta -03:00 AR
67 America/Argentina/San_Juan -03:00 AR
68 America/Argentina/San_Luis -03:00 AR
69 America/Argentina/Tucuman -03:00 AR
70 America/Argentina/Ushuaia -03:00 AR
71 America/Aruba -04:00 AW
72 America/Asuncion -04:00 PY
73 America/Atikokan -05:00 CA
74 America/Bahia -03:00 BR
75 America/Bahia_Banderas -06:00 MX
76 America/Barbados -04:00 BB
77 America/Belem -03:00 BR
78 America/Belize -06:00 BZ
79 America/Blanc-Sablon -04:00 CA
80 America/Boa_Vista -04:00 BR
81 America/Bogota -05:00 CO
82 America/Boise -07:00 US
83 America/Cambridge_Bay -07:00 CA
84 America/Campo_Grande -04:00 BR
85 America/Cancun -05:00 MX
86 America/Caracas -04:00 VE
87 America/Cayenne -03:00 GF
88 America/Cayman -05:00 KY
89 America/Chicago -06:00 US
90 America/Chihuahua -07:00 MX
91 America/Costa_Rica -06:00 CR
92 America/Creston -07:00 CA
93 America/Cuiaba -04:00 BR
94 America/Curacao -04:00 CW
95 America/Danmarkshavn +00:00 GL
96 America/Dawson -07:00 CA
97 America/Dawson_Creek -07:00 CA
98 America/Denver -07:00 US
99 America/Detroit -05:00 US
100 America/Dominica -04:00 DM
101 America/Edmonton -07:00 CA
102 America/Eirunepe -05:00 BR
103 America/El_Salvador -06:00 SV
104 America/Fort_Nelson -07:00 CA
105 America/Fortaleza -03:00 BR
106 America/Glace_Bay -04:00 CA
107 America/Goose_Bay -04:00 CA
108 America/Grand_Turk -05:00 TC
109 America/Grenada -04:00 GD
110 America/Guadeloupe -04:00 GP
111 America/Guatemala -06:00 GT
112 America/Guayaquil -05:00 EC
113 America/Guyana -04:00 GY
114 America/Halifax -04:00 CA
115 America/Havana -05:00 CU
116 America/Hermosillo -07:00 MX
117 America/Indiana/Indianapolis -05:00 US
118 America/Indiana/Knox -06:00 US
119 America/Indiana/Marengo -05:00 US
120 America/Indiana/Petersburg -05:00 US
121 America/Indiana/Tell_City -06:00 US
122 America/Indiana/Vevay -05:00 US
123 America/Indiana/Vincennes -05:00 US
124 America/Indiana/Winamac -05:00 US
125 America/Inuvik -07:00 CA
126 America/Iqaluit -05:00 CA
127 America/Jamaica -05:00 JM
128 America/Juneau -09:00 US
129 America/Kentucky/Louisville -05:00 US
130 America/Kentucky/Monticello -05:00 US
131 America/Kralendijk -04:00 BQ
132 America/La_Paz -04:00 BO
133 America/Lima -05:00 PE
134 America/Los_Angeles -08:00 US
135 America/Lower_Princes -04:00 SX
136 America/Maceio -03:00 BR
137 America/Managua -06:00 NI
138 America/Manaus -04:00 BR
139 America/Marigot -04:00 MF
140 America/Martinique -04:00 MQ
141 America/Matamoros -06:00 MX
142 America/Mazatlan -07:00 MX
143 America/Menominee -06:00 US
144 America/Merida -06:00 MX
145 America/Metlakatla -09:00 US
146 America/Mexico_City -06:00 MX
147 America/Miquelon -03:00 PM
148 America/Moncton -04:00 CA
149 America/Monterrey -06:00 MX
150 America/Montevideo -03:00 UY
151 America/Montserrat -04:00 MS
152 America/Nassau -05:00 BS
153 America/New_York -05:00 US
154 America/Nipigon -05:00 CA
155 America/Nome -09:00 US
156 America/Noronha -02:00 BR
157 America/North_Dakota/Beulah -06:00 US
158 America/North_Dakota/Center -06:00 US
159 America/North_Dakota/New_Salem -06:00 US
160 America/Nuuk -02:00 GL
161 America/Ojinaga -07:00 MX
162 America/Panama -05:00 PA;CA;KY
163 America/Paramaribo -03:00 SR
164 America/Phoenix -07:00 US;CA
165 America/Port-au-Prince -05:00 HT
166 America/Port_of_Spain -04:00 TT
167 America/Porto_Velho -04:00 BR
168 America/Puerto_Rico -04:00 PR;AG;CA;AI;AW;BL;BQ;CW;DM;GD;GP;KN;LC;MF;MS;SX;TT;VC;VG;VI
169 America/Punta_Arenas -03:00 CL
170 America/Rankin_Inlet -06:00 CA
171 America/Recife -03:00 BR
172 America/Regina -06:00 CA
173 America/Resolute -06:00 CA
174 America/Rio_Branco -05:00 BR
175 America/Santarem -03:00 BR
176 America/Santiago -04:00 CL
177 America/Santo_Domingo -04:00 DO
178 America/Sao_Paulo -03:00 BR
179 America/Scoresbysund -01:00 GL
180 America/Sitka -09:00 US
181 America/St_Barthelemy -04:00 BL
182 America/St_Johns -03:30 CA
183 America/St_Kitts -04:00 KN
184 America/St_Lucia -04:00 LC
185 America/St_Thomas -04:00 VI
186 America/St_Vincent -04:00 VC
187 America/Swift_Current -06:00 CA
188 America/Tegucigalpa -06:00 HN
189 America/Thule -04:00 GL
190 America/Thunder_Bay -05:00 CA
191 America/Tijuana -08:00 MX
192 America/Toronto -05:00 CA;BS
193 America/Tortola -04:00 VG
194 America/Vancouver -08:00 CA
195 America/Whitehorse -07:00 CA
196 America/Winnipeg -06:00 CA
197 America/Yakutat -09:00 US
198 Antarctica/Casey +08:00 AQ
199 Antarctica/Davis +07:00 AQ
200 Antarctica/DumontDUrville +10:00 AQ
201 Antarctica/Macquarie +10:00 AU
202 Antarctica/Mawson +05:00 AQ
203 Antarctica/McMurdo +12:00 AQ
204 Antarctica/Palmer -03:00 AQ
205 Antarctica/Rothera -03:00 AQ
206 Antarctica/Syowa +03:00 AQ
207 Antarctica/Troll +00:00 AQ
208 Antarctica/Vostok +06:00 AQ
209 Arctic/Longyearbyen +01:00 SJ
210 Asia/Aden +03:00 YE
211 Asia/Almaty +05:00 KZ
212 Asia/Amman +02:00 JO
213 Asia/Anadyr +12:00 RU
214 Asia/Aqtau +05:00 KZ
215 Asia/Aqtobe +05:00 KZ
216 Asia/Ashgabat +05:00 TM
217 Asia/Atyrau +05:00 KZ
218 Asia/Baghdad +03:00 IQ
219 Asia/Bahrain +03:00 BH
220 Asia/Baku +04:00 AZ
221 Asia/Bangkok +07:00 TH;CX;KH;LA;VN
222 Asia/Barnaul +07:00 RU
223 Asia/Beirut +02:00 LB
224 Asia/Bishkek +06:00 KG
225 Asia/Brunei +08:00 BN
226 Asia/Chita +09:00 RU
227 Asia/Colombo +05:30 LK
228 Asia/Damascus +02:00 SY
229 Asia/Dhaka +06:00 BD
230 Asia/Dili +09:00 TL
231 Asia/Dubai +04:00 AE;OM;RE;SC;TF
232 Asia/Dushanbe +05:00 TJ
233 Asia/Famagusta +02:00 CY
234 Asia/Gaza +02:00 PS
235 Asia/Hebron +02:00 PS
236 Asia/Ho_Chi_Minh +07:00 VN
237 Asia/Hong_Kong +08:00 HK
238 Asia/Hovd +07:00 MN
239 Asia/Irkutsk +08:00 RU
240 Asia/Jakarta +07:00 ID
241 Asia/Jayapura +09:00 ID
242 Asia/Jerusalem +02:00 IL
243 Asia/Kabul +04:30 AF
244 Asia/Kamchatka +12:00 RU
245 Asia/Karachi +05:00 PK
246 Asia/Kathmandu +05:45 NP
247 Asia/Khandyga +09:00 RU
248 Asia/Kolkata +05:30 IN
249 Asia/Krasnoyarsk +07:00 RU
250 Asia/Kuala_Lumpur +08:00 MY
251 Asia/Kuching +08:00 MY;BN
252 Asia/Kuwait +03:00 KW
253 Asia/Macau +08:00 MO
254 Asia/Magadan +11:00 RU
255 Asia/Makassar +08:00 ID
256 Asia/Manila +08:00 PH
257 Asia/Muscat +04:00 OM
258 Asia/Nicosia +02:00 CY
259 Asia/Novokuznetsk +07:00 RU
260 Asia/Novosibirsk +07:00 RU
261 Asia/Omsk +06:00 RU
262 Asia/Oral +05:00 KZ
263 Asia/Phnom_Penh +07:00 KH
264 Asia/Pontianak +07:00 ID
265 Asia/Pyongyang +09:00 KP
266 Asia/Qatar +03:00 QA;BH
267 Asia/Qostanay +06:00 KZ
268 Asia/Qyzylorda +05:00 KZ
269 Asia/Riyadh +03:00 SA;AQ;KW;YE
270 Asia/Sakhalin +11:00 RU
271 Asia/Samarkand +05:00 UZ
272 Asia/Seoul +09:00 KR
273 Asia/Shanghai +08:00 CN
274 Asia/Singapore +08:00 SG;MY
275 Asia/Srednekolymsk +11:00 RU
276 Asia/Taipei +08:00 TW
277 Asia/Tashkent +05:00 UZ
278 Asia/Tbilisi +04:00 GE
279 Asia/Tehran +03:30 IR
280 Asia/Thimphu +06:00 BT
281 Asia/Tokyo +09:00 JP
282 Asia/Tomsk +07:00 RU
283 Asia/Ulaanbaatar +08:00 MN
284 Asia/Urumqi +06:00 CN
285 Asia/Ust-Nera +10:00 RU
286 Asia/Vientiane +07:00 LA
287 Asia/Vladivostok +10:00 RU
288 Asia/Yakutsk +09:00 RU
289 Asia/Yangon +06:30 MM;CC
290 Asia/Yekaterinburg +05:00 RU
291 Asia/Yerevan +04:00 AM
292 Atlantic/Azores -01:00 PT
293 Atlantic/Bermuda -04:00 BM
294 Atlantic/Canary +00:00 ES
295 Atlantic/Cape_Verde -01:00 CV
296 Atlantic/Faroe +00:00 FO
297 Atlantic/Madeira +00:00 PT
298 Atlantic/Reykjavik +00:00 IS
299 Atlantic/South_Georgia -02:00 GS
300 Atlantic/St_Helena +00:00 SH
301 Atlantic/Stanley -03:00 FK
302 Australia/Adelaide +09:30 AU
303 Australia/Brisbane +10:00 AU
304 Australia/Broken_Hill +09:30 AU
305 Australia/Darwin +09:30 AU
306 Australia/Eucla +08:45 AU
307 Australia/Hobart +10:00 AU
308 Australia/Lindeman +10:00 AU
309 Australia/Lord_Howe +10:30 AU
310 Australia/Melbourne +10:00 AU
311 Australia/Perth +08:00 AU
312 Australia/Sydney +10:00 AU
313 Europe/Amsterdam +01:00 NL
314 Europe/Andorra +01:00 AD
315 Europe/Astrakhan +04:00 RU
316 Europe/Athens +02:00 GR
317 Europe/Belgrade +01:00 RS;BA;HR;ME;MK;SI
318 Europe/Berlin +01:00 DE;DK;NO;SE;SJ
319 Europe/Bratislava +01:00 SK
320 Europe/Brussels +01:00 BE;LU;NL
321 Europe/Bucharest +02:00 RO
322 Europe/Budapest +01:00 HU
323 Europe/Chisinau +02:00 MD
324 Europe/Copenhagen +01:00 DK
325 Europe/Dublin +00:00 IE
326 Europe/Gibraltar +01:00 GI
327 Europe/Guernsey +00:00 GG
328 Europe/Helsinki +02:00 FI;AX
329 Europe/Isle_of_Man +00:00 IM
330 Europe/Istanbul +03:00 TR
331 Europe/Jersey +00:00 JE
332 Europe/Kaliningrad +02:00 RU
333 Europe/Kirov +03:00 RU
334 Europe/Kyiv +02:00 UA
335 Europe/Lisbon +00:00 PT
336 Europe/Ljubljana +01:00 SI
337 Europe/London +00:00 UK;GG;IM;JE
338 Europe/Luxembourg +01:00 LU
339 Europe/Madrid +01:00 ES
340 Europe/Malta +01:00 MT
341 Europe/Mariehamn +02:00 AX
342 Europe/Minsk +03:00 BY
343 Europe/Monaco +01:00 MC
344 Europe/Moscow +03:00 RU
345 Europe/Oslo +01:00 NO
346 Europe/Paris +01:00 FR;MC
347 Europe/Podgorica +01:00 ME
348 Europe/Prague +01:00 CZ;SK
349 Europe/Riga +02:00 LV
350 Europe/Rome +01:00 IT;SM;VA
351 Europe/Samara +04:00 RU
352 Europe/San_Marino +01:00 SM
353 Europe/Sarajevo +01:00 BA
354 Europe/Saratov +04:00 RU
355 Europe/Simferopol +03:00 RU;UA
356 Europe/Skopje +01:00 MK
357 Europe/Sofia +02:00 BG
358 Europe/Stockholm +01:00 SE
359 Europe/Tallinn +02:00 EE
360 Europe/Tirane +01:00 AL
361 Europe/Ulyanovsk +04:00 RU
362 Europe/Vaduz +01:00 LI
363 Europe/Vatican +01:00 VA
364 Europe/Vienna +01:00 AT
365 Europe/Vilnius +02:00 LT
366 Europe/Volgograd +03:00 RU
367 Europe/Warsaw +01:00 PL
368 Europe/Zagreb +01:00 HR
369 Europe/Zurich +01:00 CH;DE;LI
370 Indian/Antananarivo +03:00 MG
371 Indian/Chagos +06:00 IO
372 Indian/Christmas +07:00 CX
373 Indian/Cocos +06:30 CC
374 Indian/Comoro +03:00 KM
375 Indian/Kerguelen +05:00 TF
376 Indian/Mahe +04:00 SC
377 Indian/Maldives +05:00 MV;TF
378 Indian/Mauritius +04:00 MU
379 Indian/Mayotte +03:00 YT
380 Indian/Reunion +04:00 RE
381 Pacific/Apia +13:00 WS
382 Pacific/Auckland +12:00 NZ;AQ
383 Pacific/Bougainville +11:00 PG
384 Pacific/Chatham +12:45 NZ
385 Pacific/Chuuk +10:00 FM
386 Pacific/Easter -06:00 CL
387 Pacific/Efate +11:00 VU
388 Pacific/Fakaofo +13:00 TK
389 Pacific/Fiji +12:00 FJ
390 Pacific/Funafuti +12:00 TV
391 Pacific/Galapagos -06:00 EC
392 Pacific/Gambier -09:00 PF
393 Pacific/Guadalcanal +11:00 SB;FM
394 Pacific/Guam +10:00 GU;MP
395 Pacific/Honolulu -10:00 US
396 Pacific/Kiritimati +14:00 KI
397 Pacific/Kosrae +11:00 FM
398 Pacific/Kwajalein +12:00 MH
399 Pacific/Majuro +12:00 MH
400 Pacific/Marquesas -09:30 PF
401 Pacific/Midway -11:00 UM
402 Pacific/Nauru +12:00 NR
403 Pacific/Niue -11:00 NU
404 Pacific/Norfolk +11:00 NF
405 Pacific/Noumea +11:00 NC
406 Pacific/Pago_Pago -11:00 AS;UM
407 Pacific/Palau +09:00 PW
408 Pacific/Pitcairn -08:00 PN
409 Pacific/Pohnpei +11:00 FM
410 Pacific/Port_Moresby +10:00 PG;AQ;FM
411 Pacific/Rarotonga -10:00 CK
412 Pacific/Saipan +10:00 MP
413 Pacific/Tahiti -10:00 PF
414 Pacific/Tarawa +12:00 KI;MH;TV;UM;WF
415 Pacific/Tongatapu +13:00 TO
416 Pacific/Wake +12:00 UM
417 Pacific/Wallis +12:00 WF

51
eslint.config.mjs Normal file
View file

@ -0,0 +1,51 @@
import typescriptEslint from '@typescript-eslint/eslint-plugin'
import globals from 'globals'
import tsParser from '@typescript-eslint/parser'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import js from '@eslint/js'
import { FlatCompat } from '@eslint/eslintrc'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
})
export default [
...compat.extends('eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'),
{
plugins: {
'@typescript-eslint': typescriptEslint
},
languageOptions: {
globals: {
...globals.node,
...globals.jest
},
parser: tsParser,
ecmaVersion: 'latest',
sourceType: 'module'
},
rules: {
'@typescript-eslint/no-var-requires': 'off',
'no-case-declarations': 'off',
'linebreak-style': ['error', 'unix'],
quotes: [
'error',
'single',
{
avoidEscape: true
}
],
semi: ['error', 'never']
}
}
]

3271
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -8,14 +8,12 @@
"db:export": "tsx scripts/db/export.ts",
"db:update": "tsx scripts/db/update.ts",
"lint": "npx eslint \"{scripts,tests}/**/*.{ts,js}\"",
"test": "jest --runInBand"
"test": "jest --runInBand",
"prepare": "husky"
},
"engines": {
"node": ">=18.0.0 <18.19.0"
"node": ">=18.0.0 <=22.12.0"
},
"pre-commit": [
"db:validate"
],
"private": true,
"author": "Arhey",
"jest": {
@ -25,7 +23,8 @@
"testRegex": "tests/(.*?/)?.*test.(js|ts)$"
},
"dependencies": {
"@freearhey/core": "^0.2.2",
"@eslint/js": "^9.16.0",
"@freearhey/core": "^0.6.0",
"@joi/date": "^2.1.0",
"@json2csv/formatters": "^7.0.3",
"@json2csv/node": "^7.0.3",
@ -34,18 +33,19 @@
"@octokit/plugin-paginate-rest": "^6.0.0",
"@octokit/plugin-rest-endpoint-methods": "^7.1.3",
"@types/jest": "^29.5.5",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@types/joi": "^17.2.3",
"@typescript-eslint/eslint-plugin": "^8.17.0",
"chalk": "^4.1.2",
"commander": "^9.0.0",
"csvtojson": "^2.0.10",
"eslint": "^8.50.0",
"eslint": "^9.19.0",
"eslint-config-prettier": "^9.0.0",
"fs-extra": "^11.2.0",
"globals": "^15.13.0",
"husky": "^9.1.7",
"jest": "^29.7.0",
"joi": "^17.6.0",
"joi": "^17.13.3",
"ts-jest": "^29.1.1",
"tsx": "^4.10.5"
},
"devDependencies": {
"pre-commit": "^1.2.2"
}
}

View file

@ -21,7 +21,10 @@ const opts = {
replaced_by: nullable,
website: nullable,
logo: nullable,
countries: listParser
countries: listParser,
timezones: listParser,
is_main: boolParser,
video_format: nullable
}
}

View file

@ -1,17 +0,0 @@
export class IDCreator {
create(name: string, country: string): string {
const slug = normalize(name)
const code = country.toLowerCase()
return `${slug}.${code}`
}
}
function normalize(name: string) {
return name
.replace(/^@/gi, 'At')
.replace(/^&/i, 'And')
.replace(/\+/gi, 'Plus')
.replace(/\s-(\d)/gi, ' Minus$1')
.replace(/[^a-z\d]+/gi, '')
}

View file

@ -2,6 +2,5 @@ export * from './csv'
export * from './issueParser'
export * from './issueLoader'
export * from './csvParser'
export * from './idCreator'
export * from './issueData'
export * from './issue'

View file

@ -9,27 +9,15 @@ const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
const octokit = new CustomOctokit()
export class IssueLoader {
async load({ labels }: { labels: string[] | string }) {
labels = Array.isArray(labels) ? labels.join(',') : labels
async load(props?: { labels: string[] | string }) {
let labels = ''
if (props && props.labels) {
labels = Array.isArray(props.labels) ? props.labels.join(',') : props.labels
}
let issues: object[] = []
if (TESTING) {
switch (labels) {
case 'channels:add,approved':
issues = require('../../tests/__data__/input/issues/channels_add_approved.js')
break
case 'channels:edit,approved':
issues = require('../../tests/__data__/input/issues/channels_edit_approved.js')
break
case 'channels:remove,approved':
issues = require('../../tests/__data__/input/issues/channels_remove_approved.js')
break
case 'blocklist:add,approved':
issues = require('../../tests/__data__/input/issues/blocklist_add_approved.js')
break
case 'blocklist:remove,approved':
issues = require('../../tests/__data__/input/issues/blocklist_remove_approved.js')
break
}
issues = (await import('../../tests/__data__/input/update/issues.js')).default
} else {
issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
owner: OWNER,

View file

@ -3,40 +3,30 @@ import { IssueData, Issue } from '../core'
const FIELDS = new Dictionary({
'Channel ID': 'channel_id',
'Channel ID (required)': 'channel_id',
'Channel ID (optional)': 'channel_id',
'Channel Name': 'name',
'Channel Name': 'channel_name',
'Feed Name': 'feed_name',
'Feed ID': 'feed_id',
'Main Feed': 'is_main',
'Alternative Names': 'alt_names',
'Alternative Names (optional)': 'alt_names',
Network: 'network',
'Network (optional)': 'network',
Owners: 'owners',
'Owners (optional)': 'owners',
Country: 'country',
Subdivision: 'subdivision',
'Subdivision (optional)': 'subdivision',
City: 'city',
'City (optional)': 'city',
'Broadcast Area': 'broadcast_area',
Timezones: 'timezones',
Format: 'video_format',
Languages: 'languages',
Categories: 'categories',
'Categories (optional)': 'categories',
NSFW: 'is_nsfw',
Launched: 'launched',
'Launched (optional)': 'launched',
Closed: 'closed',
'Closed (optional)': 'closed',
'Replaced By': 'replaced_by',
'Replaced By (optional)': 'replaced_by',
Website: 'website',
'Website (optional)': 'website',
Logo: 'logo',
Reason: 'reason',
Notes: 'notes',
'Notes (optional)': 'notes',
Reference: 'ref',
'Reference (optional)': 'ref',
'Reference (required)': 'ref'
Reference: 'ref'
})
export class IssueParser {
@ -46,7 +36,7 @@ export class IssueParser {
const data = new Dictionary()
fields.forEach((field: string) => {
let [_label, , _value] = field.split(/\r?\n/)
_label = _label ? _label.trim() : ''
_label = _label ? _label.replace(/ \(optional\)| \(required\)/, '').trim() : ''
_value = _value ? _value.trim() : ''
if (!_label || !_value) return data

View file

@ -1,36 +1,49 @@
import { CSV, IssueLoader, CSVParser, IDCreator, Issue, IssueData } from '../core'
import { Channel, Blocked } from '../models'
import { CSV, IssueLoader, CSVParser, Issue, IssueData } from '../core'
import { Channel, Blocked, Feed } from '../models'
import { DATA_DIR } from '../constants'
import { Storage, Collection } from '@freearhey/core'
import { createChannelId, createFeedId } from '../utils'
let blocklist = new Collection()
let channels = new Collection()
let feeds = new Collection()
let issues = new Collection()
const processedIssues = new Collection()
async function main() {
const idCreator = new IDCreator()
const dataStorage = new Storage(DATA_DIR)
const parser = new CSVParser()
const _channels = await dataStorage.load('channels.csv')
channels = (await parser.parse(_channels)).map(data => new Channel(data))
const _blocklist = await dataStorage.load('blocklist.csv')
blocklist = (await parser.parse(_blocklist)).map(data => new Blocked(data))
const loader = new IssueLoader()
await removeChannels({ loader })
await editChannels({ loader, idCreator })
await addChannels({ loader, idCreator })
await blockChannels({ loader })
await unblockChannels({ loader })
issues = await loader.load()
channels = sortBy(channels, 'id')
const channelsCSV = await dataStorage.load('channels.csv')
channels = (await parser.parse(channelsCSV)).map(data => new Channel(data))
const feedsCSV = await dataStorage.load('feeds.csv')
feeds = (await parser.parse(feedsCSV)).map(data => new Feed(data))
const blocklistCSV = await dataStorage.load('blocklist.csv')
blocklist = (await parser.parse(blocklistCSV)).map(data => new Blocked(data))
await removeFeeds()
await removeChannels()
await editFeeds()
await editChannels()
await addFeeds()
await addChannels()
await blockChannels()
await unblockChannels()
channels = channels.sortBy(channel => channel.id.toLowerCase())
const channelsOutput = new CSV({ items: channels }).toString()
await dataStorage.save('channels.csv', channelsOutput)
blocklist = sortBy(blocklist, 'channel')
feeds = feeds.sortBy(feed => `${feed.channel}@${feed.id}`.toLowerCase())
const feedsOutput = new CSV({ items: feeds }).toString()
await dataStorage.save('feeds.csv', feedsOutput)
blocklist = blocklist.sortBy(blocked => blocked.channel.toLowerCase())
const blocklistOutput = new CSV({ items: blocklist }).toString()
await dataStorage.save('blocklist.csv', blocklistOutput)
@ -40,21 +53,133 @@ async function main() {
main()
function sortBy(channels: Collection, key: string) {
const items = channels.all().sort((a, b) => {
const normA = a[key].toLowerCase()
const normB = b[key].toLowerCase()
if (normA < normB) return -1
if (normA > normB) return 1
return 0
})
async function removeFeeds() {
const requests = issues.filter(
issue => issue.labels.includes('feeds:remove') && issue.labels.includes('approved')
)
return new Collection(items)
requests.forEach((issue: Issue) => {
if (issue.data.missing('channel_id') || issue.data.missing('feed_id')) return
const found = feeds.first(
(feed: Feed) =>
feed.channel === issue.data.getString('channel_id') &&
feed.id === issue.data.getString('feed_id')
)
if (!found) return
feeds.remove((feed: Feed) => feed.channel === found.channel && feed.id === found.id)
onFeedRemoval(found.channel, found.id)
processedIssues.push(issue)
})
}
async function removeChannels({ loader }: { loader: IssueLoader }) {
const issues = await loader.load({ labels: ['channels:remove,approved'] })
issues.forEach((issue: Issue) => {
async function editFeeds() {
const requests = issues.filter(
issue => issue.labels.includes('feeds:edit') && issue.labels.includes('approved')
)
requests.forEach((issue: Issue) => {
const data: IssueData = issue.data
if (data.missing('channel_id') || data.missing('feed_id')) return
const found: Feed = feeds.first(
(feed: Feed) =>
feed.channel === data.getString('channel_id') && feed.id === data.getString('feed_id')
)
if (!found) return
let channelId: string | undefined = found.channel
let feedId: string | undefined = found.id
if (data.has('feed_name')) {
const name = data.getString('feed_name') || found.name
if (name) {
feedId = createFeedId(name)
if (feedId) onFeedIdChange(found.channel, found.id, feedId)
}
}
if (data.has('is_main')) {
const isMain = data.getBoolean('is_main') || false
if (isMain) onFeedNewMain(channelId, feedId)
}
if (!feedId || !channelId) return
const updated = new Feed({
channel: channelId,
id: feedId,
name: data.getString('feed_name'),
is_main: data.getBoolean('is_main'),
broadcast_area: data.getArray('broadcast_area'),
timezones: data.getArray('timezones'),
languages: data.getArray('languages'),
video_format: data.getString('video_format')
})
found.merge(updated)
processedIssues.push(issue)
})
}
async function addFeeds() {
const requests = issues.filter(
issue => issue.labels.includes('feeds:add') && issue.labels.includes('approved')
)
requests.forEach((issue: Issue) => {
const data: IssueData = issue.data
if (
data.missing('channel_id') ||
data.missing('feed_name') ||
data.missing('is_main') ||
data.missing('broadcast_area') ||
data.missing('timezones') ||
data.missing('languages') ||
data.missing('video_format')
)
return
const channelId = data.getString('channel_id')
const feedName = data.getString('feed_name') || 'SD'
const feedId = createFeedId(feedName)
if (!channelId || !feedId) return
const found: Feed = feeds.first(
(feed: Feed) => feed.channel === channelId && feed.id === feedId
)
if (found) return
const isMain = data.getBoolean('is_main') || false
if (isMain) onFeedNewMain(channelId, feedId)
feeds.push(
new Feed({
channel: channelId,
id: feedId,
name: feedName,
is_main: data.getBoolean('is_main'),
broadcast_area: data.getArray('broadcast_area'),
timezones: data.getArray('timezones'),
languages: data.getArray('languages'),
video_format: data.getString('video_format')
})
)
processedIssues.push(issue)
})
}
async function removeChannels() {
const requests = issues.filter(
issue => issue.labels.includes('channels:remove') && issue.labels.includes('approved')
)
requests.forEach((issue: Issue) => {
if (issue.data.missing('channel_id')) return
const found = channels.first(
@ -64,13 +189,18 @@ async function removeChannels({ loader }: { loader: IssueLoader }) {
channels.remove((channel: Channel) => channel.id === found.id)
onChannelRemoval(found.id)
processedIssues.push(issue)
})
}
async function editChannels({ loader, idCreator }: { loader: IssueLoader; idCreator: IDCreator }) {
const issues = await loader.load({ labels: ['channels:edit,approved'] })
issues.forEach((issue: Issue) => {
async function editChannels() {
const requests = issues.filter(
issue => issue.labels.includes('channels:edit') && issue.labels.includes('approved')
)
requests.forEach((issue: Issue) => {
const data: IssueData = issue.data
if (data.missing('channel_id')) return
@ -79,18 +209,21 @@ async function editChannels({ loader, idCreator }: { loader: IssueLoader; idCrea
)
if (!found) return
let channelId = found.id
if (data.has('name') || data.has('country')) {
const name = data.getString('name') || found.name
let channelId: string | undefined = found.id
if (data.has('channel_name') || data.has('country')) {
const name = data.getString('channel_name') || found.name
const country = data.getString('country') || found.country
if (name && country) {
channelId = idCreator.create(name, country)
channelId = createChannelId(name, country)
if (channelId) onChannelIdChange(found.id, channelId)
}
}
if (!channelId) return
const updated = new Channel({
id: channelId,
name: data.getString('name'),
name: data.getString('channel_name'),
alt_names: data.getArray('alt_names'),
network: data.getString('network'),
owners: data.getArray('owners'),
@ -114,16 +247,29 @@ async function editChannels({ loader, idCreator }: { loader: IssueLoader; idCrea
})
}
async function addChannels({ loader, idCreator }: { loader: IssueLoader; idCreator: IDCreator }) {
const issues = await loader.load({ labels: ['channels:add,approved'] })
issues.forEach((issue: Issue) => {
async function addChannels() {
const requests = issues.filter(
issue => issue.labels.includes('channels:add') && issue.labels.includes('approved')
)
requests.forEach((issue: Issue) => {
const data: IssueData = issue.data
const name = data.getString('name')
const country = data.getString('country')
if (!name || !country) return
if (
data.missing('channel_name') ||
data.missing('country') ||
data.missing('is_nsfw') ||
data.missing('logo') ||
data.missing('feed_name') ||
data.missing('broadcast_area') ||
data.missing('timezones') ||
data.missing('languages') ||
data.missing('video_format')
)
return
const channelId = idCreator.create(name, country)
const channelId = createChannelId(data.getString('channel_name'), data.getString('country'))
if (!channelId) return
const found: Channel = channels.first((channel: Channel) => channel.id === channelId)
if (found) return
@ -131,7 +277,7 @@ async function addChannels({ loader, idCreator }: { loader: IssueLoader; idCreat
channels.push(
new Channel({
id: channelId,
name: data.getString('name'),
name: data.getString('channel_name'),
alt_names: data.getArray('alt_names'),
network: data.getString('network'),
owners: data.getArray('owners'),
@ -150,13 +296,34 @@ async function addChannels({ loader, idCreator }: { loader: IssueLoader; idCreat
})
)
const feedName = data.getString('feed_name') || 'SD'
feeds.push(
new Feed({
channel: channelId,
id: createFeedId(feedName),
name: feedName,
is_main: true,
broadcast_area: data.getArray('broadcast_area'),
timezones: data.getArray('timezones'),
languages: data.getArray('languages'),
video_format: data.getString('video_format'),
launched: data.getString('launched'),
closed: data.getString('closed'),
replaced_by: data.getString('replaced_by')
})
)
processedIssues.push(issue)
})
}
async function unblockChannels({ loader }: { loader: IssueLoader }) {
const issues = await loader.load({ labels: ['blocklist:remove,approved'] })
issues.forEach((issue: Issue) => {
async function unblockChannels() {
const requests = issues.filter(
issue => issue.labels.includes('blocklist:remove') && issue.labels.includes('approved')
)
requests.forEach((issue: Issue) => {
const data = issue.data
if (data.missing('channel_id')) return
@ -171,9 +338,12 @@ async function unblockChannels({ loader }: { loader: IssueLoader }) {
})
}
async function blockChannels({ loader }: { loader: IssueLoader }) {
const issues = await loader.load({ labels: ['blocklist:add,approved'] })
issues.forEach((issue: Issue) => {
async function blockChannels() {
const requests = issues.filter(
issue => issue.labels.includes('blocklist:add') && issue.labels.includes('approved')
)
requests.forEach((issue: Issue) => {
const data = issue.data
if (data.missing('channel_id')) return
@ -183,12 +353,14 @@ async function blockChannels({ loader }: { loader: IssueLoader }) {
if (found) return
const channel = data.getString('channel_id')
const reason = data.getString('reason')?.toLowerCase()
const ref = data.getString('ref')
if (!channel || !ref) return
if (!channel || !reason || !ref) return
blocklist.push(
new Blocked({
channel,
reason,
ref
})
)
@ -196,3 +368,57 @@ async function blockChannels({ loader }: { loader: IssueLoader }) {
processedIssues.push(issue)
})
}
function onFeedIdChange(channelId: string, feedId: string, newFeedId: string) {
channels.forEach((channel: Channel) => {
if (channel.replaced_by && channel.replaced_by === `${channelId}@${feedId}`) {
channel.replaced_by = `${channelId}@${newFeedId}`
}
})
}
function onFeedNewMain(channelId: string, feedId: string) {
feeds.forEach((feed: Feed) => {
if (feed.channel === channelId && feed.id !== feedId && feed.is_main === true) {
feed.is_main = false
}
})
}
function onFeedRemoval(channelId: string, feedId: string) {
channels.forEach((channel: Channel) => {
if (channel.replaced_by && channel.replaced_by === `${channelId}@${feedId}`) {
channel.replaced_by = ''
}
})
}
function onChannelIdChange(channelId: string, newChannelId: string) {
channels.forEach((channel: Channel) => {
if (channel.replaced_by && channel.replaced_by.includes(channelId)) {
channel.replaced_by = channel.replaced_by.replace(channelId, newChannelId)
}
})
feeds.forEach((feed: Feed) => {
if (feed.channel === channelId) {
feed.channel = newChannelId
}
})
blocklist.forEach((blocked: Blocked) => {
if (blocked.channel === channelId) {
blocked.channel = newChannelId
}
})
}
function onChannelRemoval(channelId: string) {
channels.forEach((channel: Channel) => {
if (channel.replaced_by && channel.replaced_by.includes(channelId)) {
channel.replaced_by = ''
}
})
feeds.remove((feed: Feed) => feed.channel === channelId)
}

View file

@ -1,16 +1,18 @@
import { Collection, Storage, File, Dictionary, Logger } from '@freearhey/core'
import { DATA_DIR } from '../constants'
import schemesData from '../schemes'
import { program } from 'commander'
import Joi from 'joi'
import { CSVParser, IDCreator } from '../core'
import { CSVParser } from '../core'
import chalk from 'chalk'
import { createChannelId } from '../utils'
program.argument('[filepath]', 'Path to file to validate').parse(process.argv)
const logger = new Logger()
const buffer = new Dictionary()
const files = new Dictionary()
const schemes: { [key: string]: object } = require('../schemes')
const schemes: { [key: string]: object } = schemesData
async function main() {
const dataStorage = new Storage(DATA_DIR)
@ -39,21 +41,39 @@ async function main() {
const data = await parser.parse(csv)
const filename = file.name()
let grouped
switch (filename) {
case 'feeds':
buffer.set(
'feeds',
data.keyBy(item => item.channel + item.id)
)
buffer.set(
'feedsByChannel',
data.filter(item => item.is_main).keyBy(item => item.channel)
)
break
case 'blocklist':
grouped = data.keyBy(item => item.channel)
buffer.set(
'blocklist',
data.keyBy(item => item.channel + item.ref)
)
break
case 'categories':
case 'channels':
grouped = data.keyBy(item => item.id)
case 'timezones':
buffer.set(
filename,
data.keyBy(item => item.id)
)
break
default:
grouped = data.keyBy(item => item.code)
buffer.set(
filename,
data.keyBy(item => item.code)
)
break
}
buffer.set(filename, grouped)
files.set(filename, data)
}
@ -69,19 +89,18 @@ async function main() {
let fileErrors = new Collection()
switch (filename) {
case 'channels':
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, 'id'))
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, ['id']))
for (const [i, row] of rowsCopy.entries()) {
fileErrors = fileErrors.concat(validateChannelId(row, i))
fileErrors = fileErrors.concat(validateMainFeed(row, i))
fileErrors = fileErrors.concat(validateChannelBroadcastArea(row, i))
fileErrors = fileErrors.concat(validateReplacedBy(row, i))
fileErrors = fileErrors.concat(
checkValue(i, row, 'id', 'subdivision', buffer.get('subdivisions'))
)
fileErrors = fileErrors.concat(
checkValue(i, row, 'id', 'categories', buffer.get('categories'))
)
fileErrors = fileErrors.concat(
checkValue(i, row, 'id', 'replaced_by', buffer.get('channels'))
)
fileErrors = fileErrors.concat(
checkValue(i, row, 'id', 'languages', buffer.get('languages'))
)
@ -90,13 +109,22 @@ async function main() {
)
}
break
case 'feeds':
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, ['channel', 'id']))
fileErrors = fileErrors.concat(findDuplicateMainFeeds(rowsCopy))
for (const [i, row] of rowsCopy.entries()) {
fileErrors = fileErrors.concat(validateChannel(row.channel, i))
fileErrors = fileErrors.concat(validateTimezones(row, i))
}
break
case 'blocklist':
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, ['channel', 'ref']))
for (const [i, row] of rowsCopy.entries()) {
fileErrors = fileErrors.concat(validateChannel(row.channel, i))
}
break
case 'countries':
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, 'code'))
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, ['code']))
for (const [i, row] of rowsCopy.entries()) {
fileErrors = fileErrors.concat(
checkValue(i, row, 'code', 'languages', buffer.get('languages'))
@ -104,7 +132,7 @@ async function main() {
}
break
case 'subdivisions':
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, 'code'))
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, ['code']))
for (const [i, row] of rowsCopy.entries()) {
fileErrors = fileErrors.concat(
checkValue(i, row, 'code', 'country', buffer.get('countries'))
@ -112,7 +140,7 @@ async function main() {
}
break
case 'regions':
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, 'code'))
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, ['code']))
for (const [i, row] of rowsCopy.entries()) {
fileErrors = fileErrors.concat(
checkValue(i, row, 'code', 'countries', buffer.get('countries'))
@ -120,10 +148,10 @@ async function main() {
}
break
case 'categories':
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, 'id'))
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, ['id']))
break
case 'languages':
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, 'code'))
fileErrors = fileErrors.concat(findDuplicatesBy(rowsCopy, ['code']))
break
}
@ -180,6 +208,30 @@ function checkValue(
return errors
}
function validateReplacedBy(row: { [key: string]: string }, i: number) {
const errors = new Collection()
if (!row.replaced_by) return errors
const channels = buffer.get('channels')
const feeds = buffer.get('feeds')
const [channelId, feedId] = row.replaced_by.split('@')
if (channels.missing(channelId)) {
errors.push({
line: i + 2,
message: `"${row.id}" has an invalid replaced_by "${row.replaced_by}"`
})
} else if (feedId && feeds.missing(channelId + feedId)) {
errors.push({
line: i + 2,
message: `"${row.id}" has an invalid replaced_by "${row.replaced_by}"`
})
}
return errors
}
function validateChannel(channelId: string, i: number) {
const errors = new Collection()
const channels = buffer.get('channels')
@ -194,16 +246,31 @@ function validateChannel(channelId: string, i: number) {
return errors
}
function findDuplicatesBy(rows: { [key: string]: string }[], key: string) {
function validateMainFeed(row: { [key: string]: string }, i: number) {
const errors = new Collection()
const feedsByChannel = buffer.get('feedsByChannel')
if (feedsByChannel.missing(row.id)) {
errors.push({
line: i + 2,
message: `"${row.id}" channel does not have a main feed`
})
}
return errors
}
function findDuplicatesBy(rows: { [key: string]: string }[], keys: string[]) {
const errors = new Collection()
const buffer = new Dictionary()
rows.forEach((row, i) => {
const normId = row[key].toLowerCase()
const normId = keys.map(key => row[key].toString().toLowerCase()).join()
if (buffer.has(normId)) {
const fieldsList = keys.map(key => `${key} "${row[key]}"`).join(' and ')
errors.push({
line: i + 2,
message: `entry with the ${key} "${row[key]}" already exists`
message: `entry with the ${fieldsList} already exists`
})
}
@ -213,10 +280,31 @@ function findDuplicatesBy(rows: { [key: string]: string }[], key: string) {
return errors
}
function findDuplicateMainFeeds(rows: { [key: string]: string }[]) {
const errors = new Collection()
const buffer = new Dictionary()
rows.forEach((row, i) => {
const normId = `${row.channel}${row.is_main}`
if (buffer.has(normId)) {
errors.push({
line: i + 2,
message: `entry with the channel "${row.channel}" and is_main "true" already exists`
})
}
if (row.is_main) {
buffer.set(normId, true)
}
})
return errors
}
function validateChannelId(row: { [key: string]: string }, i: number) {
const errors = new Collection()
const expectedId = new IDCreator().create(row.name, row.country)
const expectedId = createChannelId(row.name, row.country)
if (expectedId !== row.id) {
errors.push({
@ -251,6 +339,22 @@ function validateChannelBroadcastArea(row: { [key: string]: string[] }, i: numbe
return errors
}
function validateTimezones(row: { [key: string]: string[] }, i: number) {
const errors = new Collection()
const timezones = buffer.get('timezones')
row.timezones.forEach((timezone: string) => {
if (timezones.missing(timezone)) {
errors.push({
line: i + 2,
message: `"${row.channel}@${row.id}" has the wrong timezone "${timezone}"`
})
}
})
return errors
}
function handleError(message: string) {
logger.error(chalk.red(message))
process.exit(1)

View file

@ -1,14 +1,17 @@
type BlockedProps = {
channel: string
reason: string
ref: string
}
export class Blocked {
channel: string
reason: string
ref: string
constructor({ ref, channel }: BlockedProps) {
constructor({ ref, reason, channel }: BlockedProps) {
this.channel = channel
this.reason = reason
this.ref = ref
}
}

55
scripts/models/feed.ts Normal file
View file

@ -0,0 +1,55 @@
type FeedProps = {
channel: string
id: string
name?: string
is_main?: boolean
broadcast_area?: string[]
timezones?: string[]
languages?: string[]
video_format?: string
}
export class Feed {
channel: string
id: string
name?: string
is_main?: boolean
broadcast_area: string[]
timezones: string[]
languages: string[]
video_format?: string
constructor({
channel,
id,
name,
is_main,
broadcast_area,
timezones,
languages,
video_format
}: FeedProps) {
this.channel = channel
this.id = id
this.name = name
this.is_main = is_main
this.broadcast_area = broadcast_area || []
this.timezones = timezones || []
this.languages = languages || []
this.video_format = video_format
}
data() {
const { ...object } = this
return object
}
merge(feed: Feed) {
const data: { [key: string]: string | string[] | boolean | undefined } = feed.data()
for (const prop in data) {
if (data[prop] === undefined) continue
this[prop] = data[prop]
}
}
}

View file

@ -1,2 +1,3 @@
export * from './channel'
export * from './blocked'
export * from './feed'

View file

@ -1,8 +0,0 @@
const Joi = require('joi')
module.exports = {
channel: Joi.string()
.regex(/^[A-Za-z0-9]+\.[a-z]{2}$/)
.required(),
ref: Joi.string().uri().required()
}

View file

@ -0,0 +1,11 @@
import Joi from 'joi'
export default {
channel: Joi.string()
.regex(/^[A-Za-z0-9]+\.[a-z]{2}$/)
.required(),
reason: Joi.string()
.valid(...['dmca', 'nsfw'])
.required(),
ref: Joi.string().uri().required()
}

View file

@ -1,10 +0,0 @@
const Joi = require('joi')
module.exports = {
id: Joi.string()
.regex(/^[a-z]+$/)
.required(),
name: Joi.string()
.regex(/^[A-Z]+$/i)
.required()
}

View file

@ -0,0 +1,10 @@
import Joi from 'joi'
export default {
id: Joi.string()
.regex(/^[a-z]+$/)
.required(),
name: Joi.string()
.regex(/^[A-Z]+$/i)
.required()
}

View file

@ -1,13 +1,17 @@
const Joi = require('joi').extend(require('@joi/date'))
const path = require('path')
const url = require('url')
import BaseJoi from 'joi'
import JoiDate from '@joi/date'
import path from 'path'
import url from 'url'
module.exports = {
const Joi = BaseJoi.extend(JoiDate)
export default {
id: Joi.string()
.regex(/^[A-Za-z0-9]+\.[a-z]{2}$/)
.required(),
name: Joi.string()
.regex(/^[a-z0-9-!:&.+'/»#%°$@?|¡–\s_—]+$/i)
.regex(/^((?!\s-\s).)*$/)
.required(),
alt_names: Joi.array().items(
Joi.string()
@ -42,7 +46,7 @@ module.exports = {
launched: Joi.date().format('YYYY-MM-DD').raw().allow(null),
closed: Joi.date().format('YYYY-MM-DD').raw().allow(null).greater(Joi.ref('launched')),
replaced_by: Joi.string()
.regex(/^[A-Za-z0-9]+\.[a-z]{2}$/)
.regex(/^[A-Za-z0-9]+\.[a-z]{2}($|@[A-Za-z0-9]+$)/)
.allow(null),
website: Joi.string()
.regex(/,/, { invert: true })

View file

@ -1,18 +0,0 @@
const Joi = require('joi')
module.exports = {
name: Joi.string()
.regex(/^[\sA-Z\u00C0-\u00FF().-]+$/i)
.required(),
code: Joi.string()
.regex(/^[A-Z]{2}$/)
.required(),
languages: Joi.array().items(
Joi.string()
.regex(/^[a-z]{3}$/)
.required()
),
flag: Joi.string()
.regex(/^[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]$/)
.required()
}

View file

@ -0,0 +1,18 @@
import Joi from 'joi'
export default {
name: Joi.string()
.regex(/^[\sA-Z\u00C0-\u00FF().-]+$/i)
.required(),
code: Joi.string()
.regex(/^[A-Z]{2}$/)
.required(),
languages: Joi.array().items(
Joi.string()
.regex(/^[a-z]{3}$/)
.required()
),
flag: Joi.string()
.regex(/^[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]$/)
.required()
}

36
scripts/schemes/feeds.ts Normal file
View file

@ -0,0 +1,36 @@
import BaseJoi from 'joi'
import JoiDate from '@joi/date'
const Joi = BaseJoi.extend(JoiDate)
export default {
channel: Joi.string()
.regex(/^[A-Za-z0-9]+\.[a-z]{2}$/)
.required(),
id: Joi.string()
.regex(/^[A-Za-z0-9]+$/)
.required(),
name: Joi.string()
.regex(/^[a-z0-9-!:&.+'/»#%°$@?|¡–\s_—]+$/i)
.regex(/^((?!\s-\s).)*$/)
.required(),
is_main: Joi.boolean().strict().required(),
broadcast_area: Joi.array().items(
Joi.string()
.regex(/^(s\/[A-Z]{2}-[A-Z0-9]{1,3}|c\/[A-Z]{2}|r\/[A-Z0-9]{2,7})$/)
.required()
),
timezones: Joi.array().items(
Joi.string()
.regex(/^[a-z-_/]+$/i)
.required()
),
languages: Joi.array().items(
Joi.string()
.regex(/^[a-z]{3}$/)
.required()
),
video_format: Joi.string()
.regex(/^\d+(i|p)$/)
.allow(null)
}

View file

@ -1,7 +0,0 @@
exports.channels = require('./channels')
exports.categories = require('./categories')
exports.countries = require('./countries')
exports.languages = require('./languages')
exports.regions = require('./regions')
exports.subdivisions = require('./subdivisions')
exports.blocklist = require('./blocklist')

21
scripts/schemes/index.ts Normal file
View file

@ -0,0 +1,21 @@
import { default as channels } from './channels'
import { default as categories } from './categories'
import { default as countries } from './countries'
import { default as languages } from './languages'
import { default as regions } from './regions'
import { default as subdivisions } from './subdivisions'
import { default as blocklist } from './blocklist'
import { default as feeds } from './feeds'
import { default as timezones } from './timezones'
export default {
channels,
categories,
countries,
languages,
regions,
subdivisions,
blocklist,
feeds,
timezones
}

View file

@ -1,8 +0,0 @@
const Joi = require('joi')
module.exports = {
code: Joi.string()
.regex(/^[a-z]{3}$/)
.required(),
name: Joi.string().required()
}

View file

@ -0,0 +1,8 @@
import Joi from 'joi'
export default {
code: Joi.string()
.regex(/^[a-z]{3}$/)
.required(),
name: Joi.string().required()
}

View file

@ -1,6 +1,6 @@
const Joi = require('joi')
import Joi from 'joi'
module.exports = {
export default {
name: Joi.string()
.regex(/^[\sA-Z\u00C0-\u00FF().,-]+$/i)
.required(),

View file

@ -1,11 +0,0 @@
const Joi = require('joi')
module.exports = {
country: Joi.string()
.regex(/^[A-Z]{2}$/)
.required(),
name: Joi.string().required(),
code: Joi.string()
.regex(/^[A-Z]{2}-[A-Z0-9]{1,3}$/)
.required()
}

View file

@ -0,0 +1,11 @@
import Joi from 'joi'
export default {
country: Joi.string()
.regex(/^[A-Z]{2}$/)
.required(),
name: Joi.string().required(),
code: Joi.string()
.regex(/^[A-Z]{2}-[A-Z0-9]{1,3}$/)
.required()
}

View file

@ -0,0 +1,11 @@
import Joi from 'joi'
export default {
id: Joi.string()
.regex(/^[a-z-_/]+$/i)
.required(),
utc_offset: Joi.string()
.regex(/^(\+|-)\d{2}:\d{2}$/)
.required(),
countries: Joi.array().items(Joi.string().regex(/^[A-Z]{2}$/))
}

24
scripts/utils.ts Normal file
View file

@ -0,0 +1,24 @@
export function createChannelId(
name: string | undefined,
country: string | undefined
): string | undefined {
if (!name || !country) return undefined
const slug = normalize(name)
const code = country.toLowerCase()
return `${slug}.${code}`
}
export function createFeedId(name: string) {
return normalize(name)
}
function normalize(string: string) {
return string
.replace(/^@/gi, 'At')
.replace(/^&/i, 'And')
.replace(/\+/gi, 'Plus')
.replace(/\s-(\d)/gi, ' Minus$1')
.replace(/[^a-z\d]+/gi, '')
}

View file

@ -1 +1 @@
[{"channel":"AnimalPlanetAfrica.za","ref":"https://github.com/iptv-org/iptv/issues/1831"}]
[{"channel":"AnimalPlanetAfrica.za","reason":"dmca","ref":"https://github.com/iptv-org/iptv/issues/1831"},{"channel":"BeijingSatelliteTV.cn","reason":"dmca","ref":"https://github.com/iptv-org/iptv/issues/1831"}]

View file

@ -1 +1 @@
[{"id":"002RadioTV.do","name":"002 Radio TV","alt_names":[],"network":null,"owners":[],"country":"DO","subdivision":null,"city":null,"broadcast_area":["c/DO"],"languages":["spa"],"categories":["general"],"is_nsfw":false,"launched":null,"closed":null,"replaced_by":null,"website":"https://www.002radio.com/","logo":"https://i.imgur.com/7oNe8xj.png"},{"id":"BeijingSatelliteTV.cn","name":"Beijing Satellite TV","alt_names":["北京卫视"],"network":null,"owners":[],"country":"CN","subdivision":null,"city":"Beijing","broadcast_area":["c/CN"],"languages":["zho"],"categories":["general"],"is_nsfw":false,"launched":"1979-05-16","closed":null,"replaced_by":null,"website":"https://www.brtn.cn/btv/","logo":"https://i.imgur.com/vsktAez.png"},{"id":"M5.hu","name":"M5","alt_names":[],"network":null,"owners":[],"country":"HU","subdivision":null,"city":null,"broadcast_area":["c/HU"],"languages":["hun"],"categories":["auto"],"is_nsfw":true,"launched":null,"closed":"2001-01-01","replaced_by":null,"website":"https://www.mediaklikk.hu/m5/","logo":"https://i.imgur.com/y21wFd0.png"}]
[{"id":"002RadioTV.do","name":"002 Radio TV","alt_names":[],"network":null,"owners":[],"country":"DO","subdivision":null,"city":null,"broadcast_area":["c/DO"],"languages":["spa"],"categories":["general"],"is_nsfw":false,"launched":null,"closed":null,"replaced_by":null,"website":"https://www.002radio.com/","logo":"https://i.imgur.com/7oNe8xj.png"},{"id":"BeijingSatelliteTV.cn","name":"Beijing Satellite TV","alt_names":["北京卫视"],"network":null,"owners":[],"country":"CN","subdivision":null,"city":"Beijing","broadcast_area":["c/CN"],"languages":["zho"],"categories":["general"],"is_nsfw":false,"launched":"1979-05-16","closed":null,"replaced_by":"002RadioTV.do@SD","website":"https://www.brtn.cn/btv/","logo":"https://i.imgur.com/vsktAez.png"},{"id":"M5.hu","name":"M5","alt_names":[],"network":null,"owners":[],"country":"HU","subdivision":null,"city":null,"broadcast_area":["c/HU"],"languages":["hun"],"categories":["auto"],"is_nsfw":true,"launched":null,"closed":"2001-01-01","replaced_by":"BeijingSatelliteTV.cn","website":"https://www.mediaklikk.hu/m5/","logo":"https://i.imgur.com/y21wFd0.png"}]

View file

@ -0,0 +1 @@
[{"channel":"002RadioTV.do","id":"SD","name":"SD","is_main":true,"broadcast_area":["c/DO"],"timezones":["America/Santo_Domingo"],"languages":["spa"],"video_format":"480i"},{"channel":"M5.hu","id":"SD","name":"SD","is_main":false,"broadcast_area":["c/DO"],"timezones":["America/Santo_Domingo"],"languages":["spa"],"video_format":"480i"}]

View file

@ -0,0 +1 @@
[{"id":"Africa/Abidjan","utc_offset":"+00:00","countries":["CI","BF","GH","GM","GN","IS","ML","MR","SH","SL","SN","TG"]}]

View file

@ -1,2 +1,3 @@
channel,ref
HGTVHungary.hu,https://github.com/iptv-org/iptv/issues/1831
channel,reason,ref
beINMoviesTurk.tr,dmca,https://github.com/iptv-org/iptv/issues/1831
HGTVHungary.hu,nsfw,https://github.com/iptv-org/iptv/issues/1831
1 channel reason ref
2 HGTVHungary.hu beINMoviesTurk.tr dmca https://github.com/iptv-org/iptv/issues/1831
3 HGTVHungary.hu nsfw https://github.com/iptv-org/iptv/issues/1831

View file

@ -1,6 +1,8 @@
id,name,alt_names,network,owners,country,subdivision,city,broadcast_area,languages,categories,is_nsfw,launched,closed,replaced_by,website,logo
0TV.dk,0-TV,,,,DK,,København,c/DK,dan,general,FALSE,,,,https://0-tv.dk/,https://i.imgur.com/aR5q6mA.png
1000xHoraTV.uy,1000xHora TV,,,,UY,,Montevideo,c/UY,spa,auto,FALSE,2020-01-01,2021-01-01,M5.hu@HD,https://www.1000xhoratv.com/,https://i.imgur.com/wP3bbYr.png
beINMoviesTurk.tr,beIN Movies Turk,beIN Movies Türk,BBC,Gazprom Media,TR,US-CA,London,c/TR,tur,movies,FALSE,1979-05-16,1980-05-16,M5.hu,http://www.digiturk.com.tr/,https://i.imgur.com/nw8Sa2z.png
M5.hu,M5,,,Duna Médiaszolgáltató Nonprofit Zrt.,HU,,,c/HU,hun,,TRUE,,,,https://www.mediaklikk.hu/m5/,https://i.imgur.com/y21wFd0.png
M5.hu,M5,,,Duna Médiaszolgáltató Nonprofit Zrt.,HU,,,c/HU,hun,,TRUE,2020-01-01,,0TV.dk@SD,https://www.mediaklikk.hu/m5/,https://i.imgur.com/y21wFd0.png
WenzhouEconomicandEducation.cn,Wenzhou Economic and Education,,,,CN,,Wenzhou,c/CN,zho,science,FALSE,,,,,https://www.tvchinese.net/uploads/tv/wzjjkj.jpg
YiwuBusinessChannel.cn,Yiwu Business Channel,,,,CN,,,c/CN,zho,business,FALSE,,,,,https://www.tvchinese.net/uploads/tv/yiwutv.jpg
YiwuNewsIntegratedChannel.cn,Yiwu News Integrated Channel,,,,CN,,,c/CN,zho,news,FALSE,,,,,https://www.tvchinese.net/uploads/tv/yiwutv.jpg
1 id name alt_names network owners country subdivision city broadcast_area languages categories is_nsfw launched closed replaced_by website logo
2 0TV.dk 0-TV DK København c/DK dan general FALSE https://0-tv.dk/ https://i.imgur.com/aR5q6mA.png
3 1000xHoraTV.uy 1000xHora TV UY Montevideo c/UY spa auto FALSE 2020-01-01 2021-01-01 M5.hu@HD https://www.1000xhoratv.com/ https://i.imgur.com/wP3bbYr.png
4 beINMoviesTurk.tr beIN Movies Turk beIN Movies Türk BBC Gazprom Media TR US-CA London c/TR tur movies FALSE 1979-05-16 1980-05-16 M5.hu http://www.digiturk.com.tr/ https://i.imgur.com/nw8Sa2z.png
5 M5.hu M5 Duna Médiaszolgáltató Nonprofit Zrt. HU c/HU hun TRUE 2020-01-01 0TV.dk@SD https://www.mediaklikk.hu/m5/ https://i.imgur.com/y21wFd0.png
6 WenzhouEconomicandEducation.cn Wenzhou Economic and Education CN Wenzhou c/CN zho science FALSE https://www.tvchinese.net/uploads/tv/wzjjkj.jpg
7 YiwuBusinessChannel.cn Yiwu Business Channel CN c/CN zho business FALSE https://www.tvchinese.net/uploads/tv/yiwutv.jpg
8 YiwuNewsIntegratedChannel.cn Yiwu News Integrated Channel CN c/CN zho news FALSE https://www.tvchinese.net/uploads/tv/yiwutv.jpg

View file

@ -0,0 +1,10 @@
channel,id,name,is_main,broadcast_area,timezones,languages,video_format
0TV.dk,SD,SD,TRUE,c/DK,Europe/Copenhagen,dan,576i
1000xHoraTV.uy,HD,HD,TRUE,c/CN,Africa/Johannesburg;Africa/Kigali,zho,576i
1000xHoraTV.uy,SD,SD,FALSE,c/UY,America/Montevideo,spa,576i
beINMoviesTurk.tr,SD,SD,TRUE,c/DO,America/Santo_Domingo,spa,480i
M5.hu,HD,HD,TRUE,c/BR,Africa/Dakar;Africa/El_Aaiun,por;spa,1080i
M5.hu,West,West,FALSE,c/DO,America/Santo_Domingo,spa,480i
WenzhouEconomicandEducation.cn,SD,SD,TRUE,c/CN,Africa/Johannesburg;Africa/Kigali,zho,576i
YiwuBusinessChannel.cn,SD,SD,TRUE,c/CN,Africa/Johannesburg;Africa/Kigali,zho,576i
YiwuNewsIntegratedChannel.cn,SD,SD,TRUE,c/CN,Africa/Johannesburg;Africa/Kigali,zho,576i
1 channel id name is_main broadcast_area timezones languages video_format
2 0TV.dk SD SD TRUE c/DK Europe/Copenhagen dan 576i
3 1000xHoraTV.uy HD HD TRUE c/CN Africa/Johannesburg;Africa/Kigali zho 576i
4 1000xHoraTV.uy SD SD FALSE c/UY America/Montevideo spa 576i
5 beINMoviesTurk.tr SD SD TRUE c/DO America/Santo_Domingo spa 480i
6 M5.hu HD HD TRUE c/BR Africa/Dakar;Africa/El_Aaiun por;spa 1080i
7 M5.hu West West FALSE c/DO America/Santo_Domingo spa 480i
8 WenzhouEconomicandEducation.cn SD SD TRUE c/CN Africa/Johannesburg;Africa/Kigali zho 576i
9 YiwuBusinessChannel.cn SD SD TRUE c/CN Africa/Johannesburg;Africa/Kigali zho 576i
10 YiwuNewsIntegratedChannel.cn SD SD TRUE c/CN Africa/Johannesburg;Africa/Kigali zho 576i

View file

@ -1,2 +0,0 @@
channel,ref
AnimalPlanetAfrica.za,https://github.com/iptv-org/iptv/issues/1831
1 channel ref
2 AnimalPlanetAfrica.za https://github.com/iptv-org/iptv/issues/1831

View file

@ -0,0 +1,3 @@
channel,reason,ref
AnimalPlanetAfrica.za,dmca,https://github.com/iptv-org/iptv/issues/1831
BeijingSatelliteTV.cn,dmca,https://github.com/iptv-org/iptv/issues/1831
1 channel reason ref
2 AnimalPlanetAfrica.za dmca https://github.com/iptv-org/iptv/issues/1831
3 BeijingSatelliteTV.cn dmca https://github.com/iptv-org/iptv/issues/1831

View file

@ -1,4 +1,4 @@
id,name,alt_names,network,owners,country,subdivision,city,broadcast_area,languages,categories,is_nsfw,launched,closed,replaced_by,website,logo
002RadioTV.do,002 Radio TV,,,,DO,,,c/DO,spa,general,FALSE,,,,https://www.002radio.com/,https://i.imgur.com/7oNe8xj.png
BeijingSatelliteTV.cn,Beijing Satellite TV,北京卫视,,,CN,,Beijing,c/CN,zho,general,FALSE,1979-05-16,,,https://www.brtn.cn/btv/,https://i.imgur.com/vsktAez.png
M5.hu,M5,,,,HU,,,c/HU,hun,auto,TRUE,,2001-01-01,,https://www.mediaklikk.hu/m5/,https://i.imgur.com/y21wFd0.png
BeijingSatelliteTV.cn,Beijing Satellite TV,北京卫视,,,CN,,Beijing,c/CN,zho,general,FALSE,1979-05-16,,002RadioTV.do@SD,https://www.brtn.cn/btv/,https://i.imgur.com/vsktAez.png
M5.hu,M5,,,,HU,,,c/HU,hun,auto,TRUE,,2001-01-01,BeijingSatelliteTV.cn,https://www.mediaklikk.hu/m5/,https://i.imgur.com/y21wFd0.png
1 id name alt_names network owners country subdivision city broadcast_area languages categories is_nsfw launched closed replaced_by website logo
2 002RadioTV.do 002 Radio TV DO c/DO spa general FALSE https://www.002radio.com/ https://i.imgur.com/7oNe8xj.png
3 BeijingSatelliteTV.cn Beijing Satellite TV 北京卫视 CN Beijing c/CN zho general FALSE 1979-05-16 002RadioTV.do@SD https://www.brtn.cn/btv/ https://i.imgur.com/vsktAez.png
4 M5.hu M5 HU c/HU hun auto TRUE 2001-01-01 BeijingSatelliteTV.cn https://www.mediaklikk.hu/m5/ https://i.imgur.com/y21wFd0.png

View file

@ -0,0 +1,3 @@
channel,id,name,is_main,broadcast_area,timezones,languages,video_format
002RadioTV.do,SD,SD,TRUE,c/DO,America/Santo_Domingo,spa,480i
M5.hu,SD,SD,FALSE,c/DO,America/Santo_Domingo,spa,480i
1 channel id name is_main broadcast_area timezones languages video_format
2 002RadioTV.do SD SD TRUE c/DO America/Santo_Domingo spa 480i
3 M5.hu SD SD FALSE c/DO America/Santo_Domingo spa 480i

View file

@ -0,0 +1,2 @@
id,utc_offset,countries
Africa/Abidjan,+00:00,CI;BF;GH;GM;GN;IS;ML;MR;SH;SL;SN;TG
1 id utc_offset countries
2 Africa/Abidjan +00:00 CI;BF;GH;GM;GN;IS;ML;MR;SH;SL;SN;TG

View file

@ -1,81 +0,0 @@
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/database/issues/5897',
repository_url: 'https://api.github.com/repos/iptv-org/database',
labels_url: 'https://api.github.com/repos/iptv-org/database/issues/5897/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/database/issues/5897/comments',
events_url: 'https://api.github.com/repos/iptv-org/database/issues/5897/events',
html_url: 'https://github.com/iptv-org/database/issues/5897',
id: 1929261634,
node_id: 'I_kwDOG1Kwp85y_jJC',
number: 5897,
title: 'Block: HGTV Hungary',
user: {
login: 'freearhey',
id: 7253922,
node_id: 'MDQ6VXNlcjcyNTM5MjI=',
avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/freearhey',
html_url: 'https://github.com/freearhey',
followers_url: 'https://api.github.com/users/freearhey/followers',
following_url: 'https://api.github.com/users/freearhey/following{/other_user}',
gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}',
starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions',
organizations_url: 'https://api.github.com/users/freearhey/orgs',
repos_url: 'https://api.github.com/users/freearhey/repos',
events_url: 'https://api.github.com/users/freearhey/events{/privacy}',
received_events_url: 'https://api.github.com/users/freearhey/received_events',
type: 'User',
site_admin: false
},
labels: [
{
id: 5366738347,
node_id: 'LA_kwDOG1Kwp88AAAABP-Htqw',
url: 'https://api.github.com/repos/iptv-org/database/labels/approved',
name: 'approved',
color: '85DDDE',
default: false,
description: ''
},
{
id: 6049155772,
node_id: 'LA_kwDOG1Kwp88AAAABaI7KvA',
url: 'https://api.github.com/repos/iptv-org/database/labels/blocklist:add',
name: 'blocklist:add',
color: 'e99695',
default: false,
description: 'Request to add a channel to the blocklist'
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2023-10-06T00:35:32Z',
updated_at: '2023-10-06T00:35:32Z',
closed_at: null,
author_association: 'CONTRIBUTOR',
active_lock_reason: null,
body: '### Channel ID\n\nHGTVHungary.hu\n\n### Reference\n\nhttps://github.com/iptv-org/iptv/issues/1831\n\n### Notes (optional)\n\n_No response_',
reactions: {
url: 'https://api.github.com/repos/iptv-org/database/issues/5897/reactions',
total_count: 0,
'+1': 0,
'-1': 0,
laugh: 0,
hooray: 0,
confused: 0,
heart: 0,
rocket: 0,
eyes: 0
},
timeline_url: 'https://api.github.com/repos/iptv-org/database/issues/5897/timeline',
performed_via_github_app: null,
state_reason: null
}
]

View file

@ -1,81 +0,0 @@
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/database/issues/5891',
repository_url: 'https://api.github.com/repos/iptv-org/database',
labels_url: 'https://api.github.com/repos/iptv-org/database/issues/5891/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/database/issues/5891/comments',
events_url: 'https://api.github.com/repos/iptv-org/database/issues/5891/events',
html_url: 'https://github.com/iptv-org/database/issues/5891',
id: 1929261634,
node_id: 'I_kwDOG1Kwp85y_jJC',
number: 5891,
title: 'Unblock: Animal Planet Africa',
user: {
login: 'freearhey',
id: 7253922,
node_id: 'MDQ6VXNlcjcyNTM5MjI=',
avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/freearhey',
html_url: 'https://github.com/freearhey',
followers_url: 'https://api.github.com/users/freearhey/followers',
following_url: 'https://api.github.com/users/freearhey/following{/other_user}',
gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}',
starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions',
organizations_url: 'https://api.github.com/users/freearhey/orgs',
repos_url: 'https://api.github.com/users/freearhey/repos',
events_url: 'https://api.github.com/users/freearhey/events{/privacy}',
received_events_url: 'https://api.github.com/users/freearhey/received_events',
type: 'User',
site_admin: false
},
labels: [
{
id: 5366738347,
node_id: 'LA_kwDOG1Kwp88AAAABP-Htqw',
url: 'https://api.github.com/repos/iptv-org/database/labels/approved',
name: 'approved',
color: '85DDDE',
default: false,
description: ''
},
{
id: 6049155772,
node_id: 'LA_kwDOG1Kwp88AAAABaI7KvA',
url: 'https://api.github.com/repos/iptv-org/database/labels/blocklist:add',
name: 'blocklist:remove',
color: 'e99695',
default: false,
description: 'Request to remove a channel from the blocklist'
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2023-10-06T00:35:32Z',
updated_at: '2023-10-06T00:35:32Z',
closed_at: null,
author_association: 'CONTRIBUTOR',
active_lock_reason: null,
body: '### Channel ID\n\nAnimalPlanetAfrica.za\n\n### Reason\n\nOther\n\n### Notes (optional)\n\n_No response_',
reactions: {
url: 'https://api.github.com/repos/iptv-org/database/issues/5891/reactions',
total_count: 0,
'+1': 0,
'-1': 0,
laugh: 0,
hooray: 0,
confused: 0,
heart: 0,
rocket: 0,
eyes: 0
},
timeline_url: 'https://api.github.com/repos/iptv-org/database/issues/5891/timeline',
performed_via_github_app: null,
state_reason: null
}
]

View file

@ -1,239 +0,0 @@
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/database/issues/5900',
repository_url: 'https://api.github.com/repos/iptv-org/database',
labels_url: 'https://api.github.com/repos/iptv-org/database/issues/5900/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/database/issues/5900/comments',
events_url: 'https://api.github.com/repos/iptv-org/database/issues/5900/events',
html_url: 'https://github.com/iptv-org/database/issues/5900',
id: 1929321995,
node_id: 'I_kwDOG1Kwp85y_x4L',
number: 5900,
title: 'Add: Yiwu News Integrated Channel',
user: {
login: 'AntiPontifex',
id: 81566772,
node_id: 'MDQ6VXNlcjgxNTY2Nzcy',
avatar_url: 'https://avatars.githubusercontent.com/u/81566772?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/AntiPontifex',
html_url: 'https://github.com/AntiPontifex',
followers_url: 'https://api.github.com/users/AntiPontifex/followers',
following_url: 'https://api.github.com/users/AntiPontifex/following{/other_user}',
gists_url: 'https://api.github.com/users/AntiPontifex/gists{/gist_id}',
starred_url: 'https://api.github.com/users/AntiPontifex/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/AntiPontifex/subscriptions',
organizations_url: 'https://api.github.com/users/AntiPontifex/orgs',
repos_url: 'https://api.github.com/users/AntiPontifex/repos',
events_url: 'https://api.github.com/users/AntiPontifex/events{/privacy}',
received_events_url: 'https://api.github.com/users/AntiPontifex/received_events',
type: 'User',
site_admin: false
},
labels: [
{
id: 5303575699,
node_id: 'LA_kwDOG1Kwp88AAAABPB4kkw',
url: 'https://api.github.com/repos/iptv-org/database/labels/channels:add',
name: 'channels:add',
color: '017ff8',
default: false,
description: 'Request to add a channel into the database'
},
{
id: 5366738347,
node_id: 'LA_kwDOG1Kwp88AAAABP-Htqw',
url: 'https://api.github.com/repos/iptv-org/database/labels/approved',
name: 'approved',
color: '85DDDE',
default: false,
description: ''
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2023-10-06T02:10:41Z',
updated_at: '2023-10-06T02:52:02Z',
closed_at: null,
author_association: 'CONTRIBUTOR',
active_lock_reason: null,
body: '### Channel Name\n\nYiwu News Integrated Channel\n\n### Alternative Names (optional)\n\n_No response_\n\n### Network (optional)\n\n_No response_\n\n### Owners (optional)\n\n_No response_\n\n### Country\n\nCN\n\n### Subdivision (optional)\n\n_No response_\n\n### City (optional)\n\n_No response_\n\n### Broadcast Area\n\nc/CN\n\n### Languages\n\nzho\n\n### Categories (optional)\n\nnews\n\n### NSFW\n\nFALSE\n\n### Launched (optional)\n\n_No response_\n\n### Closed (optional)\n\n_No response_\n\n### Replaced By (optional)\n\n_No response_\n\n### Website (optional)\n\n_No response_\n\n### Logo\n\nhttps://www.tvchinese.net/uploads/tv/yiwutv.jpg\n\n### Notes\n\n_No response_',
reactions: {
url: 'https://api.github.com/repos/iptv-org/database/issues/5900/reactions',
total_count: 0,
'+1': 0,
'-1': 0,
laugh: 0,
hooray: 0,
confused: 0,
heart: 0,
rocket: 0,
eyes: 0
},
timeline_url: 'https://api.github.com/repos/iptv-org/database/issues/5900/timeline',
performed_via_github_app: null,
state_reason: null
},
{
url: 'https://api.github.com/repos/iptv-org/database/issues/5899',
repository_url: 'https://api.github.com/repos/iptv-org/database',
labels_url: 'https://api.github.com/repos/iptv-org/database/issues/5899/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/database/issues/5899/comments',
events_url: 'https://api.github.com/repos/iptv-org/database/issues/5899/events',
html_url: 'https://github.com/iptv-org/database/issues/5899',
id: 1929318573,
node_id: 'I_kwDOG1Kwp85y_xCt',
number: 5899,
title: 'Add: Yiwu Business Channel',
user: {
login: 'AntiPontifex',
id: 81566772,
node_id: 'MDQ6VXNlcjgxNTY2Nzcy',
avatar_url: 'https://avatars.githubusercontent.com/u/81566772?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/AntiPontifex',
html_url: 'https://github.com/AntiPontifex',
followers_url: 'https://api.github.com/users/AntiPontifex/followers',
following_url: 'https://api.github.com/users/AntiPontifex/following{/other_user}',
gists_url: 'https://api.github.com/users/AntiPontifex/gists{/gist_id}',
starred_url: 'https://api.github.com/users/AntiPontifex/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/AntiPontifex/subscriptions',
organizations_url: 'https://api.github.com/users/AntiPontifex/orgs',
repos_url: 'https://api.github.com/users/AntiPontifex/repos',
events_url: 'https://api.github.com/users/AntiPontifex/events{/privacy}',
received_events_url: 'https://api.github.com/users/AntiPontifex/received_events',
type: 'User',
site_admin: false
},
labels: [
{
id: 5303575699,
node_id: 'LA_kwDOG1Kwp88AAAABPB4kkw',
url: 'https://api.github.com/repos/iptv-org/database/labels/channels:add',
name: 'channels:add',
color: '017ff8',
default: false,
description: 'Request to add a channel into the database'
},
{
id: 5366738347,
node_id: 'LA_kwDOG1Kwp88AAAABP-Htqw',
url: 'https://api.github.com/repos/iptv-org/database/labels/approved',
name: 'approved',
color: '85DDDE',
default: false,
description: ''
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2023-10-06T02:05:11Z',
updated_at: '2023-10-06T02:51:46Z',
closed_at: null,
author_association: 'CONTRIBUTOR',
active_lock_reason: null,
body: '### Channel Name\n\nYiwu Business Channel\n\n### Alternative Names (optional)\n\n_No response_\n\n### Network (optional)\n\n_No response_\n\n### Owners (optional)\n\n_No response_\n\n### Country\n\nCN\n\n### Subdivision (optional)\n\n_No response_\n\n### City (optional)\n\n_No response_\n\n### Broadcast Area\n\nc/CN\n\n### Languages\n\nzho\n\n### Categories (optional)\n\nbusiness\n\n### NSFW\n\nFALSE\n\n### Launched (optional)\n\n_No response_\n\n### Closed (optional)\n\n_No response_\n\n### Replaced By (optional)\n\n_No response_\n\n### Website (optional)\n\n_No response_\n\n### Logo\n\nhttps://www.tvchinese.net/uploads/tv/yiwutv.jpg\n\n### Notes\n\n_No response_',
reactions: {
url: 'https://api.github.com/repos/iptv-org/database/issues/5899/reactions',
total_count: 0,
'+1': 0,
'-1': 0,
laugh: 0,
hooray: 0,
confused: 0,
heart: 0,
rocket: 0,
eyes: 0
},
timeline_url: 'https://api.github.com/repos/iptv-org/database/issues/5899/timeline',
performed_via_github_app: null,
state_reason: null
},
{
url: 'https://api.github.com/repos/iptv-org/database/issues/5898',
repository_url: 'https://api.github.com/repos/iptv-org/database',
labels_url: 'https://api.github.com/repos/iptv-org/database/issues/5898/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/database/issues/5898/comments',
events_url: 'https://api.github.com/repos/iptv-org/database/issues/5898/events',
html_url: 'https://github.com/iptv-org/database/issues/5898',
id: 1929313117,
node_id: 'I_kwDOG1Kwp85y_vtd',
number: 5898,
title: 'Add: Wenzhou Economic and Education',
user: {
login: 'AntiPontifex',
id: 81566772,
node_id: 'MDQ6VXNlcjgxNTY2Nzcy',
avatar_url: 'https://avatars.githubusercontent.com/u/81566772?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/AntiPontifex',
html_url: 'https://github.com/AntiPontifex',
followers_url: 'https://api.github.com/users/AntiPontifex/followers',
following_url: 'https://api.github.com/users/AntiPontifex/following{/other_user}',
gists_url: 'https://api.github.com/users/AntiPontifex/gists{/gist_id}',
starred_url: 'https://api.github.com/users/AntiPontifex/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/AntiPontifex/subscriptions',
organizations_url: 'https://api.github.com/users/AntiPontifex/orgs',
repos_url: 'https://api.github.com/users/AntiPontifex/repos',
events_url: 'https://api.github.com/users/AntiPontifex/events{/privacy}',
received_events_url: 'https://api.github.com/users/AntiPontifex/received_events',
type: 'User',
site_admin: false
},
labels: [
{
id: 5303575699,
node_id: 'LA_kwDOG1Kwp88AAAABPB4kkw',
url: 'https://api.github.com/repos/iptv-org/database/labels/channels:add',
name: 'channels:add',
color: '017ff8',
default: false,
description: 'Request to add a channel into the database'
},
{
id: 5366738347,
node_id: 'LA_kwDOG1Kwp88AAAABP-Htqw',
url: 'https://api.github.com/repos/iptv-org/database/labels/approved',
name: 'approved',
color: '85DDDE',
default: false,
description: ''
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2023-10-06T01:56:32Z',
updated_at: '2023-10-06T02:51:22Z',
closed_at: null,
author_association: 'CONTRIBUTOR',
active_lock_reason: null,
body: '### Channel Name\n\nWenzhou Economic and Education\n\n### Alternative Names (optional)\n\n_No response_\n\n### Network (optional)\n\n_No response_\n\n### Owners (optional)\n\n_No response_\n\n### Country\n\nCN\n\n### Subdivision (optional)\n\n_No response_\n\n### City (optional)\n\nWenzhou\n\n### Broadcast Area\n\nc/CN\n\n### Languages\n\nzho\n\n### Categories (optional)\n\nscience\n\n### NSFW\n\nFALSE\n\n### Launched (optional)\n\n_No response_\n\n### Closed (optional)\n\n_No response_\n\n### Replaced By (optional)\n\n_No response_\n\n### Website (optional)\n\n_No response_\n\n### Logo\n\nhttps://www.tvchinese.net/uploads/tv/wzjjkj.jpg\n\n### Notes\n\n_No response_',
reactions: {
url: 'https://api.github.com/repos/iptv-org/database/issues/5898/reactions',
total_count: 0,
'+1': 0,
'-1': 0,
laugh: 0,
hooray: 0,
confused: 0,
heart: 0,
rocket: 0,
eyes: 0
},
timeline_url: 'https://api.github.com/repos/iptv-org/database/issues/5898/timeline',
performed_via_github_app: null,
state_reason: null
}
]

View file

@ -1,160 +0,0 @@
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/database/issues/5901',
repository_url: 'https://api.github.com/repos/iptv-org/database',
labels_url: 'https://api.github.com/repos/iptv-org/database/issues/5901/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/database/issues/5901/comments',
events_url: 'https://api.github.com/repos/iptv-org/database/issues/5901/events',
html_url: 'https://github.com/iptv-org/database/issues/5901',
id: 1929459171,
node_id: 'I_kwDOG1Kwp85zATXj',
number: 5901,
title: 'Edit: M5',
user: {
login: 'freearhey',
id: 7253922,
node_id: 'MDQ6VXNlcjcyNTM5MjI=',
avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/freearhey',
html_url: 'https://github.com/freearhey',
followers_url: 'https://api.github.com/users/freearhey/followers',
following_url: 'https://api.github.com/users/freearhey/following{/other_user}',
gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}',
starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions',
organizations_url: 'https://api.github.com/users/freearhey/orgs',
repos_url: 'https://api.github.com/users/freearhey/repos',
events_url: 'https://api.github.com/users/freearhey/events{/privacy}',
received_events_url: 'https://api.github.com/users/freearhey/received_events',
type: 'User',
site_admin: false
},
labels: [
{
id: 5303574335,
node_id: 'LA_kwDOG1Kwp88AAAABPB4fPw',
url: 'https://api.github.com/repos/iptv-org/database/labels/channels:edit',
name: 'channels:edit',
color: 'E12977',
default: false,
description: 'Request to edit channel description'
},
{
id: 5366738347,
node_id: 'LA_kwDOG1Kwp88AAAABP-Htqw',
url: 'https://api.github.com/repos/iptv-org/database/labels/approved',
name: 'approved',
color: '85DDDE',
default: false,
description: ''
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2023-10-06T05:25:44Z',
updated_at: '2023-10-06T05:25:44Z',
closed_at: null,
author_association: 'CONTRIBUTOR',
active_lock_reason: null,
body: '### Channel ID (required)\n\nM5.hu\n\n### Channel Name\n\n_No response_\n\n### Alternative Names\n\n_No response_\n\n### Network\n\n_No response_\n\n### Owners\n\nDuna Médiaszolgáltató Nonprofit Zrt.\n\n### Country\n\n_No response_\n\n### Subdivision\n\n_No response_\n\n### City\n\n_No response_\n\n### Broadcast Area\n\n_No response_\n\n### Languages\n\n_No response_\n\n### Categories\n\n~\n\n### NSFW\n\nNone\n\n### Launched\n\n_No response_\n\n### Closed\n\n~\n\n### Replaced By\n\n_No response_\n\n### Website\n\n_No response_\n\n### Logo\n\n_No response_\n\n### Notes\n\n_No response_',
reactions: {
url: 'https://api.github.com/repos/iptv-org/database/issues/5901/reactions',
total_count: 0,
'+1': 0,
'-1': 0,
laugh: 0,
hooray: 0,
confused: 0,
heart: 0,
rocket: 0,
eyes: 0
},
timeline_url: 'https://api.github.com/repos/iptv-org/database/issues/5901/timeline',
performed_via_github_app: null,
state_reason: null
},
{
url: 'https://api.github.com/repos/iptv-org/database/issues/5701',
repository_url: 'https://api.github.com/repos/iptv-org/database',
labels_url: 'https://api.github.com/repos/iptv-org/database/issues/5701/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/database/issues/5701/comments',
events_url: 'https://api.github.com/repos/iptv-org/database/issues/5701/events',
html_url: 'https://github.com/iptv-org/database/issues/5701',
id: 1929459171,
node_id: 'I_kwDOG1Kwp85zATXj',
number: 5701,
title: 'Edit: M5',
user: {
login: 'freearhey',
id: 7253922,
node_id: 'MDQ6VXNlcjcyNTM5MjI=',
avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/freearhey',
html_url: 'https://github.com/freearhey',
followers_url: 'https://api.github.com/users/freearhey/followers',
following_url: 'https://api.github.com/users/freearhey/following{/other_user}',
gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}',
starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions',
organizations_url: 'https://api.github.com/users/freearhey/orgs',
repos_url: 'https://api.github.com/users/freearhey/repos',
events_url: 'https://api.github.com/users/freearhey/events{/privacy}',
received_events_url: 'https://api.github.com/users/freearhey/received_events',
type: 'User',
site_admin: false
},
labels: [
{
id: 5303574335,
node_id: 'LA_kwDOG1Kwp88AAAABPB4fPw',
url: 'https://api.github.com/repos/iptv-org/database/labels/channels:edit',
name: 'channels:edit',
color: 'E12977',
default: false,
description: 'Request to edit channel description'
},
{
id: 5366738347,
node_id: 'LA_kwDOG1Kwp88AAAABP-Htqw',
url: 'https://api.github.com/repos/iptv-org/database/labels/approved',
name: 'approved',
color: '85DDDE',
default: false,
description: ''
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2023-10-06T05:25:44Z',
updated_at: '2023-10-06T05:25:44Z',
closed_at: null,
author_association: 'CONTRIBUTOR',
active_lock_reason: null,
body: '### Channel ID (required)\n\nBeijingSatelliteTV.cn\n\n### Channel Name\n\nbeIN Movies Turk\n\n### Alternative Names\n\nbeIN Movies Türk\n\n### Network\n\nBBC\n\n### Owners\n\nGazprom Media\n\n### Country\n\nTR\n\n### Subdivision\n\nUS-CA\n\n### City\n\nLondon\n\n### Broadcast Area\n\nc/TR\n\n### Languages\n\ntur\n\n### Categories\n\nmovies\n\n### NSFW\nTRUE\n\n### Launched\n\n1979-05-16\n\n### Closed\n\n1980-05-16\n\n### Replaced By\n\nM5.hu\n\n### Website\n\nhttp://www.digiturk.com.tr/\n\n### Logo\n\nhttps://i.imgur.com/nw8Sa2z.png\n\n### Notes\n\n_No response_',
reactions: {
url: 'https://api.github.com/repos/iptv-org/database/issues/5701/reactions',
total_count: 0,
'+1': 0,
'-1': 0,
laugh: 0,
hooray: 0,
confused: 0,
heart: 0,
rocket: 0,
eyes: 0
},
timeline_url: 'https://api.github.com/repos/iptv-org/database/issues/5701/timeline',
performed_via_github_app: null,
state_reason: null
}
]

View file

@ -1,81 +0,0 @@
module.exports = [
{
url: 'https://api.github.com/repos/iptv-org/database/issues/5871',
repository_url: 'https://api.github.com/repos/iptv-org/database',
labels_url: 'https://api.github.com/repos/iptv-org/database/issues/5871/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/database/issues/5871/comments',
events_url: 'https://api.github.com/repos/iptv-org/database/issues/5871/events',
html_url: 'https://github.com/iptv-org/database/issues/5871',
id: 1929261634,
node_id: 'I_kwDOG1Kwp85y_jJC',
number: 5871,
title: 'Remove: 002 Radio TV',
user: {
login: 'freearhey',
id: 7253922,
node_id: 'MDQ6VXNlcjcyNTM5MjI=',
avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/freearhey',
html_url: 'https://github.com/freearhey',
followers_url: 'https://api.github.com/users/freearhey/followers',
following_url: 'https://api.github.com/users/freearhey/following{/other_user}',
gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}',
starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions',
organizations_url: 'https://api.github.com/users/freearhey/orgs',
repos_url: 'https://api.github.com/users/freearhey/repos',
events_url: 'https://api.github.com/users/freearhey/events{/privacy}',
received_events_url: 'https://api.github.com/users/freearhey/received_events',
type: 'User',
site_admin: false
},
labels: [
{
id: 5366738347,
node_id: 'LA_kwDOG1Kwp88AAAABP-Htqw',
url: 'https://api.github.com/repos/iptv-org/database/labels/approved',
name: 'approved',
color: '85DDDE',
default: false,
description: ''
},
{
id: 6049155772,
node_id: 'LA_kwDOG1Kwp88AAAABaI7KvA',
url: 'https://api.github.com/repos/iptv-org/database/labels/blocklist:add',
name: 'channels:remove',
color: 'e99695',
default: false,
description: 'Request to remove a channel'
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2023-10-06T00:35:32Z',
updated_at: '2023-10-06T00:35:32Z',
closed_at: null,
author_association: 'CONTRIBUTOR',
active_lock_reason: null,
body: '### Channel ID\n\n002RadioTV.do\n\n### Reason\n\nOther\n\n### Notes (optional)\n\n_No response_',
reactions: {
url: 'https://api.github.com/repos/iptv-org/database/issues/5871/reactions',
total_count: 0,
'+1': 0,
'-1': 0,
laugh: 0,
hooray: 0,
confused: 0,
heart: 0,
rocket: 0,
eyes: 0
},
timeline_url: 'https://api.github.com/repos/iptv-org/database/issues/5871/timeline',
performed_via_github_app: null,
state_reason: null
}
]

View file

@ -0,0 +1,3 @@
channel,reason,ref
AnimalPlanetAfrica.za,dmca,https://github.com/iptv-org/iptv/issues/1831
BeijingSatelliteTV.cn,dmca,https://github.com/iptv-org/iptv/issues/1831
1 channel reason ref
2 AnimalPlanetAfrica.za dmca https://github.com/iptv-org/iptv/issues/1831
3 BeijingSatelliteTV.cn dmca https://github.com/iptv-org/iptv/issues/1831

View file

@ -0,0 +1,6 @@
id,name,alt_names,network,owners,country,subdivision,city,broadcast_area,languages,categories,is_nsfw,launched,closed,replaced_by,website,logo
002RadioTV.do,002 Radio TV,,,,DO,,,c/DO,spa,general,FALSE,,,,https://www.002radio.com/,https://i.imgur.com/7oNe8xj.png
0TV.dk,0-TV,,,,DK,,København,c/DK,dan,general,FALSE,,,01TV.fr@SD,https://0-tv.dk/,https://i.imgur.com/aR5q6mA.png
1000xHoraTV.uy,1000xHora TV,,,,UY,,Montevideo,c/UY,spa,auto,FALSE,,,M5.hu@SD,https://www.1000xhoratv.com/,https://i.imgur.com/wP3bbYr.png
BeijingSatelliteTV.cn,Beijing Satellite TV,北京卫视,,,CN,,Beijing,c/CN,zho,general,FALSE,,,002RadioTV.do@SD,https://www.brtn.cn/btv/,https://i.imgur.com/vsktAez.png
M5.hu,M5,,,,HU,,,c/HU,hun,auto,TRUE,,2021-01-01,002RadioTV.do@SD,https://www.mediaklikk.hu/m5/,https://i.imgur.com/y21wFd0.png
1 id name alt_names network owners country subdivision city broadcast_area languages categories is_nsfw launched closed replaced_by website logo
2 002RadioTV.do 002 Radio TV DO c/DO spa general FALSE https://www.002radio.com/ https://i.imgur.com/7oNe8xj.png
3 0TV.dk 0-TV DK København c/DK dan general FALSE 01TV.fr@SD https://0-tv.dk/ https://i.imgur.com/aR5q6mA.png
4 1000xHoraTV.uy 1000xHora TV UY Montevideo c/UY spa auto FALSE M5.hu@SD https://www.1000xhoratv.com/ https://i.imgur.com/wP3bbYr.png
5 BeijingSatelliteTV.cn Beijing Satellite TV 北京卫视 CN Beijing c/CN zho general FALSE 002RadioTV.do@SD https://www.brtn.cn/btv/ https://i.imgur.com/vsktAez.png
6 M5.hu M5 HU c/HU hun auto TRUE 2021-01-01 002RadioTV.do@SD https://www.mediaklikk.hu/m5/ https://i.imgur.com/y21wFd0.png

View file

@ -0,0 +1,8 @@
channel,id,name,is_main,broadcast_area,timezones,languages,video_format
002RadioTV.do,SD,SD,TRUE,c/DO,America/Santo_Domingo,spa,480i
01TV.fr,SD,SD,TRUE,c/FR,Europe/Paris,fra,576i
0TV.dk,SD,SD,TRUE,c/DK,Europe/Copenhagen,dan,576i
1000xHoraTV.uy,SD,SD,TRUE,c/UY,America/Montevideo,spa,576i
BeijingSatelliteTV.cn,SD,SD,TRUE,c/DO,America/Santo_Domingo,spa,480i
M5.hu,SD,SD,FALSE,c/DO,America/Santo_Domingo,spa,480i
M5.hu,West,West,TRUE,c/DO,America/Santo_Domingo,spa,480i
1 channel id name is_main broadcast_area timezones languages video_format
2 002RadioTV.do SD SD TRUE c/DO America/Santo_Domingo spa 480i
3 01TV.fr SD SD TRUE c/FR Europe/Paris fra 576i
4 0TV.dk SD SD TRUE c/DK Europe/Copenhagen dan 576i
5 1000xHoraTV.uy SD SD TRUE c/UY America/Montevideo spa 576i
6 BeijingSatelliteTV.cn SD SD TRUE c/DO America/Santo_Domingo spa 480i
7 M5.hu SD SD FALSE c/DO America/Santo_Domingo spa 480i
8 M5.hu West West TRUE c/DO America/Santo_Domingo spa 480i

View file

@ -0,0 +1,2 @@
id,utc_offset,countries
Africa/Abidjan,+00:00,CI;BF;GH;GM;GN;IS;ML;MR;SH;SL;SN;TG
1 id utc_offset countries
2 Africa/Abidjan +00:00 CI;BF;GH;GM;GN;IS;ML;MR;SH;SL;SN;TG

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
channel,reason,ref
002RadioTV.do,dmca,eee
002RadioTV.do,dmca,eee
1 channel reason ref
2 002RadioTV.do dmca eee
3 002RadioTV.do dmca eee

View file

@ -0,0 +1,2 @@
id,name,alt_names,network,owners,country,subdivision,city,broadcast_area,languages,categories,is_nsfw,launched,closed,replaced_by,website,logo
002RadioTV.do,002 Radio TV,,,,DO,,,c/DO,,,FALSE,,,,,https://i.imgur.com/7oNe8xj.png
1 id name alt_names network owners country subdivision city broadcast_area languages categories is_nsfw launched closed replaced_by website logo
2 002RadioTV.do 002 Radio TV DO c/DO FALSE https://i.imgur.com/7oNe8xj.png

View file

@ -0,0 +1,3 @@
name,code,languages,flag
Andorra,AD,cat,🇦🇩
Dominican Republic,DO,spa,🇩🇴
1 name code languages flag
2 Andorra AD cat 🇦🇩
3 Dominican Republic DO spa 🇩🇴

View file

@ -0,0 +1,3 @@
channel,id,name,is_main,broadcast_area,timezones,languages,video_format
002RadioTV.do,SD,SD,TRUE,c/DO,America/Santo_Domingo,spa,480i
002RadioTV.do,SD,HD,FALSE,c/DO,America/Santo_Domingo,spa,1080i
1 channel id name is_main broadcast_area timezones languages video_format
2 002RadioTV.do SD SD TRUE c/DO America/Santo_Domingo spa 480i
3 002RadioTV.do SD HD FALSE c/DO America/Santo_Domingo spa 1080i

View file

@ -0,0 +1,3 @@
code,name
cat,Catalan
spa,Spanish
1 code name
2 cat Catalan
3 spa Spanish

View file

@ -0,0 +1,2 @@
country,name,code
AD,Andorra la Vella,AD-07
1 country name code
2 AD Andorra la Vella AD-07

View file

@ -0,0 +1,2 @@
id,utc_offset,countries
America/Santo_Domingo,-04:00,DO
1 id utc_offset countries
2 America/Santo_Domingo -04:00 DO

View file

@ -1,2 +1,2 @@
channel,ref
aaa.us,https://github.com/iptv-org/iptv/issues/1831
channel,reason,ref
aaa.us,dmca,https://github.com/iptv-org/iptv/issues/1831
1 channel reason ref
2 aaa.us dmca https://github.com/iptv-org/iptv/issues/1831

View file

@ -1,2 +1,4 @@
id,name,alt_names,network,owners,country,subdivision,city,broadcast_area,languages,categories,is_nsfw,launched,closed,replaced_by,website,logo
002RadioTV.do,002 Radio TV,,,,DO,,,c/DO,spa,,FALSE,,,,ttps://www.002radio.com/,https://i.imgur.com/7oNe8xj.png
002RadioTV.do,002 Radio TV,,,,DO,,,c/DO,spa,,FALSE,,,002RadioTV.do@4K,ttps://www.002radio.com/,https://i.imgur.com/7oNe8xj.png
10Channel.do,10 Channel,,,,DO,,,c/DO,spa,,FALSE,,,,,https://i.ibb.co/0XMM4gn/download-7.png
24B.do,24B,,,,DO,,,c/DO,spa,,FALSE,,,,,https://i.imgur.com/8LgdPst.png
1 id name alt_names network owners country subdivision city broadcast_area languages categories is_nsfw launched closed replaced_by website logo
2 002RadioTV.do 002 Radio TV DO c/DO spa FALSE 002RadioTV.do@4K ttps://www.002radio.com/ https://i.imgur.com/7oNe8xj.png
3 10Channel.do 10 Channel DO c/DO spa FALSE https://i.ibb.co/0XMM4gn/download-7.png
4 24B.do 24B DO c/DO spa FALSE https://i.imgur.com/8LgdPst.png

View file

@ -0,0 +1,5 @@
channel,id,name,is_main,broadcast_area,timezones,languages,video_format
0TV.dk,SD,SD,TRUE,c/DK,Europe/Copenhagen,dan,576I
002RadioTV.do,SD,SD,TRUE,c/DK,Africa/Accra,dan,576i
002RadioTV.do,HD,HD,TRUE,c/DK,Africa/Accra,dan,576i
24B.do,SD,SD,FALSE,c/DK,Africa/Accra,dan,576i
1 channel id name is_main broadcast_area timezones languages video_format
2 0TV.dk SD SD TRUE c/DK Europe/Copenhagen dan 576I
3 002RadioTV.do SD SD TRUE c/DK Africa/Accra dan 576i
4 002RadioTV.do HD HD TRUE c/DK Africa/Accra dan 576i
5 24B.do SD SD FALSE c/DK Africa/Accra dan 576i

View file

@ -0,0 +1,2 @@
id,utc_offset,countries
Africa/Accra,+00:00,GH
1 id utc_offset countries
2 Africa/Accra +00:00 GH

View file

@ -0,0 +1,5 @@
channel,id,name,is_main,broadcast_area,timezones,languages,video_format
KSTVKids.ua,HD,HD,TRUE,c/UA,America/Santo_Domingo,ukr,480i
PeoplesWeather.do,SD,SD,TRUE,c/DO,America/Santo_Domingo,spa,480i
PeoplesWeather.do,HD,HD,FALSE,c/DO,America/Santo_Domingo,spa,1080i
PeoplesWeather.do,West,West,FALSE,c/DO,America/Santo_Domingo,spa,1080i
1 channel id name is_main broadcast_area timezones languages video_format
2 KSTVKids.ua HD HD TRUE c/UA America/Santo_Domingo ukr 480i
3 PeoplesWeather.do SD SD TRUE c/DO America/Santo_Domingo spa 480i
4 PeoplesWeather.do HD HD FALSE c/DO America/Santo_Domingo spa 1080i
5 PeoplesWeather.do West West FALSE c/DO America/Santo_Domingo spa 1080i

View file

@ -0,0 +1,2 @@
id,utc_offset,countries
America/Santo_Domingo,-04:00,DO
1 id utc_offset countries
2 America/Santo_Domingo -04:00 DO

View file

@ -7,7 +7,7 @@ beforeEach(() => {
it('can export data as json', () => {
execSync(
'DATA_DIR=tests/__data__/input/data API_DIR=tests/__data__/output/api npm run db:export',
'DATA_DIR=tests/__data__/input/export/data API_DIR=tests/__data__/output/api npm run db:export',
{
encoding: 'utf8'
}
@ -15,6 +15,8 @@ it('can export data as json', () => {
expect(content('output/api/blocklist.json')).toEqual(content('expected/api/blocklist.json'))
expect(content('output/api/channels.json')).toEqual(content('expected/api/channels.json'))
expect(content('output/api/timezones.json')).toEqual(content('expected/api/timezones.json'))
expect(content('output/api/feeds.json')).toEqual(content('expected/api/feeds.json'))
})
function content(filepath: string) {

View file

@ -3,7 +3,7 @@ import * as fs from 'fs-extra'
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
fs.copySync('tests/__data__/input/data', 'tests/__data__/output/data')
fs.copySync('tests/__data__/input/update/data', 'tests/__data__/output/data')
})
it('can update db with data from issues', () => {
@ -13,8 +13,9 @@ it('can update db with data from issues', () => {
expect(content('output/data/blocklist.csv')).toEqual(content('expected/data/blocklist.csv'))
expect(content('output/data/channels.csv')).toEqual(content('expected/data/channels.csv'))
expect(content('output/data/feeds.csv')).toEqual(content('expected/data/feeds.csv'))
expect(stdout).toEqual(
'OUTPUT=closes #5871, closes #5901, closes #5701, closes #5900, closes #5899, closes #5898, closes #5897, closes #5891'
'OUTPUT=closes #6871, closes #5871, closes #7901, closes #5901, closes #5902, closes #5903, closes #5701, closes #8900, closes #5900, closes #5899, closes #5898, closes #5897, closes #5891'
)
})

View file

@ -43,10 +43,16 @@ describe('db:validate', () => {
} catch (error) {
expect((error as ExecError).status).toBe(1)
expect((error as ExecError).stdout).toContain('entry with the id "aaa" already exists')
expect((error as ExecError).stdout).toContain(
'entry with the channel "002RadioTV.do" and ref "eee" already exists'
)
expect((error as ExecError).stdout).toContain(
'entry with the channel "002RadioTV.do" and id "SD" already exists'
)
}
})
it('shows an error if an invalid value is specified', () => {
it('shows an error if the data contains an error', () => {
try {
execSync('DATA_DIR=tests/__data__/input/validate/invalid_value npm run db:validate', {
encoding: 'utf8'
@ -54,13 +60,28 @@ describe('db:validate', () => {
process.exit(1)
} catch (error) {
expect((error as ExecError).status).toBe(1)
expect((error as ExecError).stdout).toContain('"aaa.us" is missing in the channels.csv')
expect((error as ExecError).stdout).toContain(
'2 "aaa.us" is missing in the channels.csv'
'"002RadioTV.do" has an invalid replaced_by "002RadioTV.do@4K"'
)
expect((error as ExecError).stdout).toContain(
'2 002RadioTV.do: "website" must be a valid uri with a scheme matching the http|https pattern'
'"10Channel.do" channel does not have a main feed'
)
expect((error as ExecError).stdout).toContain('2 error(s)')
expect((error as ExecError).stdout).toContain('"24B.do" channel does not have a main feed')
expect((error as ExecError).stdout).toContain(
'002RadioTV.do: "website" must be a valid uri with a scheme matching the http|https pattern'
)
expect((error as ExecError).stdout).toContain(
'entry with the channel "002RadioTV.do" and is_main "true" already exists'
)
expect((error as ExecError).stdout).toContain('"0TV.dk" is missing in the channels.csv')
expect((error as ExecError).stdout).toContain(
'"0TV.dk@SD" has the wrong timezone "Europe/Copenhagen"'
)
expect((error as ExecError).stdout).toContain(
'SD: "video_format" with value "576I" fails to match the required pattern'
)
expect((error as ExecError).stdout).toContain('9 error(s)')
}
})

985
yarn.lock

File diff suppressed because it is too large Load diff