Merge pull request #25158 from iptv-org/patch-2025.07.2

Patch 2025.07.2
This commit is contained in:
Alstruit 2025-07-29 14:29:08 -05:00 committed by GitHub
commit e8cf7f2482
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
97 changed files with 8809 additions and 8341 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text eol=crlf

1
.readme/.gitignore vendored
View File

@ -2,3 +2,4 @@ _categories.md
_countries.md
_languages.md
_regions.md
_subdivisions.md

View File

@ -1,31 +0,0 @@
# Supported Categories
| Category | Definition |
| ------------- | --------------------------------------------------------------------- |
| Auto | Programming related to cars, motorcycles, and other automobiles |
| Animation | Programming is mostly 2D or 3D animation |
| Business | Programming related to business |
| Classic | Programming is mostly from earlier decades |
| Comedy | Programming is mostly comedy |
| Cooking | Programs related to cooking or food in general |
| Culture | Programming is mostly about art and culture |
| Documentary | Programming that depicts a person or real-world event |
| Education | Programming is intended to be educational |
| Entertainment | Channels with a variety of entertainment programs |
| Family | Programming that is be suitable for all members of a family |
| General | Provides a variety of different programming |
| Kids | Programming targeted to children |
| Legislative | Programming specific to the operation of government |
| Lifestyle | Programs related to health, fitness, leisure, fashion, decor, etc. |
| Movies | Channels that only show movies |
| Music | Programming is music or music related |
| News | Programming is mostly news |
| Outdoor | Programming related to outdoor activities like fishing, hunting, etc. |
| Relax | Programming is calm sounding and beautiful views |
| Religious | Religious programming |
| Science | Science and Technology |
| Series | Channels that only show series |
| Shop | Programming is for shopping |
| Sports | Programming is sports |
| Travel | Programming is travel related |
| Weather | Programming is focused on weather |

View File

@ -36,7 +36,7 @@ https://iptv-org.github.io/iptv/index.m3u
### Grouped by category
Playlists in which channels are grouped by category. A list of all supported categories with descriptions can be found [here](.readme/supported-categories.md).
Playlists in which channels are grouped by category.
<details>
<summary>Expand</summary>
@ -91,6 +91,19 @@ Same thing, but split up into separate files:
</details>
### Grouped by subdivision
Playlists in which channels are grouped by subdivision for which they are broadcasted.
<details>
<summary>Expand</summary>
<br>
<!-- prettier-ignore -->
#include "./.readme/_subdivisions.md"
</details>
### Grouped by region
Playlists in which channels are grouped by the region for which they are broadcasted.

View File

@ -31,7 +31,7 @@ Note all links in playlists are sorted automatically by scripts so there is no n
### How to fix the stream description?
Most of the stream description (channel name, categories, languages, broadcast area, logo) we load from the [iptv-org/database](https://github.com/iptv-org/database) using the stream ID.
Most of the stream description (channel name, feed name, categories, languages, broadcast area, logo) we load from the [iptv-org/database](https://github.com/iptv-org/database) using the stream ID.
So first of all, make sure that the desired stream has the correct ID. A full list of all supported channels and their corresponding IDs can be found on [iptv-org.github.io](https://iptv-org.github.io/). To change the stream ID of any link in the playlist, just fill out this [form](https://github.com/iptv-org/iptv/issues/new?assignees=&labels=streams%3Aedit&projects=&template=2_streams_edit.yml&title=Edit%3A+).
@ -110,14 +110,14 @@ Please note that we only accept removal requests from channel owners and their o
For a stream to be approved, its description must follow this template:
```
#EXTINF:-1 tvg-id="STREAM_ID",CHANNEL_NAME (QUALITY) [LABEL]
#EXTINF:-1 tvg-id="STREAM_ID",STREAM_TITLE (QUALITY) [LABEL]
STREAM_URL
```
| Attribute | Description | Required | Valid values |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------- |
| `STREAM_ID` | Stream ID consisting of channel ID and feed ID. Full list of supported channels with corresponding ID could be found on [iptv-org.github.io](https://iptv-org.github.io/). | Optional | `<channel_id>` or `<channel_id>@<feed_id>` |
| `CHANNEL_NAME` | Full name of the channel. May contain any characters except: `,`, `[`, `]`. | Required | - |
| `STREAM_TITLE` | Stream title consisting of channel name and feed name. May contain any characters except: `,`, `[`, `]`. | Required | - |
| `QUALITY` | Maximum stream quality. | Optional | `2160p`, `1080p`, `720p`, `480p`, `360p` etc |
| `LABEL` | Specified in cases where the broadcast for some reason may not be available to some users. | Optional | `Geo-blocked` or `Not 24/7` |
| `STREAM_URL` | Stream URL. | Required | - |
@ -125,7 +125,7 @@ STREAM_URL
Example:
```xml
#EXTINF:-1 tvg-id="ExampleTV.ua@HD",Example TV (720p) [Not 24/7]
#EXTINF:-1 tvg-id="ExampleTV.us@East",Example TV East (720p) [Not 24/7]
https://example.com/playlist.m3u8
```
@ -165,7 +165,6 @@ http://example.com/stream.m3u8
- `.readme/`
- `config.json`: config for the `markdown-include` package, which is used to compile everything into one `README.md` file.
- `preview.png`: image displayed in the `README.md`.
- `supported-categories.md`: list of supported categories.
- `template.md`: template for `README.md`.
- `scripts/`: contains all scripts used in the repository.
- `streams/`: contains all streams broken down by the country from which they are broadcast.

View File

@ -1,46 +1,54 @@
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";
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 __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"),
...compat.extends('eslint:recommended', 'plugin:@typescript-eslint/recommended'),
{
plugins: {
"@typescript-eslint": typescriptEslint,
'@typescript-eslint': typescriptEslint
},
languageOptions: {
globals: {
...globals.browser,
...globals.browser
},
parser: tsParser,
ecmaVersion: "latest",
sourceType: "module",
ecmaVersion: 'latest',
sourceType: 'module'
},
rules: {
"no-case-declarations": "off",
'no-case-declarations': 'off',
indent: ["error", 2, {
SwitchCase: 1,
}],
indent: [
'error',
2,
{
SwitchCase: 1
}
],
"linebreak-style": ["error", "unix"],
quotes: ["error", "single"],
semi: ["error", "never"],
'linebreak-style': ['error', 'windows'],
quotes: ['error', 'single'],
semi: ['error', 'never']
}
},
},
];
{
ignores: ['tests/__data__/**']
}
]

98
package-lock.json generated
View File

@ -928,9 +928,9 @@
}
},
"node_modules/@eslint/config-array": {
"version": "0.20.0",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
"integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
"integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
"dependencies": {
"@eslint/object-schema": "^2.1.6",
"debug": "^4.3.1",
@ -941,17 +941,17 @@
}
},
"node_modules/@eslint/config-helpers": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz",
"integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==",
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
"integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
@ -993,11 +993,14 @@
}
},
"node_modules/@eslint/js": {
"version": "9.25.1",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz",
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==",
"version": "9.32.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz",
"integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://eslint.org/donate"
}
},
"node_modules/@eslint/object-schema": {
@ -1009,11 +1012,11 @@
}
},
"node_modules/@eslint/plugin-kit": {
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz",
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==",
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
"integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
"dependencies": {
"@eslint/core": "^0.13.0",
"@eslint/core": "^0.15.1",
"levn": "^0.4.1"
},
"engines": {
@ -2816,9 +2819,9 @@
}
},
"node_modules/acorn": {
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"bin": {
"acorn": "bin/acorn"
},
@ -3651,18 +3654,18 @@
}
},
"node_modules/eslint": {
"version": "9.25.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz",
"integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==",
"version": "9.32.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz",
"integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.20.0",
"@eslint/config-helpers": "^0.2.1",
"@eslint/core": "^0.13.0",
"@eslint/config-array": "^0.21.0",
"@eslint/config-helpers": "^0.3.0",
"@eslint/core": "^0.15.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.25.1",
"@eslint/plugin-kit": "^0.2.8",
"@eslint/js": "9.32.0",
"@eslint/plugin-kit": "^0.3.4",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
@ -3673,9 +3676,9 @@
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.3.0",
"eslint-visitor-keys": "^4.2.0",
"espree": "^10.3.0",
"eslint-scope": "^8.4.0",
"eslint-visitor-keys": "^4.2.1",
"espree": "^10.4.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@ -3710,9 +3713,9 @@
}
},
"node_modules/eslint-scope": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz",
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==",
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@ -3736,9 +3739,9 @@
}
},
"node_modules/eslint/node_modules/eslint-visitor-keys": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@ -3747,13 +3750,13 @@
}
},
"node_modules/espree": {
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"dependencies": {
"acorn": "^8.14.0",
"acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.2.0"
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@ -3763,9 +3766,9 @@
}
},
"node_modules/espree/node_modules/eslint-visitor-keys": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@ -4047,13 +4050,14 @@
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {

View File

@ -21,7 +21,7 @@
"format": "npm run playlist:format",
"update": "npm run playlist:generate && npm run api:generate && npm run readme:update",
"deploy": "npm run playlist:deploy && npm run api:deploy",
"lint": "npx eslint ./scripts/**/*.ts ./tests/**/*.ts",
"lint": "npx eslint \"scripts/**/*.{ts,js}\" \"tests/**/*.{ts,js}\"",
"test": "jest --runInBand",
"postinstall": "npm run api:load"
},

View File

@ -73,7 +73,7 @@ export default async function main(filepath: string) {
logger.info('creating search index...')
const items = channels.map((channel: Channel) => channel.getSearchable()).all()
const searchIndex = sjs.createIndex(items, {
searchable: ['name', 'altNames', 'guideNames', 'streamNames', 'feedFullNames']
searchable: ['name', 'altNames', 'guideNames', 'streamTitles', 'feedFullNames']
})
logger.info('starting...\n')
@ -100,7 +100,7 @@ async function selectChannel(
feedsGroupedByChannelId: Dictionary,
channelsKeyById: Dictionary
): Promise<string> {
const query = escapeRegex(stream.getName())
const query = escapeRegex(stream.getTitle())
const similarChannels = searchIndex
.search(query)
.map((item: ChannelSearchableData) => channelsKeyById.get(item.id))
@ -108,7 +108,7 @@ async function selectChannel(
const url = stream.url.length > 50 ? stream.url.slice(0, 50) + '...' : stream.url
const selected: ChoiceValue = await select({
message: `Select channel ID for "${stream.name}" (${url}):`,
message: `Select channel ID for "${stream.title}" (${url}):`,
choices: getChannelChoises(new Collection(similarChannels)),
pageSize: 10
})
@ -136,7 +136,7 @@ async function selectChannel(
}
async function selectFeed(channelId: string, feedsGroupedByChannelId: Dictionary): Promise<string> {
const channelFeeds = new Collection(feedsGroupedByChannelId.get(channelId)) || new Collection()
const channelFeeds = new Collection(feedsGroupedByChannelId.get(channelId))
const choices = getFeedChoises(channelFeeds)
const selected: ChoiceValue = await select({

View File

@ -53,7 +53,7 @@ async function main() {
logger.info('sorting links...')
streams = streams.orderBy(
[
(stream: Stream) => stream.name,
(stream: Stream) => stream.title,
(stream: Stream) => stream.getVerticalResolution(),
(stream: Stream) => stream.getLabel(),
(stream: Stream) => stream.url
@ -63,7 +63,7 @@ async function main() {
logger.info('saving...')
const groupedStreams = streams.groupBy((stream: Stream) => stream.getFilepath())
for (let filepath of groupedStreams.keys()) {
for (const filepath of groupedStreams.keys()) {
const streams = groupedStreams.get(filepath) || []
if (!streams.length) return

View File

@ -9,13 +9,14 @@ import {
IndexCategoryGenerator,
IndexLanguageGenerator,
IndexCountryGenerator,
SubdivisionsGenerator,
IndexRegionGenerator,
CategoriesGenerator,
CountriesGenerator,
LanguagesGenerator,
RegionsGenerator,
IndexGenerator,
SourcesGenerator,
IndexGenerator,
RawGenerator
} from '../../generators'
@ -32,6 +33,7 @@ async function main() {
feedsGroupedByChannelId,
logosGroupedByStreamId,
channelsKeyById,
subdivisions,
categories,
countries,
regions
@ -71,6 +73,9 @@ async function main() {
logger.info('generating categories/...')
await new CategoriesGenerator({ categories, streams, logFile }).generate()
logger.info('generating languages/...')
await new LanguagesGenerator({ streams, logFile }).generate()
logger.info('generating countries/...')
await new CountriesGenerator({
countries,
@ -78,8 +83,12 @@ async function main() {
logFile
}).generate()
logger.info('generating languages/...')
await new LanguagesGenerator({ streams, logFile }).generate()
logger.info('generating subdivisions/...')
await new SubdivisionsGenerator({
subdivisions,
streams,
logFile
}).generate()
logger.info('generating regions/...')
await new RegionsGenerator({

View File

@ -18,7 +18,7 @@ const LIVE_UPDATE_MAX_STREAMS = 100
let errors = 0
let warnings = 0
let results = {}
const results = {}
let interval
let streams = new Collection()
let isLiveUpdateEnabled = true

View File

@ -4,9 +4,9 @@ import type { DataProcessorData } from '../../types/dataProcessor'
import { Stream, Playlist, Channel, Issue } from '../../models'
import type { DataLoaderData } from '../../types/dataLoader'
import { DATA_DIR, STREAMS_DIR } from '../../constants'
import validUrl from 'valid-url'
import { isURI } from '../../utils'
let processedIssues = new Collection()
const processedIssues = new Collection()
async function main() {
const logger = new Logger({ level: -999 })
@ -55,7 +55,7 @@ async function main() {
logger.info('saving...')
const groupedStreams = streams.groupBy((stream: Stream) => stream.getFilepath())
for (let filepath of groupedStreams.keys()) {
for (const filepath of groupedStreams.keys()) {
let streams = groupedStreams.get(filepath) || []
streams = streams.filter((stream: Stream) => stream.removed === false)
@ -114,7 +114,7 @@ async function editStreams({
if (data.missing('streamUrl')) return
let stream: Stream = streams.first(
const stream: Stream = streams.first(
(_stream: Stream) => _stream.url === data.getString('streamUrl')
)
if (!stream) return
@ -129,7 +129,7 @@ async function editStreams({
.withChannel(channelsKeyById)
.withFeed(feedsGroupedByChannelId)
.updateId()
.updateName()
.updateTitle()
.updateFilepath()
}
@ -158,7 +158,7 @@ async function addStreams({
if (data.missing('streamId') || data.missing('streamUrl')) return
if (streams.includes((_stream: Stream) => _stream.url === data.getString('streamUrl'))) return
const streamUrl = data.getString('streamUrl') || ''
if (!isUri(streamUrl)) return
if (!isURI(streamUrl)) return
const streamId = data.getString('streamId') || ''
const [channelId, feedId] = streamId.split('@')
@ -173,11 +173,11 @@ async function addStreams({
const directives = data.getArray('directives') || []
const stream = new Stream({
channel: channelId,
feed: feedId,
name: data.getString('channelName') || channel.name,
channelId,
feedId,
title: channel.name,
url: streamUrl,
user_agent: httpUserAgent,
userAgent: httpUserAgent,
referrer: httpReferrer,
directives,
quality,
@ -185,14 +185,10 @@ async function addStreams({
})
.withChannel(channelsKeyById)
.withFeed(feedsGroupedByChannelId)
.updateName()
.updateTitle()
.updateFilepath()
streams.add(stream)
processedIssues.add(issue.number)
})
}
function isUri(string: string) {
return validUrl.isUri(encodeURI(string))
}

View File

@ -44,7 +44,7 @@ async function main() {
let errors = new Collection()
let warnings = new Collection()
let streamsGroupedByFilepath = streams.groupBy((stream: Stream) => stream.getFilepath())
const streamsGroupedByFilepath = streams.groupBy((stream: Stream) => stream.getFilepath())
for (const filepath of streamsGroupedByFilepath.keys()) {
const streams = streamsGroupedByFilepath.get(filepath)
if (!streams) continue

View File

@ -1,5 +1,11 @@
import { Logger } from '@freearhey/core'
import { CategoryTable, CountryTable, LanguageTable, RegionTable } from '../../tables'
import {
CategoryTable,
CountryTable,
LanguageTable,
RegionTable,
SubdivisionTable
} from '../../tables'
import { Markdown } from '../../core'
import { README_DIR } from '../../constants'
import path from 'path'
@ -9,10 +15,12 @@ async function main() {
logger.info('creating category table...')
await new CategoryTable().make()
logger.info('creating country table...')
await new CountryTable().make()
logger.info('creating language table...')
await new LanguageTable().make()
logger.info('creating country table...')
await new CountryTable().make()
logger.info('creating subdivision table...')
await new SubdivisionTable().make()
logger.info('creating region table...')
await new RegionTable().make()

View File

@ -4,6 +4,7 @@ import { DataProcessorData } from '../../types/dataProcessor'
import { DATA_DIR, STREAMS_DIR } from '../../constants'
import { DataLoaderData } from '../../types/dataLoader'
import { Issue, Stream } from '../../models'
import { isURI } from '../../utils'
async function main() {
const logger = new Logger()
@ -44,7 +45,7 @@ async function main() {
issue.labels.find((label: string) => label === 'streams:remove')
)
removeRequests.forEach((issue: Issue) => {
const streamUrls = issue.data.has('streamUrl') ? issue.data.getArray('streamUrl') : []
const streamUrls = issue.data.getArray('streamUrl') || []
if (!streamUrls.length) {
const result = {
@ -93,6 +94,7 @@ async function main() {
if (!channelId) result.status = 'missing_id'
else if (!streamUrl) result.status = 'missing_link'
else if (!isURI(streamUrl)) result.status = 'invalid_link'
else if (blocklistRecordsGroupedByChannelId.has(channelId)) result.status = 'blocked'
else if (channelsKeyById.missing(channelId)) result.status = 'wrong_id'
else if (streamsGroupedByUrl.has(streamUrl)) result.status = 'on_playlist'

View File

@ -25,7 +25,7 @@ export class DataProcessor {
const languages = new Collection(data.languages).map(data => new Language(data))
const languagesKeyByCode = languages.keyBy((language: Language) => language.code)
const subdivisions = new Collection(data.subdivisions).map(data => new Subdivision(data))
let subdivisions = new Collection(data.subdivisions).map(data => new Subdivision(data))
const subdivisionsKeyByCode = subdivisions.keyBy((subdivision: Subdivision) => subdivision.code)
const subdivisionsGroupedByCountryCode = subdivisions.groupBy(
(subdivision: Subdivision) => subdivision.countryCode
@ -42,6 +42,10 @@ export class DataProcessor {
)
const countriesKeyByCode = countries.keyBy((country: Country) => country.code)
subdivisions = subdivisions.map((subdivision: Subdivision) =>
subdivision.withCountry(countriesKeyByCode)
)
const timezones = new Collection(data.timezones).map(data =>
new Timezone(data).withCountries(countriesKeyByCode)
)
@ -74,7 +78,7 @@ export class DataProcessor {
const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => feed.channelId)
const feedsGroupedById = feeds.groupBy((feed: Feed) => feed.id)
let logos = new Collection(data.logos).map(data => new Logo(data).withFeed(feedsGroupedById))
const logos = new Collection(data.logos).map(data => new Logo(data).withFeed(feedsGroupedById))
const logosGroupedByChannelId = logos.groupBy((logo: Logo) => logo.channelId)
const logosGroupedByStreamId = logos.groupBy((logo: Logo) => logo.getStreamId())

View File

@ -23,6 +23,7 @@ export class IssueLoader {
repo: REPO,
per_page: 100,
labels,
status: 'open',
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}

View File

@ -10,7 +10,6 @@ const FIELDS = new Dictionary({
'New Stream URL': 'newStreamUrl',
Label: 'label',
Quality: 'quality',
'Channel Name': 'channelName',
'HTTP User-Agent': 'httpUserAgent',
'HTTP User Agent': 'httpUserAgent',
'HTTP Referrer': 'httpReferrer',

View File

@ -1,4 +1,4 @@
import { Country, Subdivision, Stream, Playlist } from '../models'
import { Country, Stream, Playlist } from '../models'
import { Collection, Storage, File } from '@freearhey/core'
import { PUBLIC_DIR } from '../constants'
import { Generator } from './generator'
@ -40,21 +40,6 @@ export class CountriesGenerator implements Generator {
this.logFile.append(
JSON.stringify({ type: 'country', filepath, count: playlist.streams.count() }) + EOL
)
country.getSubdivisions().forEach(async (subdivision: Subdivision) => {
const subdivisionStreams = streams.filter((stream: Stream) =>
stream.isBroadcastInSubdivision(subdivision)
)
if (subdivisionStreams.isEmpty()) return
const playlist = new Playlist(subdivisionStreams, { public: true })
const filepath = `subdivisions/${subdivision.code.toLowerCase()}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logFile.append(
JSON.stringify({ type: 'subdivision', filepath, count: playlist.streams.count() }) + EOL
)
})
})
const undefinedStreams = streams.filter((stream: Stream) => !stream.hasBroadcastArea())

View File

@ -10,3 +10,4 @@ export * from './languagesGenerator'
export * from './rawGenerator'
export * from './regionsGenerator'
export * from './sourcesGenerator'
export * from './subdivisionsGenerator'

View File

@ -23,7 +23,7 @@ export class RawGenerator implements Generator {
async generate() {
const files = this.streams.groupBy((stream: Stream) => stream.getFilename())
for (let filename of files.keys()) {
for (const filename of files.keys()) {
const streams = new Collection(files.get(filename)).map((stream: Stream) => {
const groupTitle = stream.getCategoryNames().join(';')
if (groupTitle) stream.groupTitle = groupTitle

View File

@ -23,7 +23,7 @@ export class SourcesGenerator implements Generator {
async generate() {
const files: Dictionary = this.streams.groupBy((stream: Stream) => stream.getFilename())
for (let filename of files.keys()) {
for (const filename of files.keys()) {
if (!filename) continue
let streams = new Collection(files.get(filename))

View File

@ -0,0 +1,46 @@
import { Subdivision, Stream, Playlist } from '../models'
import { Collection, Storage, File } from '@freearhey/core'
import { PUBLIC_DIR } from '../constants'
import { Generator } from './generator'
import { EOL } from 'node:os'
type SubdivisionsGeneratorProps = {
streams: Collection
subdivisions: Collection
logFile: File
}
export class SubdivisionsGenerator implements Generator {
streams: Collection
subdivisions: Collection
storage: Storage
logFile: File
constructor({ streams, subdivisions, logFile }: SubdivisionsGeneratorProps) {
this.streams = streams.clone()
this.subdivisions = subdivisions
this.storage = new Storage(PUBLIC_DIR)
this.logFile = logFile
}
async generate(): Promise<void> {
const streams = this.streams
.orderBy((stream: Stream) => stream.getTitle())
.filter((stream: Stream) => stream.isSFW())
this.subdivisions.forEach(async (subdivision: Subdivision) => {
const subdivisionStreams = streams.filter((stream: Stream) =>
stream.isBroadcastInSubdivision(subdivision)
)
if (subdivisionStreams.isEmpty()) return
const playlist = new Playlist(subdivisionStreams, { public: true })
const filepath = `subdivisions/${subdivision.code.toLowerCase()}.m3u`
await this.storage.save(filepath, playlist.toString())
this.logFile.append(
JSON.stringify({ type: 'subdivision', filepath, count: playlist.streams.count() }) + EOL
)
})
}
}

View File

@ -133,9 +133,9 @@ export class Channel {
return streams
}
getStreamNames(): Collection {
getStreamTitles(): Collection {
return this.getStreams()
.map((stream: Stream) => stream.getName())
.map((stream: Stream) => stream.getTitle())
.uniq()
}
@ -184,7 +184,7 @@ export class Channel {
name: this.name,
altNames: this.altNames.all(),
guideNames: this.getGuideNames().all(),
streamNames: this.getStreamNames().all(),
streamTitles: this.getStreamTitles().all(),
feedFullNames: this.getFeedFullNames().all()
}
}

View File

@ -190,8 +190,15 @@ export class Feed {
isBroadcastInSubdivision(subdivision: Subdivision): boolean {
if (this.isInternational()) return false
if (this.broadcastSubdivisionCodes.includes(subdivision.code)) return true
if (
this.broadcastSubdivisionCodes.isEmpty() &&
subdivision.country &&
this.isBroadcastInCountry(subdivision.country)
)
return true
return this.broadcastSubdivisionCodes.includes(subdivision.code)
return false
}
isBroadcastInCountry(country: Country): boolean {

View File

@ -6,7 +6,7 @@ import { IssueData } from '../core'
import path from 'node:path'
export class Stream {
name?: string
title: string
url: string
id?: string
channelId?: string
@ -28,16 +28,17 @@ export class Stream {
constructor(data?: StreamData) {
if (!data) return
const id = data.channel && data.feed ? [data.channel, data.feed].join('@') : data.channel
const id =
data.channelId && data.feedId ? [data.channelId, data.feedId].join('@') : data.channelId
const { verticalResolution, isInterlaced } = parseQuality(data.quality)
this.id = id || undefined
this.channelId = data.channel || undefined
this.feedId = data.feed || undefined
this.name = data.name || undefined
this.channelId = data.channelId || undefined
this.feedId = data.feedId || undefined
this.title = data.title || ''
this.url = data.url
this.referrer = data.referrer || undefined
this.userAgent = data.user_agent || undefined
this.userAgent = data.userAgent || undefined
this.verticalResolution = verticalResolution || undefined
this.isInterlaced = isInterlaced || undefined
this.label = data.label || undefined
@ -65,21 +66,22 @@ export class Stream {
}
fromPlaylistItem(data: parser.PlaylistItem): this {
function parseTitle(title: string): {
name: string
function parseName(name: string): {
title: string
label: string
quality: string
} {
let title = name
const [, label] = title.match(/ \[(.*)\]$/) || [null, '']
title = title.replace(new RegExp(` \\[${escapeRegExp(label)}\\]$`), '')
const [, quality] = title.match(/ \(([0-9]+p)\)$/) || [null, '']
title = title.replace(new RegExp(` \\(${quality}\\)$`), '')
return { name: title, label, quality }
return { title, label, quality }
}
function parseDirectives(string: string) {
let directives = new Collection()
const directives = new Collection()
if (!string) return directives
@ -100,7 +102,7 @@ export class Stream {
if (!data.url) throw new Error('"url" property is required')
const [channelId, feedId] = data.tvg.id.split('@')
const { name, label, quality } = parseTitle(data.name)
const { title, label, quality } = parseName(data.name)
const { verticalResolution, isInterlaced } = parseQuality(quality)
this.id = data.tvg.id || undefined
@ -108,7 +110,7 @@ export class Stream {
this.channelId = channelId || undefined
this.line = data.line
this.label = label || undefined
this.name = name
this.title = title
this.verticalResolution = verticalResolution || undefined
this.isInterlaced = isInterlaced || undefined
this.url = data.url
@ -241,12 +243,12 @@ export class Stream {
return parseInt(this.getQuality().replace(/p|i/, ''))
}
updateName(): this {
updateTitle(): this {
if (!this.channel) return this
this.name = this.channel.name
this.title = this.channel.name
if (this.feed && !this.feed.isMain) {
this.name += ` ${this.feed.name}`
this.title += ` ${this.feed.name}`
}
return this
@ -375,12 +377,12 @@ export class Stream {
return logo ? logo.url : ''
}
getName(): string {
return this.name || ''
getTitle(): string {
return this.title || ''
}
getTitle(): string {
let title = `${this.getName()}`
getFullTitle(): string {
let title = `${this.getTitle()}`
if (this.getQuality()) {
title += ` (${this.getQuality()})`
@ -405,6 +407,7 @@ export class Stream {
return {
channel: this.channelId || null,
feed: this.feedId || null,
title: this.title,
url: this.url,
referrer: this.referrer || null,
user_agent: this.userAgent || null,
@ -427,7 +430,7 @@ export class Stream {
output += ` http-user-agent="${this.userAgent}"`
}
output += `,${this.getTitle()}`
output += `,${this.getFullTitle()}`
this.directives.forEach((prop: string) => {
output += `\r\n${prop}`

View File

@ -1,6 +1,6 @@
import { Storage, Collection, File } from '@freearhey/core'
import { HTMLTable, LogParser, LogItem } from '../core'
import { Country, Subdivision } from '../models'
import { Country } from '../models'
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
import { Table } from './table'
@ -13,11 +13,6 @@ export class CountryTable implements Table {
const countriesContent = await dataStorage.json('countries.json')
const countries = new Collection(countriesContent).map(data => new Country(data))
const countriesGroupedByCode = countries.keyBy((country: Country) => country.code)
const subdivisionsContent = await dataStorage.json('subdivisions.json')
const subdivisions = new Collection(subdivisionsContent).map(data => new Subdivision(data))
const subdivisionsGroupedByCode = subdivisions.keyBy(
(subdivision: Subdivision) => subdivision.code
)
const parser = new LogParser()
const logsStorage = new Storage(LOGS_DIR)
@ -26,27 +21,6 @@ export class CountryTable implements Table {
let data = new Collection()
parsed
.filter((logItem: LogItem) => logItem.type === 'subdivision')
.forEach((logItem: LogItem) => {
const file = new File(logItem.filepath)
const code = file.name().toUpperCase()
const [countryCode, subdivisionCode] = code.split('-') || ['', '']
const country = countriesGroupedByCode.get(countryCode)
if (country && subdivisionCode) {
const subdivision = subdivisionsGroupedByCode.get(code)
if (subdivision) {
data.add([
`${country.name}/${subdivision.name}`,
`&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${subdivision.name}`,
logItem.count,
`<code>https://iptv-org.github.io/iptv/${logItem.filepath}</code>`
])
}
}
})
parsed
.filter((logItem: LogItem) => logItem.type === 'country')
.forEach((logItem: LogItem) => {

View File

@ -2,3 +2,4 @@ export * from './categoryTable'
export * from './countryTable'
export * from './languageTable'
export * from './regionTable'
export * from './subdivisionTable'

View File

@ -0,0 +1,72 @@
import { Storage, Collection, File } from '@freearhey/core'
import { HTMLTable, LogParser, LogItem } from '../core'
import { Country, Subdivision } from '../models'
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
import { Table } from './table'
export class SubdivisionTable implements Table {
constructor() {}
async make() {
const dataStorage = new Storage(DATA_DIR)
const countriesContent = await dataStorage.json('countries.json')
const countries = new Collection(countriesContent).map(data => new Country(data))
const countriesGroupedByCode = countries.keyBy((country: Country) => country.code)
const subdivisionsContent = await dataStorage.json('subdivisions.json')
const subdivisions = new Collection(subdivisionsContent).map(data => new Subdivision(data))
const subdivisionsGroupedByCode = subdivisions.keyBy(
(subdivision: Subdivision) => subdivision.code
)
const parser = new LogParser()
const logsStorage = new Storage(LOGS_DIR)
const generatorsLog = await logsStorage.load('generators.log')
const parsed = parser.parse(generatorsLog)
const parsedSubdivisions = parsed.filter((logItem: LogItem) => logItem.type === 'subdivision')
let output = ''
countries.forEach((country: Country) => {
const parsedCountrySubdivisions = parsedSubdivisions.filter((logItem: LogItem) =>
logItem.filepath.includes(`subdivisions/${country.code.toLowerCase()}`)
)
if (!parsedCountrySubdivisions.length) return
output += `\r\n<details>\r\n<summary>${country.name}</summary>\r\n`
const data = new Collection()
parsedCountrySubdivisions.forEach((logItem: LogItem) => {
const file = new File(logItem.filepath)
const code = file.name().toUpperCase()
const [countryCode, subdivisionCode] = code.split('-') || ['', '']
const country = countriesGroupedByCode.get(countryCode)
if (country && subdivisionCode) {
const subdivision = subdivisionsGroupedByCode.get(code)
if (subdivision) {
data.add([
subdivision.name,
logItem.count,
`<code>https://iptv-org.github.io/iptv/${logItem.filepath}</code>`
])
}
}
})
const table = new HTMLTable(data.all(), [
{ name: 'Subdivision' },
{ name: 'Channels', align: 'right' },
{ name: 'Playlist', nowrap: true }
])
output += table.toString()
output += '\r\n</details>'
})
const readmeStorage = new Storage(README_DIR)
await readmeStorage.save('_subdivisions.md', output.trim())
}
}

View File

@ -45,6 +45,6 @@ export type ChannelSearchableData = {
name: string
altNames: string[]
guideNames: string[]
streamNames: string[]
streamTitles: string[]
feedFullNames: string[]
}

View File

@ -1,10 +1,10 @@
export type StreamData = {
channel: string | null
feed: string | null
name: string | null
channelId: string | null
feedId: string | null
title: string | null
url: string
referrer: string | null
user_agent: string | null
userAgent: string | null
quality: string | null
label: string | null
directives: string[]

5
scripts/utils.ts Normal file
View File

@ -0,0 +1,5 @@
import validUrl from 'valid-url'
export function isURI(string: string) {
return validUrl.isUri(encodeURI(string))
}

View File

@ -2,6 +2,7 @@
{
"channel": null,
"feed": null,
"title": "Daawah TV",
"url": "http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8",
"referrer": null,
"user_agent": null
@ -9,6 +10,7 @@
{
"channel": null,
"feed": null,
"title": "Andorra TV",
"url": "http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8",
"referrer": "http://imn.iq",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
@ -16,12 +18,15 @@
{
"channel": "AndorraTV.ad",
"feed": "SD",
"title": "ATV",
"url": "https://iptv-all.lanesh4d0w.repl.co/andorra/atv|Referer=\"https://referer.xyz/\"|User-Agent=\"Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1\"|Origin=\"https://origin.xyz\"",
"referrer": null,
"user_agent": null
},
{
"channel": "BBCNews.uk",
"feed": null,
"title": "BBC News HD",
"url": "http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8",
"referrer": null,
"user_agent": null
@ -29,6 +34,7 @@
{
"channel": "LDPRTV.ru",
"feed": null,
"title": "ЛДПР ТВ",
"url": "http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8",
"referrer": null,
"user_agent": null
@ -36,6 +42,7 @@
{
"channel": "MeteoMedia.ca",
"feed": null,
"title": "Meteomedia",
"url": "http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8",
"referrer": null,
"user_agent": null
@ -43,6 +50,7 @@
{
"channel": "VisitXTV.nl",
"feed": null,
"title": "Visit-X TV",
"url": "https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8",
"referrer": null,
"user_agent": null
@ -50,6 +58,7 @@
{
"channel": "Zoo.ad",
"feed": null,
"title": "Zoo",
"url": "https://iptv-all.lanesh4d0w.repl.co/andorra/zoo",
"referrer": null,
"user_agent": null

View File

@ -1,4 +1,6 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7]
#EXTVLCOPT:http-referrer=http://imn.iq
#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
@ -15,5 +17,5 @@ https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd
http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Undefined",Duna World (576i)
http://146.59.85.40:89/dunaworld/index.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo

View File

@ -1,3 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8

View File

@ -11,5 +11,5 @@ http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://refe
https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV
http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo

View File

@ -9,6 +9,8 @@ http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7]
#EXTVLCOPT:http-referrer=http://imn.iq
#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
@ -25,5 +27,5 @@ https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd
http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Undefined",Duna World (576i)
http://146.59.85.40:89/dunaworld/index.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo

View File

@ -1,6 +1,8 @@
#EXTM3U
#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="https://i.imgur.com/BnhTn8i.png" group-title="Andorra",ATV
https://iptv-all.lanesh4d0w.repl.co/andorra/atv
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Canada",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Canada",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Kazakhstan",ЭлТР (480p) [Not 24/7]
@ -31,5 +33,5 @@ http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://refe
https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV
http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo

View File

@ -5,6 +5,8 @@ https://iptv-all.lanesh4d0w.repl.co/andorra/atv
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Russian",ЛДПР ТВ (1080p)
http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7]
#EXTVLCOPT:http-referrer=http://imn.iq
#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
@ -21,7 +23,7 @@ http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
http://146.59.85.40:89/dunaworld/index.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Undefined",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Undefined",ЭлТР (480p) [Not 24/7]
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8

View File

@ -1,4 +1,6 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7]
#EXTVLCOPT:http-referrer=http://imn.iq
#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
@ -19,7 +21,7 @@ http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
http://146.59.85.40:89/dunaworld/index.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p)
http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8

View File

@ -1,4 +1,6 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Americas",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Americas",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Asia",ЛДПР ТВ (1080p)
@ -23,8 +25,12 @@ https://iptv-all.lanesh4d0w.repl.co/andorra/atv
http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Europe, the Middle East and Africa",ЭлТР (480p) [Not 24/7]
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="North America",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="North America",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Northern America",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Northern America",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8
#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="International",BBC News HD
@ -43,5 +49,5 @@ http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://refe
https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV
http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo

View File

@ -1,4 +1,6 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7]
#EXTVLCOPT:http-referrer=http://imn.iq
#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
@ -15,7 +17,7 @@ http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
http://146.59.85.40:89/dunaworld/index.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="General",ЭлТР (480p) [Not 24/7]
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8

View File

@ -1,5 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo
#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="https://i.imgur.com/BnhTn8i.png" group-title="Undefined",ATV
https://iptv-all.lanesh4d0w.repl.co/andorra/atv

View File

@ -1,3 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8

View File

@ -1,3 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8

View File

@ -1,3 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8

View File

@ -1,3 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8

View File

@ -11,5 +11,5 @@ http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://refe
https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd
#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV
http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo

View File

@ -3,5 +3,5 @@
https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd
#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="https://i.imgur.com/BnhTn8i.png" group-title="Undefined",ATV
https://iptv-all.lanesh4d0w.repl.co/andorra/atv
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p)
#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo

View File

@ -1,3 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8

View File

@ -1,3 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8

View File

@ -1,88 +1,238 @@
{"type":"raw","filepath":"raw/ad.m3u","count":4}
{"type":"raw","filepath":"raw/ca.m3u","count":1}
{"type":"raw","filepath":"raw/ca.m3u","count":2}
{"type":"raw","filepath":"raw/in.m3u","count":1}
{"type":"raw","filepath":"raw/kg.m3u","count":1}
{"type":"raw","filepath":"raw/uk.m3u","count":1}
{"type":"raw","filepath":"raw/unsorted.m3u","count":4}
{"type":"source","filepath":"sources/ad.m3u","count":3}
{"type":"source","filepath":"sources/ca.m3u","count":1}
{"type":"source","filepath":"sources/in.m3u","count":1}
{"type":"source","filepath":"sources/kg.m3u","count":1}
{"type":"source","filepath":"sources/uk.m3u","count":1}
{"type":"source","filepath":"sources/unsorted.m3u","count":4}
{"type":"category","filepath":"categories/auto.m3u","count":0}
{"type":"category","filepath":"categories/cooking.m3u","count":0}
{"type":"category","filepath":"categories/comedy.m3u","count":0}
{"type":"category","filepath":"categories/documentary.m3u","count":0}
{"type":"category","filepath":"categories/business.m3u","count":0}
{"type":"category","filepath":"categories/classic.m3u","count":0}
{"type":"category","filepath":"categories/entertainment.m3u","count":0}
{"type":"category","filepath":"categories/education.m3u","count":0}
{"type":"category","filepath":"categories/cooking.m3u","count":0}
{"type":"category","filepath":"categories/animation.m3u","count":0}
{"type":"category","filepath":"categories/classic.m3u","count":0}
{"type":"category","filepath":"categories/documentary.m3u","count":0}
{"type":"category","filepath":"categories/family.m3u","count":0}
{"type":"category","filepath":"categories/kids.m3u","count":0}
{"type":"category","filepath":"categories/culture.m3u","count":0}
{"type":"category","filepath":"categories/lifestyle.m3u","count":0}
{"type":"category","filepath":"categories/education.m3u","count":0}
{"type":"category","filepath":"categories/general.m3u","count":3}
{"type":"category","filepath":"categories/outdoor.m3u","count":0}
{"type":"category","filepath":"categories/music.m3u","count":0}
{"type":"category","filepath":"categories/legislative.m3u","count":0}
{"type":"category","filepath":"categories/series.m3u","count":0}
{"type":"category","filepath":"categories/news.m3u","count":1}
{"type":"category","filepath":"categories/kids.m3u","count":0}
{"type":"category","filepath":"categories/lifestyle.m3u","count":0}
{"type":"category","filepath":"categories/movies.m3u","count":0}
{"type":"category","filepath":"categories/relax.m3u","count":0}
{"type":"category","filepath":"categories/religious.m3u","count":0}
{"type":"category","filepath":"categories/weather.m3u","count":1}
{"type":"category","filepath":"categories/music.m3u","count":0}
{"type":"category","filepath":"categories/outdoor.m3u","count":0}
{"type":"category","filepath":"categories/science.m3u","count":0}
{"type":"category","filepath":"categories/shop.m3u","count":0}
{"type":"category","filepath":"categories/xxx.m3u","count":1}
{"type":"category","filepath":"categories/news.m3u","count":1}
{"type":"category","filepath":"categories/religious.m3u","count":0}
{"type":"category","filepath":"categories/series.m3u","count":0}
{"type":"category","filepath":"categories/relax.m3u","count":0}
{"type":"category","filepath":"categories/sports.m3u","count":0}
{"type":"category","filepath":"categories/undefined.m3u","count":7}
{"type":"category","filepath":"categories/shop.m3u","count":0}
{"type":"category","filepath":"categories/entertainment.m3u","count":0}
{"type":"category","filepath":"categories/travel.m3u","count":0}
{"type":"category","filepath":"categories/undefined.m3u","count":6}
{"type":"country","filepath":"countries/ad.m3u","count":1}
{"type":"country","filepath":"countries/ca.m3u","count":1}
{"type":"country","filepath":"countries/kg.m3u","count":1}
{"type":"country","filepath":"countries/kz.m3u","count":1}
{"type":"country","filepath":"countries/tj.m3u","count":1}
{"type":"country","filepath":"countries/ru.m3u","count":1}
{"type":"country","filepath":"countries/tm.m3u","count":1}
{"type":"country","filepath":"countries/undefined.m3u","count":4}
{"type":"country","filepath":"countries/uz.m3u","count":1}
{"type":"category","filepath":"categories/xxx.m3u","count":1}
{"type":"category","filepath":"categories/legislative.m3u","count":0}
{"type":"category","filepath":"categories/weather.m3u","count":1}
{"type":"language","filepath":"languages/cat.m3u","count":1}
{"type":"language","filepath":"languages/rus.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-on.m3u","count":1}
{"type":"language","filepath":"languages/undefined.m3u","count":7}
{"type":"language","filepath":"languages/eng.m3u","count":1}
{"type":"language","filepath":"languages/undefined.m3u","count":8}
{"type":"country","filepath":"countries/ca.m3u","count":2}
{"type":"country","filepath":"countries/ad.m3u","count":1}
{"type":"country","filepath":"countries/ru.m3u","count":1}
{"type":"country","filepath":"countries/uz.m3u","count":1}
{"type":"country","filepath":"countries/kz.m3u","count":1}
{"type":"country","filepath":"countries/tj.m3u","count":1}
{"type":"country","filepath":"countries/tm.m3u","count":1}
{"type":"country","filepath":"countries/undefined.m3u","count":4}
{"type":"country","filepath":"countries/kg.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ad-07.m3u","count":1}
{"type":"region","filepath":"regions/afr.m3u","count":0}
{"type":"subdivision","filepath":"subdivisions/ad-02.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ad-04.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ad-08.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ad-03.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-ab.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ad-05.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-bc.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-nl.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ad-06.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-mb.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-nb.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-nt.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-nu.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-on.m3u","count":2}
{"type":"subdivision","filepath":"subdivisions/ca-ns.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-pe.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-qc.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-sk.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kg-j.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kg-b.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kg-t.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ca-yt.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kg-c.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kg-gb.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kg-n.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kg-y.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-ala.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kg-go.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-alm.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-akm.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-zap.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-aty.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-man.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-yuz.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-kus.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-akt.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-kar.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-kzy.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-ast.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-shy.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-pav.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-vos.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ad.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-sev.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-al.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ba.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-da.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-bu.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-in.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kk.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kl.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/kz-zha.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-me.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-se.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-mo.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ko.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ty.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kr.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-alt.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-amu.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-bel.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ast.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-sa.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-che.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-cu.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ta.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ce.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-bry.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-irk.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-chu.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kb.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-iva.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-klu.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kc.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ark.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kda.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kha.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kgd.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kya.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-khm.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kgn.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kem.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kir.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-mag.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kam.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-krs.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-len.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-kos.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-lip.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-mur.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-mow.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-niz.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-nen.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ngr.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ore.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-oms.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-orl.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-nvs.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-mos.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-psk.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ros.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-per.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-pnz.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-pri.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-sam.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-sak.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-rya.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-spe.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-sar.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-tam.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-tom.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-smo.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-sta.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-tul.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-sve.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-vla.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-tve.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-ud.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-vgg.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-vor.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-uly.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-zab.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-vlg.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-tyu.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tj-du.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-yan.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tj-kt.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tj-gb.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tj-ra.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tm-a.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-yev.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/ru-yar.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tj-su.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tm-b.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tm-l.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tm-m.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/tm-d.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-ji.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-nw.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-bu.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-qr.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-an.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-fa.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-qa.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-si.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-sa.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-tk.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-ng.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-xo.m3u","count":1}
{"type":"subdivision","filepath":"subdivisions/uz-su.m3u","count":1}
{"type":"region","filepath":"regions/apac.m3u","count":0}
{"type":"region","filepath":"regions/amer.m3u","count":1}
{"type":"region","filepath":"regions/arab.m3u","count":0}
{"type":"region","filepath":"regions/amer.m3u","count":2}
{"type":"region","filepath":"regions/asean.m3u","count":0}
{"type":"region","filepath":"regions/cenamer.m3u","count":0}
{"type":"region","filepath":"regions/asia.m3u","count":2}
{"type":"region","filepath":"regions/arab.m3u","count":0}
{"type":"region","filepath":"regions/carib.m3u","count":0}
{"type":"region","filepath":"regions/cis.m3u","count":2}
{"type":"region","filepath":"regions/hispam.m3u","count":0}
{"type":"region","filepath":"regions/emea.m3u","count":3}
{"type":"region","filepath":"regions/lac.m3u","count":0}
{"type":"region","filepath":"regions/cas.m3u","count":1}
{"type":"region","filepath":"regions/latam.m3u","count":0}
{"type":"region","filepath":"regions/cenamer.m3u","count":0}
{"type":"region","filepath":"regions/lac.m3u","count":0}
{"type":"region","filepath":"regions/emea.m3u","count":3}
{"type":"region","filepath":"regions/eur.m3u","count":3}
{"type":"region","filepath":"regions/nam.m3u","count":1}
{"type":"region","filepath":"regions/mena.m3u","count":0}
{"type":"region","filepath":"regions/noram.m3u","count":1}
{"type":"region","filepath":"regions/mideast.m3u","count":0}
{"type":"region","filepath":"regions/hispam.m3u","count":0}
{"type":"region","filepath":"regions/latam.m3u","count":0}
{"type":"region","filepath":"regions/maghreb.m3u","count":0}
{"type":"region","filepath":"regions/ssa.m3u","count":0}
{"type":"region","filepath":"regions/nord.m3u","count":0}
{"type":"region","filepath":"regions/asia.m3u","count":2}
{"type":"region","filepath":"regions/oce.m3u","count":0}
{"type":"region","filepath":"regions/southam.m3u","count":0}
{"type":"region","filepath":"regions/wafr.m3u","count":0}
{"type":"region","filepath":"regions/noram.m3u","count":2}
{"type":"region","filepath":"regions/nord.m3u","count":0}
{"type":"region","filepath":"regions/nam.m3u","count":2}
{"type":"region","filepath":"regions/int.m3u","count":2}
{"type":"region","filepath":"regions/southam.m3u","count":0}
{"type":"region","filepath":"regions/mideast.m3u","count":0}
{"type":"region","filepath":"regions/wafr.m3u","count":0}
{"type":"region","filepath":"regions/sas.m3u","count":0}
{"type":"region","filepath":"regions/ssa.m3u","count":0}
{"type":"region","filepath":"regions/undefined.m3u","count":4}
{"type":"index","filepath":"index.m3u","count":10}
{"type":"index","filepath":"index.category.m3u","count":11}
{"type":"index","filepath":"index.country.m3u","count":14}
{"type":"index","filepath":"index.language.m3u","count":10}
{"type":"index","filepath":"index.region.m3u","count":20}
{"type":"source","filepath":"sources/in.m3u","count":1}
{"type":"source","filepath":"sources/unsorted.m3u","count":4}
{"type":"source","filepath":"sources/ca.m3u","count":2}
{"type":"source","filepath":"sources/ad.m3u","count":3}
{"type":"source","filepath":"sources/uk.m3u","count":1}
{"type":"source","filepath":"sources/kg.m3u","count":1}
{"type":"index","filepath":"index.m3u","count":11}
{"type":"index","filepath":"index.category.m3u","count":12}
{"type":"index","filepath":"index.country.m3u","count":15}
{"type":"index","filepath":"index.language.m3u","count":11}
{"type":"index","filepath":"index.region.m3u","count":23}

View File

@ -145,7 +145,6 @@ Same thing, but split up into separate files:
<tbody>
<tr><td>🇨🇲 Cameroon</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/cm.m3u</code></td></tr>
<tr><td>🇨🇦 Canada</td><td align="right">2</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/ca.m3u</code></td></tr>
<tr><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ontario</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/subdivisions/ca-on.m3u</code></td></tr>
<tr><td>🇨🇻 Cape Verde</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/cv.m3u</code></td></tr>
<tr><td>🇨🇬 Republic of the Congo</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/cg.m3u</code></td></tr>
<tr><td>🇷🇪 Réunion</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/re.m3u</code></td></tr>
@ -161,6 +160,27 @@ Same thing, but split up into separate files:
</details>
### Grouped by subdivision
<details>
<summary>Expand</summary>
<br>
<!-- prettier-ignore -->
<details>
<summary>Canada</summary>
<table>
<thead>
<tr><th align="left">Subdivision</th><th align="left">Channels</th><th align="left">Playlist</th></tr>
</thead>
<tbody>
<tr><td>Ontario</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/subdivisions/ca-on.m3u</code></td></tr>
</tbody>
</table>
</details>
</details>
### Grouped by region
<details>

View File

@ -669,5 +669,15 @@
"city": null,
"categories": [],
"is_nsfw": false
},
{
"id": "5AABTV.ca",
"name": "5AAB TV",
"network": null,
"country": "CA",
"subdivision": null,
"city": null,
"categories": [],
"is_nsfw": false
}
]

View File

@ -826,5 +826,19 @@
],
"languages": [],
"video_format": "576i"
},
{
"channel": "5AABTV.ca",
"id": "SD",
"name": "SD",
"is_main": true,
"broadcast_area": [
"c/CA"
],
"timezones": [
"Asia/Bishkek"
],
"languages": [],
"video_format": "576i"
}
]

View File

@ -7,5 +7,6 @@
{"channel":"VisitXTV.nl","feed":null,"tags":[],"width":512,"height":512,"format":"JPEG","url":"https://i.imgur.com/RJ9wbNF.jpg"},
{"channel":"AndorraTV.ad","feed":"SD","tags":[],"width":512,"height":512,"format":"PNG","url":"https://i.imgur.com/BnhTn8i.png"},
{"channel":"AndorraTV.ad","feed":null,"tags":[],"width":1000,"height":1000,"format":"JPEG","url":"https://i.imgur.com/AnhTn8i.png"},
{"channel":"AndorraTV.ad","feed":null,"tags":[],"width":512,"height":512,"format":"PNG","url":"https://i.imgur.com/CnhTn8i.png"}
{"channel":"AndorraTV.ad","feed":null,"tags":[],"width":512,"height":512,"format":"PNG","url":"https://i.imgur.com/CnhTn8i.png"},
{"channel":"Zoo.ad","feed":null,"tags":[],"width":512,"height":512,"format":"PNG","url":"https://i.imgur.com/ciTJrnl.png"}
]

View File

@ -1682,7 +1682,7 @@ module.exports = [
closed_at: null,
author_association: 'COLLABORATOR',
active_lock_reason: null,
body: '### Stream URL\n\nhttps://livestream.telvue.com/templeuni1/f7b44cfafd5c52223d5498196c8a2e7b.sdp/playlist.m3u8\n\n### Stream ID\n\nboo.us\n\n### Channel Name\n\nBBC America\n\n### Quality\n\n720p\n\n### Label\n\nGeo-blocked\n\n### HTTP User-Agent\n\nMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 Edge/12.246\n\n### HTTP Referrer\n\n_No response_\n\n### Notes\n\n_No response_\n\n### Contributing Guide\n\n- [X] I have read [Contributing Guide](https://github.com/iptv-org/iptv/blob/master/CONTRIBUTING.md)',
body: '### Stream URL\n\nhttps://livestream.telvue.com/templeuni1/f7b44cfafd5c52223d5498196c8a2e7b.sdp/playlist.m3u8\n\n### Stream ID\n\nboo.us\n\n### Quality\n\n720p\n\n### Label\n\nGeo-blocked\n\n### HTTP User-Agent\n\nMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 Edge/12.246\n\n### HTTP Referrer\n\n_No response_\n\n### Notes\n\n_No response_\n\n### Contributing Guide\n\n- [X] I have read [Contributing Guide](https://github.com/iptv-org/iptv/blob/master/CONTRIBUTING.md)',
reactions: {
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14120/reactions',
total_count: 0,
@ -2284,5 +2284,114 @@ module.exports = [
timeline_url: 'https://api.github.com/repos/iptv-org/iptv/issues/20956/timeline',
performed_via_github_app: null,
state_reason: null
},
{
url: 'https://api.github.com/repos/iptv-org/iptv/issues/25157',
repository_url: 'https://api.github.com/repos/iptv-org/iptv',
labels_url: 'https://api.github.com/repos/iptv-org/iptv/issues/25157/labels{/name}',
comments_url: 'https://api.github.com/repos/iptv-org/iptv/issues/25157/comments',
events_url: 'https://api.github.com/repos/iptv-org/iptv/issues/25157/events',
html_url: 'https://github.com/iptv-org/iptv/issues/25157',
id: 3245640024,
node_id: 'I_kwDOCWUK8M7BdIlY',
number: 25157,
title: 'Add: OnTime Sports SD',
user: {
login: 'zezopm300',
id: 215159878,
node_id: 'U_kgDODNMURg',
avatar_url: 'https://avatars.githubusercontent.com/u/215159878?v=4',
gravatar_id: '',
url: 'https://api.github.com/users/zezopm300',
html_url: 'https://github.com/zezopm300',
followers_url: 'https://api.github.com/users/zezopm300/followers',
following_url: 'https://api.github.com/users/zezopm300/following{/other_user}',
gists_url: 'https://api.github.com/users/zezopm300/gists{/gist_id}',
starred_url: 'https://api.github.com/users/zezopm300/starred{/owner}{/repo}',
subscriptions_url: 'https://api.github.com/users/zezopm300/subscriptions',
organizations_url: 'https://api.github.com/users/zezopm300/orgs',
repos_url: 'https://api.github.com/users/zezopm300/repos',
events_url: 'https://api.github.com/users/zezopm300/events{/privacy}',
received_events_url: 'https://api.github.com/users/zezopm300/received_events',
type: 'User',
user_view_type: 'public',
site_admin: false
},
labels: [
{
id: 5923508587,
node_id: 'LA_kwDOCWUK8M8AAAABYRGRaw',
url: 'https://api.github.com/repos/iptv-org/iptv/labels/streams:add',
name: 'streams:add',
color: '017ff9',
default: false,
description: 'Request to add a new link to a playlist'
}
],
state: 'open',
locked: false,
assignee: null,
assignees: [],
milestone: null,
comments: 0,
created_at: '2025-07-19T20:44:05Z',
updated_at: '2025-07-19T20:44:05Z',
closed_at: null,
author_association: 'NONE',
type: null,
active_lock_reason: null,
sub_issues_summary: { total: 0, completed: 0, percent_completed: 0 },
body:
'### Stream ID (required)\n' +
'\n' +
'OnTimeSports.eg@SD\n' +
'\n' +
'### Stream URL (required)\n' +
'\n' +
' OnTime Sports SD.mu38\n' +
'\n' +
'### Quality\n' +
'\n' +
'None\n' +
'\n' +
'### Label\n' +
'\n' +
'None\n' +
'\n' +
'### HTTP User Agent\n' +
'\n' +
'_No response_\n' +
'\n' +
'### HTTP Referrer\n' +
'\n' +
'_No response_\n' +
'\n' +
'### Directives\n' +
'\n' +
'_No response_\n' +
'\n' +
'### Notes\n' +
'\n' +
'_No response_\n' +
'\n' +
'### Contributing Guide\n' +
'\n' +
'- [x] I have read [Contributing Guide](https://github.com/iptv-org/iptv/blob/master/CONTRIBUTING.md)',
closed_by: null,
reactions: {
url: 'https://api.github.com/repos/iptv-org/iptv/issues/25157/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/iptv/issues/25157/timeline',
performed_via_github_app: null,
state_reason: null
}
]

View File

@ -1,3 +1,5 @@
#EXTM3U
#EXTINF:-1 tvg-id="5AABTV.ca",5AAB TV
http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8
#EXTINF:-1 tvg-id="MeteoMedia.ca",Meteomedia
http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8

View File

@ -96,6 +96,17 @@ Same thing, but split up into separate files:
</details>
### Grouped by subdivision
<details>
<summary>Expand</summary>
<br>
<!-- prettier-ignore -->
#include "tests/__data__/output/.readme/_subdivisions.md"
</details>
### Grouped by region
<details>

View File

@ -15,18 +15,20 @@ describe('report:create', () => {
expect(
stdout.includes(`
(index) issueNumber type streamId streamUrl status
0 14120 'streams:edit' 'boo.us' 'https://livestream.telvue.com/templeuni1/f7b44cfafd5c52223d5498196c8a2e7b.sdp/playlist.m3u8' 'invalid_id'
1 14135 'streams:add' 'BBCWorldNews.uk@SouthAsia' 'http://103.199.161.254/Content/bbcworld/Live/Channel%28BBCworld%29/Stream%2801%29/index.m3u8' 'wrong_id'
2 14177 'streams:add' 'TUTV.us' 'https://livestream.telvue.com/templeuni1/f7b44cfafd5c52223d5498196c8a2e7b.sdp/playlist.m3u8' 'on_playlist'
3 14178 'streams:add' 'TV3.my' 'https://live-streams-ssai-01.tonton.com.my/live/2dd2b7cd-1b34-4871-b669-57b5c9beca23/live.isml/.m3u8...' 'blocked'
4 16120 'streams:remove' undefined 'http://190.61.102.67:2000/play/a038/index.m3u8' 'wrong_link'
5 19956 'channel search' 'CNBCe.tr' undefined 'invalid_id'
6 19957 'channel search' '13thStreet.au' undefined 'closed'
7 20956 'channel search' 'IONTV.us' undefined 'fulfilled'
`)
4 14179 'streams:add' 'ManoramaNews.in' '(https://mitelefe.com/Api/Videos/GetSourceUrl/694564/0/HLS / https://ssl.cloud.telefe.com/Api/Videos...' 'invalid_link'
5 16120 'streams:remove' undefined 'http://190.61.102.67:2000/play/a038/index.m3u8' 'wrong_link'
6 19956 'channel search' 'CNBCe.tr' undefined 'invalid_id'
7 19957 'channel search' '13thStreet.au' undefined 'closed'
8 20956 'channel search' 'IONTV.us' undefined 'fulfilled'
9 25157 'streams:add' 'OnTimeSports.eg@SD' 'OnTime Sports SD.mu38' 'invalid_link'
`)
).toBe(true)
})
})