mirror of
https://github.com/iptv-org/database.git
synced 2025-05-09 19:20:01 -04:00
Merge branch 'master' into pr/17426
This commit is contained in:
commit
c80ff4ae3c
89 changed files with 55424 additions and 15011 deletions
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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?'
|
|
@ -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'
|
||||
|
|
@ -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
92
.github/ISSUE_TEMPLATE/4_feeds_add.yml
vendored
Normal 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
88
.github/ISSUE_TEMPLATE/5_feeds_edit.yml
vendored
Normal 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?'
|
38
.github/ISSUE_TEMPLATE/6_feeds_remove.yml
vendored
Normal file
38
.github/ISSUE_TEMPLATE/6_feeds_remove.yml
vendored
Normal 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?'
|
|
@ -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?'
|
|
@ -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
|
16
.github/workflows/check.yml
vendored
16
.github/workflows/check.yml
vendored
|
@ -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'
|
||||
|
|
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
|
@ -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
|
||||
|
|
2
.github/workflows/update.yml
vendored
2
.github/workflows/update.yml
vendored
|
@ -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
1
.husky/pre-commit
Normal file
|
@ -0,0 +1 @@
|
|||
npm run db:validate
|
115
CONTRIBUTING.md
115
CONTRIBUTING.md
|
@ -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` |
|
||||
| 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` |
|
||||
| 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). | Required | `https://example.com/logo.png` |
|
||||
| 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
|
||||
|
||||
|
@ -56,8 +116,8 @@
|
|||
### subdivisions
|
||||
|
||||
| Field | Description | Required | Example |
|
||||
| ------- | ------------------------------------------------------------------------------ | -------- | ------------------ |
|
||||
| country | Country code of the division | Required | `CA` |
|
||||
| ------- | ------------------------------------------------------------------------------------------ | -------- | ------------------ |
|
||||
| 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` |
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
|
2452
data/blocklist.csv
2452
data/blocklist.csv
File diff suppressed because it is too large
Load diff
20402
data/channels.csv
20402
data/channels.csv
File diff suppressed because it is too large
Load diff
|
@ -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,🇦🇽
|
|
39576
data/feeds.csv
Normal file
39576
data/feeds.csv
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
|
417
data/timezones.csv
Normal file
417
data/timezones.csv
Normal 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
|
|
51
eslint.config.mjs
Normal file
51
eslint.config.mjs
Normal 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']
|
||||
}
|
||||
}
|
||||
]
|
3261
package-lock.json
generated
3261
package-lock.json
generated
File diff suppressed because it is too large
Load diff
24
package.json
24
package.json
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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, '')
|
||||
}
|
|
@ -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'
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
55
scripts/models/feed.ts
Normal 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]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
export * from './channel'
|
||||
export * from './blocked'
|
||||
export * from './feed'
|
||||
|
|
|
@ -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()
|
||||
}
|
11
scripts/schemes/blocklist.ts
Normal file
11
scripts/schemes/blocklist.ts
Normal 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()
|
||||
}
|
|
@ -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()
|
||||
}
|
10
scripts/schemes/categories.ts
Normal file
10
scripts/schemes/categories.ts
Normal 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()
|
||||
}
|
|
@ -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 })
|
|
@ -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()
|
||||
}
|
18
scripts/schemes/countries.ts
Normal file
18
scripts/schemes/countries.ts
Normal 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
36
scripts/schemes/feeds.ts
Normal 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)
|
||||
}
|
|
@ -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
21
scripts/schemes/index.ts
Normal 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
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
const Joi = require('joi')
|
||||
|
||||
module.exports = {
|
||||
code: Joi.string()
|
||||
.regex(/^[a-z]{3}$/)
|
||||
.required(),
|
||||
name: Joi.string().required()
|
||||
}
|
8
scripts/schemes/languages.ts
Normal file
8
scripts/schemes/languages.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import Joi from 'joi'
|
||||
|
||||
export default {
|
||||
code: Joi.string()
|
||||
.regex(/^[a-z]{3}$/)
|
||||
.required(),
|
||||
name: Joi.string().required()
|
||||
}
|
|
@ -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(),
|
|
@ -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()
|
||||
}
|
11
scripts/schemes/subdivisions.ts
Normal file
11
scripts/schemes/subdivisions.ts
Normal 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()
|
||||
}
|
11
scripts/schemes/timezones.ts
Normal file
11
scripts/schemes/timezones.ts
Normal 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
24
scripts/utils.ts
Normal 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, '')
|
||||
}
|
|
@ -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"}]
|
|
@ -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"}]
|
1
tests/__data__/expected/api/feeds.json
Normal file
1
tests/__data__/expected/api/feeds.json
Normal 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"}]
|
1
tests/__data__/expected/api/timezones.json
Normal file
1
tests/__data__/expected/api/timezones.json
Normal file
|
@ -0,0 +1 @@
|
|||
[{"id":"Africa/Abidjan","utc_offset":"+00:00","countries":["CI","BF","GH","GM","GN","IS","ML","MR","SH","SL","SN","TG"]}]
|
|
@ -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,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
|
|
10
tests/__data__/expected/data/feeds.csv
Normal file
10
tests/__data__/expected/data/feeds.csv
Normal 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,2 +0,0 @@
|
|||
channel,ref
|
||||
AnimalPlanetAfrica.za,https://github.com/iptv-org/iptv/issues/1831
|
|
3
tests/__data__/input/export/data/blocklist.csv
Normal file
3
tests/__data__/input/export/data/blocklist.csv
Normal 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,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
|
|
3
tests/__data__/input/export/data/feeds.csv
Normal file
3
tests/__data__/input/export/data/feeds.csv
Normal 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
|
|
2
tests/__data__/input/export/data/timezones.csv
Normal file
2
tests/__data__/input/export/data/timezones.csv
Normal 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,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
|
||||
}
|
||||
]
|
|
@ -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
|
||||
}
|
||||
]
|
|
@ -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
|
||||
}
|
||||
]
|
|
@ -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
|
||||
}
|
||||
]
|
|
@ -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
|
||||
}
|
||||
]
|
3
tests/__data__/input/update/data/blocklist.csv
Normal file
3
tests/__data__/input/update/data/blocklist.csv
Normal 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
|
|
6
tests/__data__/input/update/data/channels.csv
Normal file
6
tests/__data__/input/update/data/channels.csv
Normal 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
|
|
8
tests/__data__/input/update/data/feeds.csv
Normal file
8
tests/__data__/input/update/data/feeds.csv
Normal 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
|
|
2
tests/__data__/input/update/data/timezones.csv
Normal file
2
tests/__data__/input/update/data/timezones.csv
Normal 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
|
|
1029
tests/__data__/input/update/issues.js
Normal file
1029
tests/__data__/input/update/issues.js
Normal file
File diff suppressed because it is too large
Load diff
3
tests/__data__/input/validate/duplicate/blocklist.csv
Normal file
3
tests/__data__/input/validate/duplicate/blocklist.csv
Normal file
|
@ -0,0 +1,3 @@
|
|||
channel,reason,ref
|
||||
002RadioTV.do,dmca,eee
|
||||
002RadioTV.do,dmca,eee
|
|
2
tests/__data__/input/validate/duplicate/channels.csv
Normal file
2
tests/__data__/input/validate/duplicate/channels.csv
Normal 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
|
|
3
tests/__data__/input/validate/duplicate/countries.csv
Normal file
3
tests/__data__/input/validate/duplicate/countries.csv
Normal file
|
@ -0,0 +1,3 @@
|
|||
name,code,languages,flag
|
||||
Andorra,AD,cat,🇦🇩
|
||||
Dominican Republic,DO,spa,🇩🇴
|
|
3
tests/__data__/input/validate/duplicate/feeds.csv
Normal file
3
tests/__data__/input/validate/duplicate/feeds.csv
Normal 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
|
|
3
tests/__data__/input/validate/duplicate/languages.csv
Normal file
3
tests/__data__/input/validate/duplicate/languages.csv
Normal file
|
@ -0,0 +1,3 @@
|
|||
code,name
|
||||
cat,Catalan
|
||||
spa,Spanish
|
|
2
tests/__data__/input/validate/duplicate/subdivisions.csv
Normal file
2
tests/__data__/input/validate/duplicate/subdivisions.csv
Normal file
|
@ -0,0 +1,2 @@
|
|||
country,name,code
|
||||
AD,Andorra la Vella,AD-07
|
|
2
tests/__data__/input/validate/duplicate/timezones.csv
Normal file
2
tests/__data__/input/validate/duplicate/timezones.csv
Normal file
|
@ -0,0 +1,2 @@
|
|||
id,utc_offset,countries
|
||||
America/Santo_Domingo,-04:00,DO
|
|
|
@ -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,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
|
|
5
tests/__data__/input/validate/invalid_value/feeds.csv
Normal file
5
tests/__data__/input/validate/invalid_value/feeds.csv
Normal 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
|
|
|
@ -0,0 +1,2 @@
|
|||
id,utc_offset,countries
|
||||
Africa/Accra,+00:00,GH
|
|
5
tests/__data__/input/validate/valid_data/feeds.csv
Normal file
5
tests/__data__/input/validate/valid_data/feeds.csv
Normal 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
|
|
2
tests/__data__/input/validate/valid_data/timezones.csv
Normal file
2
tests/__data__/input/validate/valid_data/timezones.csv
Normal file
|
@ -0,0 +1,2 @@
|
|||
id,utc_offset,countries
|
||||
America/Santo_Domingo,-04:00,DO
|
|
|
@ -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) {
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -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)')
|
||||
}
|
||||
})
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue