mirror of
https://github.com/iptv-org/iptv
synced 2026-04-07 03:23:16 -04:00
Merge pull request #26208 from iptv-org/patch-2025.08.3
Patch 2025.08.3
This commit is contained in:
3
.readme/.gitignore
vendored
3
.readme/.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
_categories.md
|
||||
_countries.md
|
||||
_languages.md
|
||||
_regions.md
|
||||
_subdivisions.md
|
||||
_regions.md
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"build" : "PLAYLISTS.md",
|
||||
"files" : ["./.readme/template.md"]
|
||||
}
|
||||
@@ -40,16 +40,17 @@ Same thing, but split up into separate files:
|
||||
|
||||
</details>
|
||||
|
||||
### Grouped by country
|
||||
### Grouped by broadcast area
|
||||
|
||||
Playlists in which channels are grouped by country for which they are broadcasted.
|
||||
Playlists in which channels are grouped by broadcast area.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
#### Countries
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.country.m3u
|
||||
https://iptv-org.github.io/iptv/index.countries.m3u
|
||||
```
|
||||
|
||||
Same thing, but split up into separate files:
|
||||
@@ -57,31 +58,10 @@ Same thing, but split up into separate files:
|
||||
<!-- prettier-ignore -->
|
||||
#include "./.readme/_countries.md"
|
||||
|
||||
</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.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
#### Regions
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.region.m3u
|
||||
https://iptv-org.github.io/iptv/index.regions.m3u
|
||||
```
|
||||
|
||||
Same thing, but split up into separate files:
|
||||
|
||||
91
package-lock.json
generated
91
package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.32.0",
|
||||
"@freearhey/core": "^0.10.2",
|
||||
@@ -45,6 +46,11 @@
|
||||
"tsx": "^4.20.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@alex_neo/jest-expect-message": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@alex_neo/jest-expect-message/-/jest-expect-message-1.0.5.tgz",
|
||||
"integrity": "sha512-1eBykZCd0pPGl5qKtV6Z5ARA6yuhXzHsVN2h5GH5/H6svYa37Jr7vMio5OFpiw1LBHtscrZs7amSkZkcwm0cvQ=="
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
@@ -1254,14 +1260,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/editor": {
|
||||
"version": "4.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.15.tgz",
|
||||
"integrity": "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ==",
|
||||
"license": "MIT",
|
||||
"version": "4.2.17",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.17.tgz",
|
||||
"integrity": "sha512-r6bQLsyPSzbWrZZ9ufoWL+CztkSatnJ6uSxqd6N+o41EZC51sQeWOzI6s5jLb+xxTWxl7PlUppqm8/sow241gg==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^10.1.15",
|
||||
"@inquirer/type": "^3.0.8",
|
||||
"external-editor": "^3.1.0"
|
||||
"@inquirer/external-editor": "^1.0.1",
|
||||
"@inquirer/type": "^3.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -1297,6 +1302,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/external-editor": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz",
|
||||
"integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==",
|
||||
"dependencies": {
|
||||
"chardet": "^2.1.0",
|
||||
"iconv-lite": "^0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/figures": {
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz",
|
||||
@@ -3853,10 +3878,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chardet": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
|
||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
|
||||
"license": "MIT"
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz",
|
||||
"integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA=="
|
||||
},
|
||||
"node_modules/ci-info": {
|
||||
"version": "4.3.0",
|
||||
@@ -4547,20 +4571,6 @@
|
||||
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/external-editor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
|
||||
"integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chardet": "^0.7.0",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"tmp": "^0.0.33"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-content-type-parse": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
|
||||
@@ -5033,12 +5043,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"license": "MIT",
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -6848,15 +6857,6 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/outvariant": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz",
|
||||
@@ -7280,8 +7280,7 @@
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
@@ -7667,18 +7666,6 @@
|
||||
"resolved": "https://registry.npmjs.org/timer-node/-/timer-node-5.0.9.tgz",
|
||||
"integrity": "sha512-zXxCE/5/YDi0hY9pygqgRqjRbrFRzigYxOudG0I3syaqAAmX9/w9sxex1bNFCN6c1S66RwPtEIJv65dN+1psew=="
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"os-tmpdir": "~1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tmpl": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.32.0",
|
||||
"@freearhey/core": "^0.10.2",
|
||||
|
||||
@@ -18,7 +18,8 @@ async function main() {
|
||||
loader.download('logos.json'),
|
||||
loader.download('timezones.json'),
|
||||
loader.download('guides.json'),
|
||||
loader.download('streams.json')
|
||||
loader.download('streams.json'),
|
||||
loader.download('cities.json')
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
LanguagesGenerator,
|
||||
RegionsGenerator,
|
||||
SourcesGenerator,
|
||||
CitiesGenerator,
|
||||
IndexGenerator,
|
||||
RawGenerator
|
||||
} from '../../generators'
|
||||
@@ -36,7 +37,8 @@ async function main() {
|
||||
subdivisions,
|
||||
categories,
|
||||
countries,
|
||||
regions
|
||||
regions,
|
||||
cities
|
||||
}: DataProcessorData = processor.process(data)
|
||||
|
||||
logger.info('loading streams...')
|
||||
@@ -90,6 +92,13 @@ async function main() {
|
||||
logFile
|
||||
}).generate()
|
||||
|
||||
logger.info('generating cities/...')
|
||||
await new CitiesGenerator({
|
||||
cities,
|
||||
streams,
|
||||
logFile
|
||||
}).generate()
|
||||
|
||||
logger.info('generating regions/...')
|
||||
await new RegionsGenerator({
|
||||
streams,
|
||||
|
||||
@@ -1,32 +1,47 @@
|
||||
import { Logger } from '@freearhey/core'
|
||||
import {
|
||||
CategoryTable,
|
||||
CountryTable,
|
||||
LanguageTable,
|
||||
RegionTable,
|
||||
SubdivisionTable
|
||||
} from '../../tables'
|
||||
import { Markdown } from '../../core'
|
||||
import { README_DIR } from '../../constants'
|
||||
import path from 'path'
|
||||
import { CategoriesTable, CountriesTable, LanguagesTable, RegionsTable } from '../../tables'
|
||||
import { DataLoader, DataProcessor, Markdown } from '../../core'
|
||||
import { DataProcessorData } from '../../types/dataProcessor'
|
||||
import { DataLoaderData } from '../../types/dataLoader'
|
||||
import { README_DIR, DATA_DIR, ROOT_DIR } from '../../constants'
|
||||
import { Logger, Storage } from '@freearhey/core'
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger()
|
||||
const dataStorage = new Storage(DATA_DIR)
|
||||
const processor = new DataProcessor()
|
||||
const loader = new DataLoader({ storage: dataStorage })
|
||||
const data: DataLoaderData = await loader.load()
|
||||
const {
|
||||
subdivisionsKeyByCode,
|
||||
languagesKeyByCode,
|
||||
countriesKeyByCode,
|
||||
categoriesKeyById,
|
||||
subdivisions,
|
||||
countries,
|
||||
regions,
|
||||
cities
|
||||
}: DataProcessorData = processor.process(data)
|
||||
|
||||
logger.info('creating category table...')
|
||||
await new CategoryTable().make()
|
||||
await new CategoriesTable({ categoriesKeyById }).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()
|
||||
await new LanguagesTable({ languagesKeyByCode }).make()
|
||||
logger.info('creating countires table...')
|
||||
await new CountriesTable({
|
||||
countriesKeyByCode,
|
||||
subdivisionsKeyByCode,
|
||||
subdivisions,
|
||||
countries,
|
||||
cities
|
||||
}).make()
|
||||
logger.info('creating region table...')
|
||||
await new RegionTable().make()
|
||||
await new RegionsTable({ regions }).make()
|
||||
|
||||
logger.info('updating playlists.md...')
|
||||
const configPath = path.join(README_DIR, 'config.json')
|
||||
const playlists = new Markdown(configPath)
|
||||
const playlists = new Markdown({
|
||||
build: `${ROOT_DIR}/PLAYLISTS.md`,
|
||||
template: `${README_DIR}/template.md`
|
||||
})
|
||||
playlists.compile()
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ export class DataLoader {
|
||||
logos,
|
||||
timezones,
|
||||
guides,
|
||||
streams
|
||||
streams,
|
||||
cities
|
||||
] = await Promise.all([
|
||||
this.storage.json('countries.json'),
|
||||
this.storage.json('regions.json'),
|
||||
@@ -70,7 +71,8 @@ export class DataLoader {
|
||||
this.storage.json('logos.json'),
|
||||
this.storage.json('timezones.json'),
|
||||
this.storage.json('guides.json'),
|
||||
this.storage.json('streams.json')
|
||||
this.storage.json('streams.json'),
|
||||
this.storage.json('cities.json')
|
||||
])
|
||||
|
||||
return {
|
||||
@@ -85,7 +87,8 @@ export class DataLoader {
|
||||
logos,
|
||||
timezones,
|
||||
guides,
|
||||
streams
|
||||
streams,
|
||||
cities
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DataProcessorData } from '../types/dataProcessor'
|
||||
import { DataLoaderData } from '../types/dataLoader'
|
||||
import { Collection } from '@freearhey/core'
|
||||
import {
|
||||
@@ -11,14 +12,16 @@ import {
|
||||
Region,
|
||||
Stream,
|
||||
Guide,
|
||||
City,
|
||||
Feed,
|
||||
Logo
|
||||
} from '../models'
|
||||
|
||||
export class DataProcessor {
|
||||
constructor() {}
|
||||
process(data: DataLoaderData): DataProcessorData {
|
||||
let regions = new Collection(data.regions).map(data => new Region(data))
|
||||
let regionsKeyByCode = regions.keyBy((region: Region) => region.code)
|
||||
|
||||
process(data: DataLoaderData) {
|
||||
const categories = new Collection(data.categories).map(data => new Category(data))
|
||||
const categoriesKeyById = categories.keyBy((category: Category) => category.id)
|
||||
|
||||
@@ -26,25 +29,23 @@ export class DataProcessor {
|
||||
const languagesKeyByCode = languages.keyBy((language: Language) => language.code)
|
||||
|
||||
let subdivisions = new Collection(data.subdivisions).map(data => new Subdivision(data))
|
||||
const subdivisionsKeyByCode = subdivisions.keyBy((subdivision: Subdivision) => subdivision.code)
|
||||
const subdivisionsGroupedByCountryCode = subdivisions.groupBy(
|
||||
let subdivisionsKeyByCode = subdivisions.keyBy((subdivision: Subdivision) => subdivision.code)
|
||||
let subdivisionsGroupedByCountryCode = subdivisions.groupBy(
|
||||
(subdivision: Subdivision) => subdivision.countryCode
|
||||
)
|
||||
|
||||
let regions = new Collection(data.regions).map(data => new Region(data))
|
||||
const regionsKeyByCode = regions.keyBy((region: Region) => region.code)
|
||||
let countries = new Collection(data.countries).map(data => new Country(data))
|
||||
let countriesKeyByCode = countries.keyBy((country: Country) => country.code)
|
||||
|
||||
const countries = new Collection(data.countries).map(data =>
|
||||
new Country(data)
|
||||
const cities = new Collection(data.cities).map(data =>
|
||||
new City(data)
|
||||
.withRegions(regions)
|
||||
.withLanguage(languagesKeyByCode)
|
||||
.withSubdivisions(subdivisionsGroupedByCountryCode)
|
||||
)
|
||||
const countriesKeyByCode = countries.keyBy((country: Country) => country.code)
|
||||
|
||||
subdivisions = subdivisions.map((subdivision: Subdivision) =>
|
||||
subdivision.withCountry(countriesKeyByCode)
|
||||
.withCountry(countriesKeyByCode)
|
||||
.withSubdivision(subdivisionsKeyByCode)
|
||||
)
|
||||
const citiesKeyByCode = cities.keyBy((city: City) => city.code)
|
||||
const citiesGroupedByCountryCode = cities.groupBy((city: City) => city.countryCode)
|
||||
const citiesGroupedBySubdivisionCode = cities.groupBy((city: City) => city.subdivisionCode)
|
||||
|
||||
const timezones = new Collection(data.timezones).map(data =>
|
||||
new Timezone(data).withCountries(countriesKeyByCode)
|
||||
@@ -56,27 +57,12 @@ export class DataProcessor {
|
||||
(blocklistRecord: BlocklistRecord) => blocklistRecord.channelId
|
||||
)
|
||||
|
||||
let channels = new Collection(data.channels).map(data =>
|
||||
new Channel(data)
|
||||
.withCategories(categoriesKeyById)
|
||||
.withCountry(countriesKeyByCode)
|
||||
.withSubdivision(subdivisionsKeyByCode)
|
||||
.withCategories(categoriesKeyById)
|
||||
)
|
||||
let channels = new Collection(data.channels).map(data => new Channel(data))
|
||||
let channelsKeyById = channels.keyBy((channel: Channel) => channel.id)
|
||||
|
||||
const channelsKeyById = channels.keyBy((channel: Channel) => channel.id)
|
||||
|
||||
const feeds = new Collection(data.feeds).map(data =>
|
||||
new Feed(data)
|
||||
.withChannel(channelsKeyById)
|
||||
.withLanguages(languagesKeyByCode)
|
||||
.withTimezones(timezonesKeyById)
|
||||
.withBroadcastCountries(countriesKeyByCode, regionsKeyByCode, subdivisionsKeyByCode)
|
||||
.withBroadcastRegions(regions)
|
||||
.withBroadcastSubdivisions(subdivisionsKeyByCode)
|
||||
)
|
||||
const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => feed.channelId)
|
||||
const feedsGroupedById = feeds.groupBy((feed: Feed) => feed.id)
|
||||
let feeds = new Collection(data.feeds).map(data => new Feed(data))
|
||||
let feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => feed.channelId)
|
||||
let feedsGroupedById = feeds.groupBy((feed: Feed) => feed.id)
|
||||
|
||||
const logos = new Collection(data.logos).map(data => new Logo(data).withFeed(feedsGroupedById))
|
||||
const logosGroupedByChannelId = logos.groupBy((logo: Logo) => logo.channelId)
|
||||
@@ -90,11 +76,60 @@ export class DataProcessor {
|
||||
const guides = new Collection(data.guides).map(data => new Guide(data))
|
||||
const guidesGroupedByStreamId = guides.groupBy((guide: Guide) => guide.getStreamId())
|
||||
|
||||
regions = regions.map((region: Region) => region.withCountries(countriesKeyByCode))
|
||||
regions = regions.map((region: Region) =>
|
||||
region
|
||||
.withCountries(countriesKeyByCode)
|
||||
.withRegions(regions)
|
||||
.withSubdivisions(subdivisions)
|
||||
.withCities(cities)
|
||||
)
|
||||
regionsKeyByCode = regions.keyBy((region: Region) => region.code)
|
||||
|
||||
countries = countries.map((country: Country) =>
|
||||
country
|
||||
.withCities(citiesGroupedByCountryCode)
|
||||
.withSubdivisions(subdivisionsGroupedByCountryCode)
|
||||
.withRegions(regions)
|
||||
.withLanguage(languagesKeyByCode)
|
||||
)
|
||||
countriesKeyByCode = countries.keyBy((country: Country) => country.code)
|
||||
|
||||
subdivisions = subdivisions.map((subdivision: Subdivision) =>
|
||||
subdivision
|
||||
.withCities(citiesGroupedBySubdivisionCode)
|
||||
.withCountry(countriesKeyByCode)
|
||||
.withRegions(regions)
|
||||
)
|
||||
subdivisionsKeyByCode = subdivisions.keyBy((subdivision: Subdivision) => subdivision.code)
|
||||
subdivisionsGroupedByCountryCode = subdivisions.groupBy(
|
||||
(subdivision: Subdivision) => subdivision.countryCode
|
||||
)
|
||||
|
||||
channels = channels.map((channel: Channel) =>
|
||||
channel.withFeeds(feedsGroupedByChannelId).withLogos(logosGroupedByChannelId)
|
||||
channel
|
||||
.withFeeds(feedsGroupedByChannelId)
|
||||
.withLogos(logosGroupedByChannelId)
|
||||
.withCategories(categoriesKeyById)
|
||||
.withCountry(countriesKeyByCode)
|
||||
.withSubdivision(subdivisionsKeyByCode)
|
||||
.withCategories(categoriesKeyById)
|
||||
)
|
||||
channelsKeyById = channels.keyBy((channel: Channel) => channel.id)
|
||||
|
||||
feeds = feeds.map((feed: Feed) =>
|
||||
feed
|
||||
.withChannel(channelsKeyById)
|
||||
.withLanguages(languagesKeyByCode)
|
||||
.withTimezones(timezonesKeyById)
|
||||
.withBroadcastArea(
|
||||
citiesKeyByCode,
|
||||
subdivisionsKeyByCode,
|
||||
countriesKeyByCode,
|
||||
regionsKeyByCode
|
||||
)
|
||||
)
|
||||
feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => feed.channelId)
|
||||
feedsGroupedById = feeds.groupBy((feed: Feed) => feed.id)
|
||||
|
||||
return {
|
||||
blocklistRecordsGroupedByChannelId,
|
||||
@@ -111,6 +146,7 @@ export class DataProcessor {
|
||||
regionsKeyByCode,
|
||||
blocklistRecords,
|
||||
channelsKeyById,
|
||||
citiesKeyByCode,
|
||||
subdivisions,
|
||||
categories,
|
||||
countries,
|
||||
@@ -119,6 +155,7 @@ export class DataProcessor {
|
||||
channels,
|
||||
regions,
|
||||
streams,
|
||||
cities,
|
||||
guides,
|
||||
feeds,
|
||||
logos
|
||||
|
||||
@@ -1,33 +1,37 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export class Markdown {
|
||||
filepath: string
|
||||
type MarkdownConfig = {
|
||||
build: string
|
||||
template: string
|
||||
}
|
||||
|
||||
constructor(filepath: string) {
|
||||
this.filepath = filepath
|
||||
export class Markdown {
|
||||
build: string
|
||||
template: string
|
||||
|
||||
constructor(config: MarkdownConfig) {
|
||||
this.build = config.build
|
||||
this.template = config.template
|
||||
}
|
||||
|
||||
compile() {
|
||||
const config = JSON.parse(fs.readFileSync(this.filepath, 'utf8'))
|
||||
const workingDir = process.cwd()
|
||||
|
||||
config.files.forEach((templateFile: string) => {
|
||||
const templatePath = path.resolve(workingDir, templateFile)
|
||||
const content = fs.readFileSync(templatePath, 'utf8')
|
||||
const processedContent = this.processIncludes(content, workingDir)
|
||||
|
||||
if (config.build) {
|
||||
const outputPath = path.resolve(workingDir, config.build)
|
||||
fs.writeFileSync(outputPath, processedContent, 'utf8')
|
||||
}
|
||||
})
|
||||
const workingDir = process.cwd()
|
||||
|
||||
const templatePath = path.resolve(workingDir, this.template)
|
||||
const template = fs.readFileSync(templatePath, 'utf8')
|
||||
const processedContent = this.processIncludes(template, workingDir)
|
||||
|
||||
if (this.build) {
|
||||
const outputPath = path.resolve(workingDir, this.build)
|
||||
fs.writeFileSync(outputPath, processedContent, 'utf8')
|
||||
}
|
||||
}
|
||||
|
||||
private processIncludes(content: string, baseDir: string): string {
|
||||
private processIncludes(template: string, baseDir: string): string {
|
||||
const includeRegex = /#include\s+"([^"]+)"/g
|
||||
|
||||
return content.replace(includeRegex, (match, includePath) => {
|
||||
|
||||
return template.replace(includeRegex, (match, includePath) => {
|
||||
try {
|
||||
const fullPath = path.resolve(baseDir, includePath)
|
||||
const includeContent = fs.readFileSync(fullPath, 'utf8')
|
||||
|
||||
43
scripts/generators/citiesGenerator.ts
Normal file
43
scripts/generators/citiesGenerator.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { City, Stream, Playlist } from '../models'
|
||||
import { Collection, Storage, File } from '@freearhey/core'
|
||||
import { PUBLIC_DIR, EOL } from '../constants'
|
||||
import { Generator } from './generator'
|
||||
|
||||
type CitiesGeneratorProps = {
|
||||
streams: Collection
|
||||
cities: Collection
|
||||
logFile: File
|
||||
}
|
||||
|
||||
export class CitiesGenerator implements Generator {
|
||||
streams: Collection
|
||||
cities: Collection
|
||||
storage: Storage
|
||||
logFile: File
|
||||
|
||||
constructor({ streams, cities, logFile }: CitiesGeneratorProps) {
|
||||
this.streams = streams.clone()
|
||||
this.cities = cities
|
||||
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.cities.forEach(async (city: City) => {
|
||||
const cityStreams = streams.filter((stream: Stream) => stream.isBroadcastInCity(city))
|
||||
|
||||
if (cityStreams.isEmpty()) return
|
||||
|
||||
const playlist = new Playlist(cityStreams, { public: true })
|
||||
const filepath = `cities/${city.code.toLowerCase()}.m3u`
|
||||
await this.storage.save(filepath, playlist.toString())
|
||||
this.logFile.append(
|
||||
JSON.stringify({ type: 'city', filepath, count: playlist.streams.count() }) + EOL
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,18 @@ export class CountriesGenerator implements Generator {
|
||||
)
|
||||
})
|
||||
|
||||
const internationalStreams = streams.filter((stream: Stream) => stream.isInternational())
|
||||
const internationalPlaylist = new Playlist(internationalStreams, { public: true })
|
||||
const internationalFilepath = 'countries/int.m3u'
|
||||
await this.storage.save(internationalFilepath, internationalPlaylist.toString())
|
||||
this.logFile.append(
|
||||
JSON.stringify({
|
||||
type: 'country',
|
||||
filepath: internationalFilepath,
|
||||
count: internationalPlaylist.streams.count()
|
||||
}) + EOL
|
||||
)
|
||||
|
||||
const undefinedStreams = streams.filter((stream: Stream) => !stream.hasBroadcastArea())
|
||||
const undefinedPlaylist = new Playlist(undefinedStreams, { public: true })
|
||||
const undefinedFilepath = 'countries/undefined.m3u'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './categoriesGenerator'
|
||||
export * from './citiesGenerator'
|
||||
export * from './countriesGenerator'
|
||||
export * from './indexCategoryGenerator'
|
||||
export * from './indexCountryGenerator'
|
||||
|
||||
@@ -26,6 +26,13 @@ export class IndexCountryGenerator implements Generator {
|
||||
.orderBy((stream: Stream) => stream.getTitle())
|
||||
.filter((stream: Stream) => stream.isSFW())
|
||||
.forEach((stream: Stream) => {
|
||||
if (stream.isInternational()) {
|
||||
const streamClone = stream.clone()
|
||||
streamClone.groupTitle = 'International'
|
||||
groupedStreams.add(streamClone)
|
||||
return
|
||||
}
|
||||
|
||||
if (!stream.hasBroadcastArea()) {
|
||||
const streamClone = stream.clone()
|
||||
streamClone.groupTitle = 'Undefined'
|
||||
@@ -33,12 +40,6 @@ export class IndexCountryGenerator implements Generator {
|
||||
return
|
||||
}
|
||||
|
||||
if (stream.isInternational()) {
|
||||
const streamClone = stream.clone()
|
||||
streamClone.groupTitle = 'International'
|
||||
groupedStreams.add(streamClone)
|
||||
}
|
||||
|
||||
stream.getBroadcastCountries().forEach((country: Country) => {
|
||||
const streamClone = stream.clone()
|
||||
streamClone.groupTitle = country.name
|
||||
|
||||
@@ -28,32 +28,18 @@ export class IndexRegionGenerator implements Generator {
|
||||
.orderBy((stream: Stream) => stream.getTitle())
|
||||
.filter((stream: Stream) => stream.isSFW())
|
||||
.forEach((stream: Stream) => {
|
||||
if (stream.isInternational()) {
|
||||
const streamClone = stream.clone()
|
||||
streamClone.groupTitle = 'International'
|
||||
groupedStreams.push(streamClone)
|
||||
return
|
||||
}
|
||||
|
||||
if (!stream.hasBroadcastArea()) {
|
||||
const streamClone = stream.clone()
|
||||
streamClone.groupTitle = 'Undefined'
|
||||
groupedStreams.push(streamClone)
|
||||
return
|
||||
}
|
||||
if (!stream.hasBroadcastArea()) return
|
||||
|
||||
stream.getBroadcastRegions().forEach((region: Region) => {
|
||||
if (region.isWorldwide()) return
|
||||
|
||||
const streamClone = stream.clone()
|
||||
streamClone.groupTitle = region.name
|
||||
groupedStreams.push(streamClone)
|
||||
})
|
||||
})
|
||||
|
||||
groupedStreams = groupedStreams.orderBy((stream: Stream) => {
|
||||
if (stream.groupTitle === 'International') return 'ZZ'
|
||||
if (stream.groupTitle === 'Undefined') return 'ZZZ'
|
||||
return stream.groupTitle
|
||||
})
|
||||
groupedStreams = groupedStreams.orderBy((stream: Stream) => stream.groupTitle)
|
||||
|
||||
const playlist = new Playlist(groupedStreams, { public: true })
|
||||
const filepath = 'index.region.m3u'
|
||||
|
||||
@@ -39,25 +39,5 @@ export class RegionsGenerator implements Generator {
|
||||
JSON.stringify({ type: 'region', filepath, count: playlist.streams.count() }) + EOL
|
||||
)
|
||||
})
|
||||
|
||||
const internationalStreams = streams.filter((stream: Stream) => stream.isInternational())
|
||||
const internationalPlaylist = new Playlist(internationalStreams, { public: true })
|
||||
const internationalFilepath = 'regions/int.m3u'
|
||||
await this.storage.save(internationalFilepath, internationalPlaylist.toString())
|
||||
this.logFile.append(
|
||||
JSON.stringify({
|
||||
type: 'region',
|
||||
filepath: internationalFilepath,
|
||||
count: internationalPlaylist.streams.count()
|
||||
}) + EOL
|
||||
)
|
||||
|
||||
const undefinedStreams = streams.filter((stream: Stream) => !stream.hasBroadcastArea())
|
||||
const playlist = new Playlist(undefinedStreams, { public: true })
|
||||
const filepath = 'regions/undefined.m3u'
|
||||
await this.storage.save(filepath, playlist.toString())
|
||||
this.logFile.append(
|
||||
JSON.stringify({ type: 'region', filepath, count: playlist.streams.count() }) + EOL
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,99 @@
|
||||
type BroadcastAreaProps = {
|
||||
code: string
|
||||
}
|
||||
import { Collection, Dictionary } from '@freearhey/core'
|
||||
import { City, Subdivision, Region, Country } from './'
|
||||
|
||||
export class BroadcastArea {
|
||||
code: string
|
||||
codes: Collection
|
||||
citiesIncluded: Collection
|
||||
subdivisionsIncluded: Collection
|
||||
countriesIncluded: Collection
|
||||
regionsIncluded: Collection
|
||||
|
||||
constructor(data: BroadcastAreaProps) {
|
||||
this.code = data.code
|
||||
constructor(codes: Collection) {
|
||||
this.codes = codes
|
||||
}
|
||||
|
||||
withLocations(
|
||||
citiesKeyByCode: Dictionary,
|
||||
subdivisionsKeyByCode: Dictionary,
|
||||
countriesKeyByCode: Dictionary,
|
||||
regionsKeyByCode: Dictionary
|
||||
): this {
|
||||
let citiesIncluded = new Collection()
|
||||
let subdivisionsIncluded = new Collection()
|
||||
let countriesIncluded = new Collection()
|
||||
let regionsIncluded = new Collection()
|
||||
|
||||
this.codes.forEach((value: string) => {
|
||||
const [type, code] = value.split('/')
|
||||
|
||||
switch (type) {
|
||||
case 'ct': {
|
||||
const city: City = citiesKeyByCode.get(code)
|
||||
if (!city) return
|
||||
citiesIncluded.add(city)
|
||||
regionsIncluded = regionsIncluded.concat(city.getRegions())
|
||||
}
|
||||
case 's': {
|
||||
const subdivision: Subdivision = subdivisionsKeyByCode.get(code)
|
||||
if (!subdivision) return
|
||||
subdivisionsIncluded.add(subdivision)
|
||||
regionsIncluded = regionsIncluded.concat(subdivision.getRegions())
|
||||
}
|
||||
case 'c': {
|
||||
const country: Country = countriesKeyByCode.get(code)
|
||||
if (!country) return
|
||||
countriesIncluded.add(country)
|
||||
regionsIncluded = regionsIncluded.concat(country.getRegions())
|
||||
}
|
||||
case 'r': {
|
||||
const region: Region = regionsKeyByCode.get(code)
|
||||
if (!region) return
|
||||
regionsIncluded = regionsIncluded.concat(region.getRegions())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.citiesIncluded = citiesIncluded.uniqBy((city: City) => city.code)
|
||||
this.subdivisionsIncluded = subdivisionsIncluded.uniqBy(
|
||||
(subdivision: Subdivision) => subdivision.code
|
||||
)
|
||||
this.countriesIncluded = countriesIncluded.uniqBy((country: Country) => country.code)
|
||||
this.regionsIncluded = regionsIncluded.uniqBy((region: Region) => region.code)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getCountries(): Collection {
|
||||
return this.countriesIncluded || new Collection()
|
||||
}
|
||||
|
||||
getSubdivisions(): Collection {
|
||||
return this.subdivisionsIncluded || new Collection()
|
||||
}
|
||||
|
||||
getCities(): Collection {
|
||||
return this.citiesIncluded || new Collection()
|
||||
}
|
||||
|
||||
getRegions(): Collection {
|
||||
return this.regionsIncluded || new Collection()
|
||||
}
|
||||
|
||||
includesCountry(country: Country): boolean {
|
||||
return this.getCountries().includes((_country: Country) => _country.code === country.code)
|
||||
}
|
||||
|
||||
includesSubdivision(subdivision: Subdivision): boolean {
|
||||
return this.getSubdivisions().includes(
|
||||
(_subdivision: Subdivision) => _subdivision.code === subdivision.code
|
||||
)
|
||||
}
|
||||
|
||||
includesRegion(region: Region): boolean {
|
||||
return this.getRegions().includes((_region: Region) => _region.code === region.code)
|
||||
}
|
||||
|
||||
includesCity(city: City): boolean {
|
||||
return this.getCities().includes((_city: City) => _city.code === city.code)
|
||||
}
|
||||
}
|
||||
|
||||
78
scripts/models/city.ts
Normal file
78
scripts/models/city.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Collection, Dictionary } from '@freearhey/core'
|
||||
import { Country, Region, Subdivision } from '.'
|
||||
import type { CityData, CitySerializedData } from '../types/city'
|
||||
|
||||
export class City {
|
||||
code: string
|
||||
name: string
|
||||
countryCode: string
|
||||
country?: Country
|
||||
subdivisionCode?: string
|
||||
subdivision?: Subdivision
|
||||
wikidataId: string
|
||||
regions?: Collection
|
||||
|
||||
constructor(data?: CityData) {
|
||||
if (!data) return
|
||||
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.countryCode = data.country
|
||||
this.subdivisionCode = data.subdivision || undefined
|
||||
this.wikidataId = data.wikidata_id
|
||||
}
|
||||
|
||||
withCountry(countriesKeyByCode: Dictionary): this {
|
||||
this.country = countriesKeyByCode.get(this.countryCode)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withSubdivision(subdivisionsKeyByCode: Dictionary): this {
|
||||
if (!this.subdivisionCode) return this
|
||||
|
||||
this.subdivision = subdivisionsKeyByCode.get(this.subdivisionCode)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withRegions(regions: Collection): this {
|
||||
this.regions = regions.filter((region: Region) =>
|
||||
region.countryCodes.includes(this.countryCode)
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getRegions(): Collection {
|
||||
if (!this.regions) return new Collection()
|
||||
|
||||
return this.regions
|
||||
}
|
||||
|
||||
serialize(): CitySerializedData {
|
||||
return {
|
||||
code: this.code,
|
||||
name: this.name,
|
||||
countryCode: this.countryCode,
|
||||
country: this.country ? this.country.serialize() : undefined,
|
||||
subdivisionCode: this.subdivisionCode || null,
|
||||
subdivision: this.subdivision ? this.subdivision.serialize() : undefined,
|
||||
wikidataId: this.wikidataId
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: CitySerializedData): this {
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.countryCode = data.countryCode
|
||||
this.country = data.country ? new Country().deserialize(data.country) : undefined
|
||||
this.subdivisionCode = data.subdivisionCode || undefined
|
||||
this.subdivision = data.subdivision
|
||||
? new Subdivision().deserialize(data.subdivision)
|
||||
: undefined
|
||||
this.wikidataId = data.wikidataId
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ export class Country {
|
||||
language?: Language
|
||||
subdivisions?: Collection
|
||||
regions?: Collection
|
||||
cities?: Collection
|
||||
|
||||
constructor(data?: CountryData) {
|
||||
if (!data) return
|
||||
@@ -23,15 +24,19 @@ export class Country {
|
||||
}
|
||||
|
||||
withSubdivisions(subdivisionsGroupedByCountryCode: Dictionary): this {
|
||||
this.subdivisions = subdivisionsGroupedByCountryCode.get(this.code) || new Collection()
|
||||
this.subdivisions = new Collection(subdivisionsGroupedByCountryCode.get(this.code))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withRegions(regions: Collection): this {
|
||||
this.regions = regions.filter(
|
||||
(region: Region) => region.code !== 'INT' && region.includesCountryCode(this.code)
|
||||
)
|
||||
this.regions = regions.filter((region: Region) => region.includesCountryCode(this.code))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withCities(citiesGroupedByCountryCode: Dictionary): this {
|
||||
this.cities = new Collection(citiesGroupedByCountryCode.get(this.code))
|
||||
|
||||
return this
|
||||
}
|
||||
@@ -54,6 +59,10 @@ export class Country {
|
||||
return this.subdivisions || new Collection()
|
||||
}
|
||||
|
||||
getCities(): Collection {
|
||||
return this.cities || new Collection()
|
||||
}
|
||||
|
||||
serialize(): CountrySerializedData {
|
||||
return {
|
||||
code: this.code,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Country, Language, Region, Channel, Subdivision } from './index'
|
||||
import { Country, Language, Region, Channel, Subdivision, BroadcastArea, City } from './index'
|
||||
import { Collection, Dictionary } from '@freearhey/core'
|
||||
import type { FeedData } from '../types/feed'
|
||||
|
||||
@@ -9,12 +9,7 @@ export class Feed {
|
||||
name: string
|
||||
isMain: boolean
|
||||
broadcastAreaCodes: Collection
|
||||
broadcastCountryCodes: Collection
|
||||
broadcastCountries?: Collection
|
||||
broadcastRegionCodes: Collection
|
||||
broadcastRegions?: Collection
|
||||
broadcastSubdivisionCodes: Collection
|
||||
broadcastSubdivisions?: Collection
|
||||
broadcastArea?: BroadcastArea
|
||||
languageCodes: Collection
|
||||
languages?: Collection
|
||||
timezoneIds: Collection
|
||||
@@ -32,25 +27,6 @@ export class Feed {
|
||||
this.languageCodes = new Collection(data.languages)
|
||||
this.timezoneIds = new Collection(data.timezones)
|
||||
this.videoFormat = data.video_format
|
||||
this.broadcastCountryCodes = new Collection()
|
||||
this.broadcastRegionCodes = new Collection()
|
||||
this.broadcastSubdivisionCodes = new Collection()
|
||||
|
||||
this.broadcastAreaCodes.forEach((areaCode: string) => {
|
||||
const [type, code] = areaCode.split('/')
|
||||
|
||||
switch (type) {
|
||||
case 'c':
|
||||
this.broadcastCountryCodes.add(code)
|
||||
break
|
||||
case 'r':
|
||||
this.broadcastRegionCodes.add(code)
|
||||
break
|
||||
case 's':
|
||||
this.broadcastSubdivisionCodes.add(code)
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
withChannel(channelsKeyById: Dictionary): this {
|
||||
@@ -93,76 +69,36 @@ export class Feed {
|
||||
return this
|
||||
}
|
||||
|
||||
withBroadcastSubdivisions(subdivisionsKeyByCode: Dictionary): this {
|
||||
this.broadcastSubdivisions = this.broadcastSubdivisionCodes.map((code: string) =>
|
||||
subdivisionsKeyByCode.get(code)
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withBroadcastCountries(
|
||||
withBroadcastArea(
|
||||
citiesKeyByCode: Dictionary,
|
||||
subdivisionsKeyByCode: Dictionary,
|
||||
countriesKeyByCode: Dictionary,
|
||||
regionsKeyByCode: Dictionary,
|
||||
subdivisionsKeyByCode: Dictionary
|
||||
regionsKeyByCode: Dictionary
|
||||
): this {
|
||||
const broadcastCountries = new Collection()
|
||||
|
||||
if (this.isInternational()) {
|
||||
this.broadcastCountries = broadcastCountries
|
||||
return this
|
||||
}
|
||||
|
||||
this.broadcastCountryCodes.forEach((code: string) => {
|
||||
broadcastCountries.add(countriesKeyByCode.get(code))
|
||||
})
|
||||
|
||||
this.broadcastRegionCodes.forEach((code: string) => {
|
||||
const region: Region = regionsKeyByCode.get(code)
|
||||
if (region) {
|
||||
region.countryCodes.forEach((countryCode: string) => {
|
||||
broadcastCountries.add(countriesKeyByCode.get(countryCode))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.broadcastSubdivisionCodes.forEach((code: string) => {
|
||||
const subdivision: Subdivision = subdivisionsKeyByCode.get(code)
|
||||
if (subdivision) {
|
||||
broadcastCountries.add(countriesKeyByCode.get(subdivision.countryCode))
|
||||
}
|
||||
})
|
||||
|
||||
this.broadcastCountries = broadcastCountries.uniq().filter(Boolean)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withBroadcastRegions(regions: Collection): this {
|
||||
if (!this.broadcastCountries) return this
|
||||
const countriesCodes = this.broadcastCountries.map((country: Country) => country.code)
|
||||
|
||||
this.broadcastRegions = regions.filter((region: Region) => {
|
||||
if (region.code === 'INT') return false
|
||||
const intersected = region.countryCodes.intersects(countriesCodes)
|
||||
return intersected.notEmpty()
|
||||
})
|
||||
this.broadcastArea = new BroadcastArea(this.broadcastAreaCodes).withLocations(
|
||||
citiesKeyByCode,
|
||||
subdivisionsKeyByCode,
|
||||
countriesKeyByCode,
|
||||
regionsKeyByCode
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
hasBroadcastArea(): boolean {
|
||||
return (
|
||||
this.isInternational() || (!!this.broadcastCountries && this.broadcastCountries.notEmpty())
|
||||
)
|
||||
return !!this.broadcastArea
|
||||
}
|
||||
|
||||
getBroadcastCountries(): Collection {
|
||||
return this.broadcastCountries || new Collection()
|
||||
if (!this.broadcastArea) return new Collection()
|
||||
|
||||
return this.broadcastArea.getCountries()
|
||||
}
|
||||
|
||||
getBroadcastRegions(): Collection {
|
||||
return this.broadcastRegions || new Collection()
|
||||
if (!this.broadcastArea) return new Collection()
|
||||
|
||||
return this.broadcastArea.getRegions()
|
||||
}
|
||||
|
||||
getTimezones(): Collection {
|
||||
@@ -184,35 +120,34 @@ export class Feed {
|
||||
)
|
||||
}
|
||||
|
||||
isInternational(): boolean {
|
||||
return this.broadcastAreaCodes.includes('r/INT')
|
||||
isBroadcastInCity(city: City): boolean {
|
||||
if (!this.broadcastArea) return false
|
||||
|
||||
return this.broadcastArea.includesCity(city)
|
||||
}
|
||||
|
||||
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
|
||||
if (!this.broadcastArea) return false
|
||||
|
||||
return false
|
||||
return this.broadcastArea.includesSubdivision(subdivision)
|
||||
}
|
||||
|
||||
isBroadcastInCountry(country: Country): boolean {
|
||||
if (this.isInternational()) return false
|
||||
if (!this.broadcastArea) return false
|
||||
|
||||
return this.getBroadcastCountries().includes(
|
||||
(_country: Country) => _country.code === country.code
|
||||
)
|
||||
return this.broadcastArea.includesCountry(country)
|
||||
}
|
||||
|
||||
isBroadcastInRegion(region: Region): boolean {
|
||||
if (this.isInternational()) return false
|
||||
if (!this.broadcastArea) return false
|
||||
|
||||
return this.getBroadcastRegions().includes((_region: Region) => _region.code === region.code)
|
||||
return this.broadcastArea.includesRegion(region)
|
||||
}
|
||||
|
||||
isInternational(): boolean {
|
||||
if (!this.broadcastArea) return false
|
||||
|
||||
return this.broadcastArea.codes.join(',').includes('r/')
|
||||
}
|
||||
|
||||
getGuides(): Collection {
|
||||
|
||||
@@ -2,6 +2,7 @@ export * from './blocklistRecord'
|
||||
export * from './broadcastArea'
|
||||
export * from './category'
|
||||
export * from './channel'
|
||||
export * from './city'
|
||||
export * from './country'
|
||||
export * from './feed'
|
||||
export * from './guide'
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { Collection, Dictionary } from '@freearhey/core'
|
||||
import { Country, Subdivision } from '.'
|
||||
import { City, Country, Subdivision } from '.'
|
||||
import type { RegionData, RegionSerializedData } from '../types/region'
|
||||
import { CountrySerializedData } from '../types/country'
|
||||
import { SubdivisionSerializedData } from '../types/subdivision'
|
||||
import { CitySerializedData } from '../types/city'
|
||||
|
||||
export class Region {
|
||||
code: string
|
||||
name: string
|
||||
countryCodes: Collection
|
||||
countries: Collection = new Collection()
|
||||
subdivisions: Collection = new Collection()
|
||||
countries?: Collection
|
||||
subdivisions?: Collection
|
||||
cities?: Collection
|
||||
regions?: Collection
|
||||
|
||||
constructor(data?: RegionData) {
|
||||
if (!data) return
|
||||
@@ -33,20 +36,50 @@ export class Region {
|
||||
return this
|
||||
}
|
||||
|
||||
withCities(cities: Collection): this {
|
||||
this.cities = cities.filter((city: City) => this.countryCodes.indexOf(city.countryCode) > -1)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withRegions(regions: Collection): this {
|
||||
this.regions = regions.filter(
|
||||
(region: Region) => !region.countryCodes.intersects(this.countryCodes).isEmpty()
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getSubdivisions(): Collection {
|
||||
if (!this.subdivisions) return new Collection()
|
||||
|
||||
return this.subdivisions
|
||||
}
|
||||
|
||||
getCountries(): Collection {
|
||||
if (!this.countries) return new Collection()
|
||||
|
||||
return this.countries
|
||||
}
|
||||
|
||||
getCities(): Collection {
|
||||
if (!this.cities) return new Collection()
|
||||
|
||||
return this.cities
|
||||
}
|
||||
|
||||
getRegions(): Collection {
|
||||
if (!this.regions) return new Collection()
|
||||
|
||||
return this.regions
|
||||
}
|
||||
|
||||
includesCountryCode(code: string): boolean {
|
||||
return this.countryCodes.includes((countryCode: string) => countryCode === code)
|
||||
}
|
||||
|
||||
isWorldwide(): boolean {
|
||||
return this.code === 'INT'
|
||||
return ['INT', 'WW'].includes(this.code)
|
||||
}
|
||||
|
||||
serialize(): RegionSerializedData {
|
||||
@@ -54,9 +87,14 @@ export class Region {
|
||||
code: this.code,
|
||||
name: this.name,
|
||||
countryCodes: this.countryCodes.all(),
|
||||
countries: this.countries.map((country: Country) => country.serialize()).all(),
|
||||
subdivisions: this.subdivisions
|
||||
countries: this.getCountries()
|
||||
.map((country: Country) => country.serialize())
|
||||
.all(),
|
||||
subdivisions: this.getSubdivisions()
|
||||
.map((subdivision: Subdivision) => subdivision.serialize())
|
||||
.all(),
|
||||
cities: this.getCities()
|
||||
.map((city: City) => city.serialize())
|
||||
.all()
|
||||
}
|
||||
}
|
||||
@@ -71,6 +109,9 @@ export class Region {
|
||||
this.subdivisions = new Collection(data.subdivisions).map((data: SubdivisionSerializedData) =>
|
||||
new Subdivision().deserialize(data)
|
||||
)
|
||||
this.cities = new Collection(data.cities).map((data: CitySerializedData) =>
|
||||
new City().deserialize(data)
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
import { Feed, Channel, Category, Region, Subdivision, Country, Language, Logo } from './index'
|
||||
import {
|
||||
Feed,
|
||||
Channel,
|
||||
Category,
|
||||
Region,
|
||||
Subdivision,
|
||||
Country,
|
||||
Language,
|
||||
Logo,
|
||||
City
|
||||
} from './index'
|
||||
import { URL, Collection, Dictionary } from '@freearhey/core'
|
||||
import type { StreamData } from '../types/stream'
|
||||
import parser from 'iptv-playlist-parser'
|
||||
@@ -330,6 +340,10 @@ export class Stream {
|
||||
return this.feed ? this.feed.broadcastAreaCodes : new Collection()
|
||||
}
|
||||
|
||||
isBroadcastInCity(city: City): boolean {
|
||||
return this.feed ? this.feed.isBroadcastInCity(city) : false
|
||||
}
|
||||
|
||||
isBroadcastInSubdivision(subdivision: Subdivision): boolean {
|
||||
return this.feed ? this.feed.isBroadcastInSubdivision(subdivision) : false
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { SubdivisionData, SubdivisionSerializedData } from '../types/subdivision'
|
||||
import { Dictionary } from '@freearhey/core'
|
||||
import { Country } from '.'
|
||||
import { Dictionary, Collection } from '@freearhey/core'
|
||||
import { Country, Region } from '.'
|
||||
|
||||
export class Subdivision {
|
||||
code: string
|
||||
name: string
|
||||
countryCode: string
|
||||
country?: Country
|
||||
parentCode?: string
|
||||
regions?: Collection
|
||||
cities?: Collection
|
||||
|
||||
constructor(data?: SubdivisionData) {
|
||||
if (!data) return
|
||||
@@ -14,6 +17,7 @@ export class Subdivision {
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.countryCode = data.country
|
||||
this.parentCode = data.parent || undefined
|
||||
}
|
||||
|
||||
withCountry(countriesKeyByCode: Dictionary): this {
|
||||
@@ -22,12 +26,39 @@ export class Subdivision {
|
||||
return this
|
||||
}
|
||||
|
||||
withRegions(regions: Collection): this {
|
||||
this.regions = regions.filter((region: Region) =>
|
||||
region.countryCodes.includes(this.countryCode)
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withCities(citiesGroupedBySubdivisionCode: Dictionary): this {
|
||||
this.cities = new Collection(citiesGroupedBySubdivisionCode.get(this.code))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getRegions(): Collection {
|
||||
if (!this.regions) return new Collection()
|
||||
|
||||
return this.regions
|
||||
}
|
||||
|
||||
getCities(): Collection {
|
||||
if (!this.cities) return new Collection()
|
||||
|
||||
return this.cities
|
||||
}
|
||||
|
||||
serialize(): SubdivisionSerializedData {
|
||||
return {
|
||||
code: this.code,
|
||||
name: this.name,
|
||||
countryCode: this.code,
|
||||
country: this.country ? this.country.serialize() : undefined
|
||||
countryCode: this.countryCode,
|
||||
country: this.country ? this.country.serialize() : undefined,
|
||||
parentCode: this.parentCode || null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +67,7 @@ export class Subdivision {
|
||||
this.name = data.name
|
||||
this.countryCode = data.countryCode
|
||||
this.country = data.country ? new Country().deserialize(data.country) : undefined
|
||||
this.parentCode = data.parentCode || undefined
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -1,32 +1,35 @@
|
||||
import { Storage, Collection, File } from '@freearhey/core'
|
||||
import { Storage, Collection, File, Dictionary } from '@freearhey/core'
|
||||
import { HTMLTable, LogParser, LogItem } from '../core'
|
||||
import { LOGS_DIR, README_DIR } from '../constants'
|
||||
import { Category } from '../models'
|
||||
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
|
||||
import { Table } from './table'
|
||||
|
||||
export class CategoryTable implements Table {
|
||||
constructor() {}
|
||||
type CategoriesTableProps = {
|
||||
categoriesKeyById: Dictionary
|
||||
}
|
||||
|
||||
export class CategoriesTable implements Table {
|
||||
categoriesKeyById: Dictionary
|
||||
|
||||
constructor({ categoriesKeyById }: CategoriesTableProps) {
|
||||
this.categoriesKeyById = categoriesKeyById
|
||||
}
|
||||
|
||||
async make() {
|
||||
const dataStorage = new Storage(DATA_DIR)
|
||||
const categoriesContent = await dataStorage.json('categories.json')
|
||||
const categories = new Collection(categoriesContent).map(data => new Category(data))
|
||||
const categoriesGroupedById = categories.keyBy((category: Category) => category.id)
|
||||
|
||||
const parser = new LogParser()
|
||||
const logsStorage = new Storage(LOGS_DIR)
|
||||
const generatorsLog = await logsStorage.load('generators.log')
|
||||
|
||||
let data = new Collection()
|
||||
let items = new Collection()
|
||||
parser
|
||||
.parse(generatorsLog)
|
||||
.filter((logItem: LogItem) => logItem.type === 'category')
|
||||
.forEach((logItem: LogItem) => {
|
||||
const file = new File(logItem.filepath)
|
||||
const categoryId = file.name()
|
||||
const category: Category = categoriesGroupedById.get(categoryId)
|
||||
const category: Category = this.categoriesKeyById.get(categoryId)
|
||||
|
||||
data.add([
|
||||
items.add([
|
||||
category ? category.name : 'ZZ',
|
||||
category ? category.name : 'Undefined',
|
||||
logItem.count,
|
||||
@@ -34,14 +37,14 @@ export class CategoryTable implements Table {
|
||||
])
|
||||
})
|
||||
|
||||
data = data
|
||||
items = items
|
||||
.orderBy(item => item[0])
|
||||
.map(item => {
|
||||
item.shift()
|
||||
return item
|
||||
})
|
||||
|
||||
const table = new HTMLTable(data.all(), [
|
||||
const table = new HTMLTable(items.all(), [
|
||||
{ name: 'Category' },
|
||||
{ name: 'Channels', align: 'right' },
|
||||
{ name: 'Playlist', nowrap: true }
|
||||
189
scripts/tables/countriesTable.ts
Normal file
189
scripts/tables/countriesTable.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { Storage, Collection, Dictionary } from '@freearhey/core'
|
||||
import { City, Country, Subdivision } from '../models'
|
||||
import { LOGS_DIR, README_DIR } from '../constants'
|
||||
import { LogParser, LogItem } from '../core'
|
||||
import { Table } from './table'
|
||||
|
||||
type CountriesTableProps = {
|
||||
countriesKeyByCode: Dictionary
|
||||
subdivisionsKeyByCode: Dictionary
|
||||
countries: Collection
|
||||
subdivisions: Collection
|
||||
cities: Collection
|
||||
}
|
||||
|
||||
export class CountriesTable implements Table {
|
||||
countriesKeyByCode: Dictionary
|
||||
subdivisionsKeyByCode: Dictionary
|
||||
countries: Collection
|
||||
subdivisions: Collection
|
||||
cities: Collection
|
||||
|
||||
constructor({
|
||||
countriesKeyByCode,
|
||||
subdivisionsKeyByCode,
|
||||
countries,
|
||||
subdivisions,
|
||||
cities
|
||||
}: CountriesTableProps) {
|
||||
this.countriesKeyByCode = countriesKeyByCode
|
||||
this.subdivisionsKeyByCode = subdivisionsKeyByCode
|
||||
this.countries = countries
|
||||
this.subdivisions = subdivisions
|
||||
this.cities = cities
|
||||
}
|
||||
|
||||
async make() {
|
||||
const parser = new LogParser()
|
||||
const logsStorage = new Storage(LOGS_DIR)
|
||||
const generatorsLog = await logsStorage.load('generators.log')
|
||||
const parsed = parser.parse(generatorsLog)
|
||||
const logCountries = parsed.filter((logItem: LogItem) => logItem.type === 'country')
|
||||
const logSubdivisions = parsed.filter((logItem: LogItem) => logItem.type === 'subdivision')
|
||||
const logCities = parsed.filter((logItem: LogItem) => logItem.type === 'city')
|
||||
|
||||
let items = new Collection()
|
||||
this.countries.forEach((country: Country) => {
|
||||
const countriesLogItem = logCountries.find(
|
||||
(logItem: LogItem) => logItem.filepath === `countries/${country.code.toLowerCase()}.m3u`
|
||||
)
|
||||
|
||||
let countryItem = {
|
||||
index: country.name,
|
||||
count: 0,
|
||||
link: `https://iptv-org.github.io/iptv/countries/${country.code.toLowerCase()}.m3u`,
|
||||
name: `${country.flag} ${country.name}`,
|
||||
children: new Collection()
|
||||
}
|
||||
|
||||
if (countriesLogItem) {
|
||||
countryItem.count = countriesLogItem.count
|
||||
}
|
||||
|
||||
const countrySubdivisions = this.subdivisions.filter(
|
||||
(subdivision: Subdivision) => subdivision.countryCode === country.code
|
||||
)
|
||||
const countryCities = this.cities.filter((city: City) => city.countryCode === country.code)
|
||||
if (countrySubdivisions.notEmpty()) {
|
||||
this.subdivisions.forEach((subdivision: Subdivision) => {
|
||||
if (subdivision.countryCode !== country.code) return
|
||||
const subdivisionCities = countryCities.filter(
|
||||
(city: City) =>
|
||||
(city.subdivisionCode && city.subdivisionCode === subdivision.code) ||
|
||||
city.countryCode === subdivision.countryCode
|
||||
)
|
||||
const subdivisionsLogItem = logSubdivisions.find(
|
||||
(logItem: LogItem) =>
|
||||
logItem.filepath === `subdivisions/${subdivision.code.toLowerCase()}.m3u`
|
||||
)
|
||||
|
||||
let subdivisionItem = {
|
||||
index: subdivision.name,
|
||||
name: subdivision.name,
|
||||
count: 0,
|
||||
link: `https://iptv-org.github.io/iptv/subdivisions/${subdivision.code.toLowerCase()}.m3u`,
|
||||
children: new Collection()
|
||||
}
|
||||
|
||||
if (subdivisionsLogItem) {
|
||||
subdivisionItem.count = subdivisionsLogItem.count
|
||||
}
|
||||
|
||||
subdivisionCities.forEach((city: City) => {
|
||||
if (city.countryCode !== country.code || city.subdivisionCode !== subdivision.code)
|
||||
return
|
||||
const citiesLogItem = logCities.find(
|
||||
(logItem: LogItem) => logItem.filepath === `cities/${city.code.toLowerCase()}.m3u`
|
||||
)
|
||||
|
||||
if (!citiesLogItem) return
|
||||
|
||||
subdivisionItem.children.add({
|
||||
index: city.name,
|
||||
name: city.name,
|
||||
count: citiesLogItem.count,
|
||||
link: `https://iptv-org.github.io/iptv/${citiesLogItem.filepath}`
|
||||
})
|
||||
})
|
||||
|
||||
if (subdivisionItem.count > 0 || subdivisionItem.children.notEmpty()) {
|
||||
countryItem.children.add(subdivisionItem)
|
||||
}
|
||||
})
|
||||
} else if (countryCities.notEmpty()) {
|
||||
countryCities.forEach((city: City) => {
|
||||
const citiesLogItem = logCities.find(
|
||||
(logItem: LogItem) => logItem.filepath === `cities/${city.code.toLowerCase()}.m3u`
|
||||
)
|
||||
|
||||
if (!citiesLogItem) return
|
||||
|
||||
countryItem.children.add({
|
||||
index: city.name,
|
||||
name: city.name,
|
||||
count: citiesLogItem.count,
|
||||
link: `https://iptv-org.github.io/iptv/${citiesLogItem.filepath}`,
|
||||
children: new Collection()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (countryItem.count > 0 || countryItem.children.notEmpty()) {
|
||||
items.add(countryItem)
|
||||
}
|
||||
})
|
||||
|
||||
const internationalLogItem = logCountries.find(
|
||||
(logItem: LogItem) => logItem.filepath === 'countries/int.m3u'
|
||||
)
|
||||
|
||||
if (internationalLogItem) {
|
||||
items.push({
|
||||
index: 'ZZ',
|
||||
name: '🌐 International',
|
||||
count: internationalLogItem.count,
|
||||
link: `https://iptv-org.github.io/iptv/${internationalLogItem.filepath}`,
|
||||
children: new Collection()
|
||||
})
|
||||
}
|
||||
|
||||
const undefinedLogItem = logCountries.find(
|
||||
(logItem: LogItem) => logItem.filepath === `countries/undefined.m3u`
|
||||
)
|
||||
|
||||
if (undefinedLogItem) {
|
||||
items.push({
|
||||
index: 'ZZZ',
|
||||
name: 'Undefined',
|
||||
count: undefinedLogItem.count,
|
||||
link: `https://iptv-org.github.io/iptv/${undefinedLogItem.filepath}`,
|
||||
children: new Collection()
|
||||
})
|
||||
}
|
||||
|
||||
items = items.orderBy(item => item.index)
|
||||
|
||||
const output = items
|
||||
.map(item => {
|
||||
let row = `- ${item.name} <code>${item.link}</code>`
|
||||
|
||||
item.children
|
||||
.orderBy(item => item.index)
|
||||
.forEach(item => {
|
||||
row += `\r\n\ - ${item.name} <code>${item.link}</code>`
|
||||
|
||||
item.children
|
||||
.orderBy(item => item.index)
|
||||
.forEach(item => {
|
||||
row += `\r\n\ - ${item.name} <code>${item.link}</code>`
|
||||
})
|
||||
})
|
||||
|
||||
return row
|
||||
})
|
||||
.join('\r\n')
|
||||
|
||||
const readmeStorage = new Storage(README_DIR)
|
||||
await readmeStorage.save('_countries.md', output)
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import { Storage, Collection, File } from '@freearhey/core'
|
||||
import { HTMLTable, LogParser, LogItem } from '../core'
|
||||
import { Country } from '../models'
|
||||
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
|
||||
import { Table } from './table'
|
||||
|
||||
export class CountryTable 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 parser = new LogParser()
|
||||
const logsStorage = new Storage(LOGS_DIR)
|
||||
const generatorsLog = await logsStorage.load('generators.log')
|
||||
const parsed = parser.parse(generatorsLog)
|
||||
|
||||
let data = new Collection()
|
||||
|
||||
parsed
|
||||
.filter((logItem: LogItem) => logItem.type === 'country')
|
||||
.forEach((logItem: LogItem) => {
|
||||
const file = new File(logItem.filepath)
|
||||
const code = file.name().toUpperCase()
|
||||
const [countryCode] = code.split('-') || ['', '']
|
||||
const country = countriesGroupedByCode.get(countryCode)
|
||||
|
||||
if (country) {
|
||||
data.add([
|
||||
country.name,
|
||||
`${country.flag} ${country.name}`,
|
||||
logItem.count,
|
||||
`<code>https://iptv-org.github.io/iptv/${logItem.filepath}</code>`
|
||||
])
|
||||
} else {
|
||||
data.add([
|
||||
'ZZ',
|
||||
'Undefined',
|
||||
logItem.count,
|
||||
`<code>https://iptv-org.github.io/iptv/${logItem.filepath}</code>`
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
data = data
|
||||
.orderBy(item => item[0])
|
||||
.map(item => {
|
||||
item.shift()
|
||||
return item
|
||||
})
|
||||
|
||||
const table = new HTMLTable(data.all(), [
|
||||
{ name: 'Country' },
|
||||
{ name: 'Channels', align: 'right' },
|
||||
{ name: 'Playlist', nowrap: true }
|
||||
])
|
||||
|
||||
const readmeStorage = new Storage(README_DIR)
|
||||
await readmeStorage.save('_countries.md', table.toString())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './categoryTable'
|
||||
export * from './countryTable'
|
||||
export * from './languageTable'
|
||||
export * from './regionTable'
|
||||
export * from './subdivisionTable'
|
||||
export * from './categoriesTable'
|
||||
export * from './countriesTable'
|
||||
export * from './languagesTable'
|
||||
export * from './regionsTable'
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { Storage, Collection, File } from '@freearhey/core'
|
||||
import { Storage, Collection, File, Dictionary } from '@freearhey/core'
|
||||
import { HTMLTable, LogParser, LogItem } from '../core'
|
||||
import { LOGS_DIR, README_DIR } from '../constants'
|
||||
import { Language } from '../models'
|
||||
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
|
||||
import { Table } from './table'
|
||||
|
||||
export class LanguageTable implements Table {
|
||||
constructor() {}
|
||||
type LanguagesTableProps = {
|
||||
languagesKeyByCode: Dictionary
|
||||
}
|
||||
|
||||
export class LanguagesTable implements Table {
|
||||
languagesKeyByCode: Dictionary
|
||||
|
||||
constructor({ languagesKeyByCode }: LanguagesTableProps) {
|
||||
this.languagesKeyByCode = languagesKeyByCode
|
||||
}
|
||||
|
||||
async make() {
|
||||
const dataStorage = new Storage(DATA_DIR)
|
||||
const languagesContent = await dataStorage.json('languages.json')
|
||||
const languages = new Collection(languagesContent).map(data => new Language(data))
|
||||
const languagesGroupedByCode = languages.keyBy((language: Language) => language.code)
|
||||
|
||||
const parser = new LogParser()
|
||||
const logsStorage = new Storage(LOGS_DIR)
|
||||
const generatorsLog = await logsStorage.load('generators.log')
|
||||
@@ -24,7 +27,7 @@ export class LanguageTable implements Table {
|
||||
.forEach((logItem: LogItem) => {
|
||||
const file = new File(logItem.filepath)
|
||||
const languageCode = file.name()
|
||||
const language: Language = languagesGroupedByCode.get(languageCode)
|
||||
const language: Language = this.languagesKeyByCode.get(languageCode)
|
||||
|
||||
data.add([
|
||||
language ? language.name : 'ZZ',
|
||||
@@ -1,62 +0,0 @@
|
||||
import { Storage, Collection, File } from '@freearhey/core'
|
||||
import { HTMLTable, LogParser, LogItem } from '../core'
|
||||
import { Region } from '../models'
|
||||
import { DATA_DIR, LOGS_DIR, README_DIR } from '../constants'
|
||||
import { Table } from './table'
|
||||
|
||||
export class RegionTable implements Table {
|
||||
constructor() {}
|
||||
|
||||
async make() {
|
||||
const dataStorage = new Storage(DATA_DIR)
|
||||
const regionsContent = await dataStorage.json('regions.json')
|
||||
const regions = new Collection(regionsContent).map(data => new Region(data))
|
||||
const regionsGroupedByCode = regions.keyBy((region: Region) => region.code)
|
||||
|
||||
const parser = new LogParser()
|
||||
const logsStorage = new Storage(LOGS_DIR)
|
||||
const generatorsLog = await logsStorage.load('generators.log')
|
||||
|
||||
let data = new Collection()
|
||||
parser
|
||||
.parse(generatorsLog)
|
||||
.filter((logItem: LogItem) => logItem.type === 'region')
|
||||
.forEach((logItem: LogItem) => {
|
||||
const file = new File(logItem.filepath)
|
||||
const regionCode = file.name().toUpperCase()
|
||||
const region: Region = regionsGroupedByCode.get(regionCode)
|
||||
|
||||
if (region) {
|
||||
data.add([
|
||||
region.name,
|
||||
region.name,
|
||||
logItem.count,
|
||||
`<code>https://iptv-org.github.io/iptv/${logItem.filepath}</code>`
|
||||
])
|
||||
} else {
|
||||
data.add([
|
||||
'ZZZ',
|
||||
'Undefined',
|
||||
logItem.count,
|
||||
`<code>https://iptv-org.github.io/iptv/${logItem.filepath}</code>`
|
||||
])
|
||||
}
|
||||
})
|
||||
|
||||
data = data
|
||||
.orderBy(item => item[0])
|
||||
.map(item => {
|
||||
item.shift()
|
||||
return item
|
||||
})
|
||||
|
||||
const table = new HTMLTable(data.all(), [
|
||||
{ name: 'Region', align: 'left' },
|
||||
{ name: 'Channels', align: 'right' },
|
||||
{ name: 'Playlist', align: 'left', nowrap: true }
|
||||
])
|
||||
|
||||
const readmeStorage = new Storage(README_DIR)
|
||||
await readmeStorage.save('_regions.md', table.toString())
|
||||
}
|
||||
}
|
||||
52
scripts/tables/regionsTable.ts
Normal file
52
scripts/tables/regionsTable.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Storage, Collection, File } from '@freearhey/core'
|
||||
import { HTMLTable, LogParser, LogItem } from '../core'
|
||||
import { LOGS_DIR, README_DIR } from '../constants'
|
||||
import { Region } from '../models'
|
||||
import { Table } from './table'
|
||||
|
||||
type RegionsTableProps = {
|
||||
regions: Collection
|
||||
}
|
||||
|
||||
export class RegionsTable implements Table {
|
||||
regions: Collection
|
||||
|
||||
constructor({ regions }: RegionsTableProps) {
|
||||
this.regions = regions
|
||||
}
|
||||
|
||||
async make() {
|
||||
const parser = new LogParser()
|
||||
const logsStorage = new Storage(LOGS_DIR)
|
||||
const generatorsLog = await logsStorage.load('generators.log')
|
||||
const parsed = parser.parse(generatorsLog)
|
||||
const logRegions = parsed.filter((logItem: LogItem) => logItem.type === 'region')
|
||||
|
||||
let items = new Collection()
|
||||
this.regions.forEach((region: Region) => {
|
||||
const logItem = logRegions.find(
|
||||
(logItem: LogItem) => logItem.filepath === `regions/${region.code.toLowerCase()}.m3u`
|
||||
)
|
||||
|
||||
if (!logItem) return
|
||||
|
||||
items.add({
|
||||
index: region.name,
|
||||
name: region.name,
|
||||
count: logItem.count,
|
||||
link: `https://iptv-org.github.io/iptv/${logItem.filepath}`
|
||||
})
|
||||
})
|
||||
|
||||
items = items.orderBy(item => item.index)
|
||||
|
||||
const output = items
|
||||
.map(item => {
|
||||
return `- ${item.name} <code>${item.link}</code>`
|
||||
})
|
||||
.join('\r\n')
|
||||
|
||||
const readmeStorage = new Storage(README_DIR)
|
||||
await readmeStorage.save('_regions.md', output)
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
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())
|
||||
}
|
||||
}
|
||||
20
scripts/types/city.d.ts
vendored
Normal file
20
scripts/types/city.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import { CountrySerializedData } from './country'
|
||||
import { SubdivisionSerializedData } from './subdivision'
|
||||
|
||||
export type CitySerializedData = {
|
||||
code: string
|
||||
name: string
|
||||
countryCode: string
|
||||
country?: CountrySerializedData
|
||||
subdivisionCode: string | null
|
||||
subdivision?: SubdivisionSerializedData
|
||||
wikidataId: string
|
||||
}
|
||||
|
||||
export type CityData = {
|
||||
code: string
|
||||
name: string
|
||||
country: string
|
||||
subdivision: string | null
|
||||
wikidata_id: string
|
||||
}
|
||||
1
scripts/types/dataLoader.d.ts
vendored
1
scripts/types/dataLoader.d.ts
vendored
@@ -17,4 +17,5 @@ export type DataLoaderData = {
|
||||
timezones: object | object[]
|
||||
guides: object | object[]
|
||||
streams: object | object[]
|
||||
cities: object | object[]
|
||||
}
|
||||
|
||||
3
scripts/types/dataProcessor.d.ts
vendored
3
scripts/types/dataProcessor.d.ts
vendored
@@ -15,6 +15,7 @@ export type DataProcessorData = {
|
||||
regionsKeyByCode: Dictionary
|
||||
blocklistRecords: Collection
|
||||
channelsKeyById: Dictionary
|
||||
citiesKeyByCode: Dictionary
|
||||
subdivisions: Collection
|
||||
categories: Collection
|
||||
countries: Collection
|
||||
@@ -23,6 +24,8 @@ export type DataProcessorData = {
|
||||
channels: Collection
|
||||
regions: Collection
|
||||
streams: Collection
|
||||
cities: Collection
|
||||
guides: Collection
|
||||
feeds: Collection
|
||||
logos: Collection
|
||||
}
|
||||
|
||||
8
scripts/types/feed.d.ts
vendored
8
scripts/types/feed.d.ts
vendored
@@ -1,12 +1,10 @@
|
||||
import { Collection } from '@freearhey/core'
|
||||
|
||||
export type FeedData = {
|
||||
channel: string
|
||||
id: string
|
||||
name: string
|
||||
is_main: boolean
|
||||
broadcast_area: Collection
|
||||
languages: Collection
|
||||
timezones: Collection
|
||||
broadcast_area: string[]
|
||||
languages: string[]
|
||||
timezones: string[]
|
||||
video_format: string
|
||||
}
|
||||
|
||||
5
scripts/types/region.d.ts
vendored
5
scripts/types/region.d.ts
vendored
@@ -1,9 +1,14 @@
|
||||
import { CitySerializedData } from './city'
|
||||
import { CountrySerializedData } from './country'
|
||||
import { SubdivisionSerializedData } from './subdivision'
|
||||
|
||||
export type RegionSerializedData = {
|
||||
code: string
|
||||
name: string
|
||||
countryCodes: string[]
|
||||
countries?: CountrySerializedData[]
|
||||
subdivisions?: SubdivisionSerializedData[]
|
||||
cities?: CitySerializedData[]
|
||||
}
|
||||
|
||||
export type RegionData = {
|
||||
|
||||
4
scripts/types/subdivision.d.ts
vendored
4
scripts/types/subdivision.d.ts
vendored
@@ -1,12 +1,16 @@
|
||||
import { CountrySerializedData } from './country'
|
||||
|
||||
export type SubdivisionSerializedData = {
|
||||
code: string
|
||||
name: string
|
||||
countryCode: string
|
||||
country?: CountrySerializedData
|
||||
parentCode: string | null
|
||||
}
|
||||
|
||||
export type SubdivisionData = {
|
||||
code: string
|
||||
name: string
|
||||
country: string
|
||||
parent: string | null
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#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
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
@@ -1,26 +1,14 @@
|
||||
#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]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8
|
||||
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Kyrgyzstan",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Russia",ЛДПР ТВ (1080p)
|
||||
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="Tajikistan",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8
|
||||
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Turkmenistan",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8
|
||||
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Uzbekistan",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.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
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="International",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.m3u8
|
||||
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="International",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.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
|
||||
|
||||
@@ -1,53 +1,139 @@
|
||||
#EXTM3U
|
||||
#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="Africa",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Africa",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.m3u8
|
||||
#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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="Americas",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Americas",Duna World (576i)
|
||||
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="Americas",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="Arab world",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Arab world",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Asia",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Asia",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.m3u8
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Asia",ЛДПР ТВ (1080p)
|
||||
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="Asia",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.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="Asia-Pacific",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Asia-Pacific",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Association of Southeast Asian Nations",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Association of Southeast Asian Nations",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Caribbean",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Caribbean",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Central America",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Central America",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Central Asia",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Central Asia",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.m3u8
|
||||
#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Central Asia",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.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="Commonwealth of Independent States",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Commonwealth of Independent States",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.m3u8
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Commonwealth of Independent States",ЛДПР ТВ (1080p)
|
||||
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="Commonwealth of Independent States",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8
|
||||
#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="https://i.imgur.com/BnhTn8i.png" group-title="Europe",ATV
|
||||
https://iptv-all.lanesh4d0w.repl.co/andorra/atv
|
||||
#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="Europe",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Europe",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.m3u8
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Europe",ЛДПР ТВ (1080p)
|
||||
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",ЭлТР (480p) [Not 24/7]
|
||||
http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8
|
||||
#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="https://i.imgur.com/BnhTn8i.png" group-title="Europe, the Middle East and Africa",ATV
|
||||
https://iptv-all.lanesh4d0w.repl.co/andorra/atv
|
||||
#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="Europe, the Middle East and Africa",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Europe, the Middle East and Africa",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.m3u8
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Europe, the Middle East and Africa",ЛДПР ТВ (1080p)
|
||||
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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="Hispanic America",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Hispanic America",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Latin America",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Latin America",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Latin America and the Caribbean",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Latin America and the Caribbean",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Maghreb",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Maghreb",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Middle East",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Middle East",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Middle East and North Africa",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Middle East and North Africa",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Nordics",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Nordics",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="North America",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="North America",Duna World (576i)
|
||||
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="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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="Northern America",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Northern America",Duna World (576i)
|
||||
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="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
|
||||
#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="Oceania",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="International",Duna World (576i)
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Oceania",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="South America",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="South America",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="South Asia",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="South Asia",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="Sub-Saharan Africa",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Sub-Saharan Africa",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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="West Africa",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="West Africa",Duna World (576i)
|
||||
http://146.59.85.40:89/dunaworld/index.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
|
||||
#KODIPROP:inputstream=inputstream.adaptive
|
||||
#KODIPROP:inputstream.adaptive.manifest_type=mpd
|
||||
#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha
|
||||
#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}|
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|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"
|
||||
#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="https://i.imgur.com/CnhTn8i.png" group-title="Undefined",ATV HD
|
||||
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="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
|
||||
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
#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]
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
@@ -1,4 +1,8 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
#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]
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#EXTM3U
|
||||
#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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
#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]
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#EXTM3U
|
||||
#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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
#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]
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#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="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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="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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#EXTM3U
|
||||
#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
|
||||
#KODIPROP:inputstream=inputstream.adaptive
|
||||
#KODIPROP:inputstream.adaptive.manifest_type=mpd
|
||||
#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha
|
||||
#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}|
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|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"
|
||||
#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="https://i.imgur.com/CnhTn8i.png" group-title="Undefined",ATV HD
|
||||
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="https://i.imgur.com/ciTJrnl.png" group-title="Undefined",Zoo (720p)
|
||||
https://iptv-all.lanesh4d0w.repl.co/andorra/zoo
|
||||
@@ -1 +1,5 @@
|
||||
#EXTM3U
|
||||
#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="General;News",BBC News HD
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#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
|
||||
|
||||
@@ -5,227 +5,71 @@
|
||||
{"type":"raw","filepath":"raw/uk.m3u","count":1}
|
||||
{"type":"raw","filepath":"raw/unsorted.m3u","count":4}
|
||||
{"type":"category","filepath":"categories/auto.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/comedy.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/business.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/cooking.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/animation.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/business.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/cooking.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/culture.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/education.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/general.m3u","count":3}
|
||||
{"type":"category","filepath":"categories/documentary.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/comedy.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/family.m3u","count":0}
|
||||
{"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/legislative.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/entertainment.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/music.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/outdoor.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/general.m3u","count":3}
|
||||
{"type":"category","filepath":"categories/lifestyle.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/relax.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/religious.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/movies.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/shop.m3u","count":0}
|
||||
{"type":"category","filepath":"categories/science.m3u","count":0}
|
||||
{"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/weather.m3u","count":1}
|
||||
{"type":"category","filepath":"categories/travel.m3u","count":0}
|
||||
{"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":"category","filepath":"categories/undefined.m3u","count":7}
|
||||
{"type":"language","filepath":"languages/cat.m3u","count":1}
|
||||
{"type":"language","filepath":"languages/rus.m3u","count":1}
|
||||
{"type":"language","filepath":"languages/eng.m3u","count":1}
|
||||
{"type":"language","filepath":"languages/rus.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/ca.m3u","count":1}
|
||||
{"type":"country","filepath":"countries/int.m3u","count":3}
|
||||
{"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":2}
|
||||
{"type":"region","filepath":"regions/asean.m3u","count":0}
|
||||
{"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/cas.m3u","count":1}
|
||||
{"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/mena.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/asia.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/oce.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":"subdivision","filepath":"subdivisions/ca-on.m3u","count":1}
|
||||
{"type":"city","filepath":"cities/adcan.m3u","count":1}
|
||||
{"type":"region","filepath":"regions/afr.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/amer.m3u","count":4}
|
||||
{"type":"region","filepath":"regions/apac.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/carib.m3u","count":2}
|
||||
{"type":"source","filepath":"sources/in.m3u","count":1}
|
||||
{"type":"region","filepath":"regions/asean.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/arab.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/asia.m3u","count":4}
|
||||
{"type":"region","filepath":"regions/emea.m3u","count":5}
|
||||
{"type":"region","filepath":"regions/cenamer.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/cis.m3u","count":4}
|
||||
{"type":"region","filepath":"regions/cas.m3u","count":3}
|
||||
{"type":"region","filepath":"regions/hispam.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/maghreb.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/eur.m3u","count":5}
|
||||
{"type":"region","filepath":"regions/lac.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/mideast.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/noram.m3u","count":4}
|
||||
{"type":"region","filepath":"regions/latam.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/oce.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/nam.m3u","count":4}
|
||||
{"type":"region","filepath":"regions/southam.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/mena.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/wafr.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/nord.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/sas.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/ssa.m3u","count":2}
|
||||
{"type":"source","filepath":"sources/unsorted.m3u","count":4}
|
||||
{"type":"source","filepath":"sources/ca.m3u","count":2}
|
||||
{"type":"source","filepath":"sources/ad.m3u","count":3}
|
||||
@@ -233,6 +77,6 @@
|
||||
{"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.country.m3u","count":9}
|
||||
{"type":"index","filepath":"index.language.m3u","count":11}
|
||||
{"type":"index","filepath":"index.region.m3u","count":23}
|
||||
{"type":"index","filepath":"index.region.m3u","count":69}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# Playlists
|
||||
## Playlists
|
||||
|
||||
There are several versions of playlists that differ in the way they are grouped. As of January 30th, 2024, we have stopped distributing NSFW channels. For more information, please look at [this issue](https://github.com/iptv-org/iptv/issues/15723).
|
||||
|
||||
### Grouped by category
|
||||
|
||||
Playlists in which channels are grouped by category.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
Playlist in which each channel has its _category_ as a group title:
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.category.m3u
|
||||
```
|
||||
@@ -56,12 +58,12 @@ Same thing, but split up into separate files:
|
||||
|
||||
### Grouped by language
|
||||
|
||||
Playlists in which channels are grouped by the language in which they are broadcast.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
Playlist in which each channel has its _language_ as a group title:
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.language.m3u
|
||||
```
|
||||
@@ -84,114 +86,77 @@ Same thing, but split up into separate files:
|
||||
|
||||
</details>
|
||||
|
||||
### Grouped by country
|
||||
### Grouped by broadcast area
|
||||
|
||||
Playlists in which channels are grouped by broadcast area.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
Playlist in which each channel has its _country_ as a group title:
|
||||
#### Countries
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.country.m3u
|
||||
https://iptv-org.github.io/iptv/index.countries.m3u
|
||||
```
|
||||
|
||||
Same thing, but split up into separate files:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th align="left">Country</th><th align="left">Channels</th><th align="left">Playlist</th></tr>
|
||||
</thead>
|
||||
<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>🇨🇻 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>
|
||||
<tr><td>🇷🇴 Romania</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/ro.m3u</code></td></tr>
|
||||
<tr><td>🇷🇺 Russia</td><td align="right">2</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/ru.m3u</code></td></tr>
|
||||
<tr><td>🇷🇼 Rwanda</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/rw.m3u</code></td></tr>
|
||||
<tr><td>🇧🇱 Saint Barthélemy</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/bl.m3u</code></td></tr>
|
||||
<tr><td>🇸🇭 Saint Helena</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/sh.m3u</code></td></tr>
|
||||
<tr><td>🇰🇳 Saint Kitts and Nevis</td><td align="right">1</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/kn.m3u</code></td></tr>
|
||||
<tr><td>Undefined</td><td align="right">2</td><td nowrap><code>https://iptv-org.github.io/iptv/countries/undefined.m3u</code></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
- 🇦🇩 Andorra <code>https://iptv-org.github.io/iptv/countries/ad.m3u</code>
|
||||
- Canillo <code>https://iptv-org.github.io/iptv/subdivisions/ad-02.m3u</code>
|
||||
- Canillo <code>https://iptv-org.github.io/iptv/cities/adcan.m3u</code>
|
||||
- 🇨🇲 Cameroon <code>https://iptv-org.github.io/iptv/countries/cm.m3u</code>
|
||||
- 🇨🇦 Canada <code>https://iptv-org.github.io/iptv/countries/ca.m3u</code>
|
||||
- Ontario <code>https://iptv-org.github.io/iptv/subdivisions/ca-on.m3u</code>
|
||||
- 🇨🇻 Cape Verde <code>https://iptv-org.github.io/iptv/countries/cv.m3u</code>
|
||||
- 🇭🇰 Hong Kong <code>https://iptv-org.github.io/iptv/countries/hk.m3u</code>
|
||||
- Sai Kung <code>https://iptv-org.github.io/iptv/cities/hk9sk.m3u</code>
|
||||
- 🇨🇬 Republic of the Congo <code>https://iptv-org.github.io/iptv/countries/cg.m3u</code>
|
||||
- 🇷🇪 Réunion <code>https://iptv-org.github.io/iptv/countries/re.m3u</code>
|
||||
- 🇷🇴 Romania <code>https://iptv-org.github.io/iptv/countries/ro.m3u</code>
|
||||
- 🇷🇺 Russia <code>https://iptv-org.github.io/iptv/countries/ru.m3u</code>
|
||||
- 🇷🇼 Rwanda <code>https://iptv-org.github.io/iptv/countries/rw.m3u</code>
|
||||
- 🇧🇱 Saint Barthélemy <code>https://iptv-org.github.io/iptv/countries/bl.m3u</code>
|
||||
- 🇸🇭 Saint Helena <code>https://iptv-org.github.io/iptv/countries/sh.m3u</code>
|
||||
- 🇰🇳 Saint Kitts and Nevis <code>https://iptv-org.github.io/iptv/countries/kn.m3u</code>
|
||||
- 🌐 International <code>https://iptv-org.github.io/iptv/countries/int.m3u</code>
|
||||
- Undefined <code>https://iptv-org.github.io/iptv/countries/undefined.m3u</code>
|
||||
|
||||
</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>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
Playlist in which each channel has its _region_ as a group title:
|
||||
#### Regions
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.region.m3u
|
||||
https://iptv-org.github.io/iptv/index.regions.m3u
|
||||
```
|
||||
|
||||
Same thing, but split up into separate files:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th align="left">Region</th><th align="left">Channels</th><th align="left">Playlist</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td align="left">Africa</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/afr.m3u</code></td></tr>
|
||||
<tr><td align="left">Americas</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/amer.m3u</code></td></tr>
|
||||
<tr><td align="left">Arab world</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/arab.m3u</code></td></tr>
|
||||
<tr><td align="left">Asia</td><td align="right">2</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/asia.m3u</code></td></tr>
|
||||
<tr><td align="left">Asia-Pacific</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/apac.m3u</code></td></tr>
|
||||
<tr><td align="left">Association of Southeast Asian Nations</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/asean.m3u</code></td></tr>
|
||||
<tr><td align="left">Caribbean</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/carib.m3u</code></td></tr>
|
||||
<tr><td align="left">Central America</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/cenamer.m3u</code></td></tr>
|
||||
<tr><td align="left">Central Asia</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/cas.m3u</code></td></tr>
|
||||
<tr><td align="left">Commonwealth of Independent States</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/cis.m3u</code></td></tr>
|
||||
<tr><td align="left">Europe</td><td align="right">3</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/eur.m3u</code></td></tr>
|
||||
<tr><td align="left">Europe, the Middle East and Africa</td><td align="right">3</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/emea.m3u</code></td></tr>
|
||||
<tr><td align="left">Hispanic America</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/hispam.m3u</code></td></tr>
|
||||
<tr><td align="left">Latin America</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/latam.m3u</code></td></tr>
|
||||
<tr><td align="left">Latin America and the Caribbean</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/lac.m3u</code></td></tr>
|
||||
<tr><td align="left">Maghreb</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/maghreb.m3u</code></td></tr>
|
||||
<tr><td align="left">Middle East</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/mideast.m3u</code></td></tr>
|
||||
<tr><td align="left">Middle East and North Africa</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/mena.m3u</code></td></tr>
|
||||
<tr><td align="left">Nordics</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/nord.m3u</code></td></tr>
|
||||
<tr><td align="left">North America</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/noram.m3u</code></td></tr>
|
||||
<tr><td align="left">Northern America</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/nam.m3u</code></td></tr>
|
||||
<tr><td align="left">Oceania</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/oce.m3u</code></td></tr>
|
||||
<tr><td align="left">South America</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/southam.m3u</code></td></tr>
|
||||
<tr><td align="left">South Asia</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/sas.m3u</code></td></tr>
|
||||
<tr><td align="left">Sub-Saharan Africa</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/ssa.m3u</code></td></tr>
|
||||
<tr><td align="left">West Africa</td><td align="right">0</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/wafr.m3u</code></td></tr>
|
||||
<tr><td align="left">Worldwide</td><td align="right">1</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/int.m3u</code></td></tr>
|
||||
<tr><td align="left">Undefined</td><td align="right">2</td><td align="left" nowrap><code>https://iptv-org.github.io/iptv/regions/undefined.m3u</code></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
- Africa <code>https://iptv-org.github.io/iptv/regions/afr.m3u</code>
|
||||
- Americas <code>https://iptv-org.github.io/iptv/regions/amer.m3u</code>
|
||||
- Arab world <code>https://iptv-org.github.io/iptv/regions/arab.m3u</code>
|
||||
- Asia <code>https://iptv-org.github.io/iptv/regions/asia.m3u</code>
|
||||
- Asia-Pacific <code>https://iptv-org.github.io/iptv/regions/apac.m3u</code>
|
||||
- Association of Southeast Asian Nations <code>https://iptv-org.github.io/iptv/regions/asean.m3u</code>
|
||||
- Caribbean <code>https://iptv-org.github.io/iptv/regions/carib.m3u</code>
|
||||
- Central America <code>https://iptv-org.github.io/iptv/regions/cenamer.m3u</code>
|
||||
- Central Asia <code>https://iptv-org.github.io/iptv/regions/cas.m3u</code>
|
||||
- Commonwealth of Independent States <code>https://iptv-org.github.io/iptv/regions/cis.m3u</code>
|
||||
- Europe <code>https://iptv-org.github.io/iptv/regions/eur.m3u</code>
|
||||
- Europe, the Middle East and Africa <code>https://iptv-org.github.io/iptv/regions/emea.m3u</code>
|
||||
- Hispanic America <code>https://iptv-org.github.io/iptv/regions/hispam.m3u</code>
|
||||
- Latin America <code>https://iptv-org.github.io/iptv/regions/latam.m3u</code>
|
||||
- Latin America and the Caribbean <code>https://iptv-org.github.io/iptv/regions/lac.m3u</code>
|
||||
- Maghreb <code>https://iptv-org.github.io/iptv/regions/maghreb.m3u</code>
|
||||
- Middle East <code>https://iptv-org.github.io/iptv/regions/mideast.m3u</code>
|
||||
- Middle East and North Africa <code>https://iptv-org.github.io/iptv/regions/mena.m3u</code>
|
||||
- Nordics <code>https://iptv-org.github.io/iptv/regions/nord.m3u</code>
|
||||
- North America <code>https://iptv-org.github.io/iptv/regions/noram.m3u</code>
|
||||
- Northern America <code>https://iptv-org.github.io/iptv/regions/nam.m3u</code>
|
||||
- Oceania <code>https://iptv-org.github.io/iptv/regions/oce.m3u</code>
|
||||
- South America <code>https://iptv-org.github.io/iptv/regions/southam.m3u</code>
|
||||
- South Asia <code>https://iptv-org.github.io/iptv/regions/sas.m3u</code>
|
||||
- Sub-Saharan Africa <code>https://iptv-org.github.io/iptv/regions/ssa.m3u</code>
|
||||
- West Africa <code>https://iptv-org.github.io/iptv/regions/wafr.m3u</code>
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
1
tests/__data__/input/data/cities.json
Normal file
1
tests/__data__/input/data/cities.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"country":"AD","subdivision":"AD-02","code":"ADCAN","name":"Canillo","wikidata_id":"Q386802"},{"country":"CA","subdivision":"CA-ON","code":"CAAAC","name":"Ailsa Craig","wikidata_id":"Q65963197"},{"country":"HK","subdivision":null,"code":"HK9SK","name":"Sai Kung","wikidata_id":"Q206377"}]
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "SD",
|
||||
"is_main": true,
|
||||
"broadcast_area": [
|
||||
"c/AD"
|
||||
"ct/ADCAN"
|
||||
],
|
||||
"languages": [
|
||||
"cat"
|
||||
@@ -37,7 +37,7 @@
|
||||
"name": "SD",
|
||||
"is_main": true,
|
||||
"broadcast_area": [
|
||||
"r/INT"
|
||||
"r/WW"
|
||||
],
|
||||
"languages": [
|
||||
"eng"
|
||||
@@ -180,7 +180,7 @@
|
||||
"name": "SD",
|
||||
"is_main": true,
|
||||
"broadcast_area": [
|
||||
"r/INT"
|
||||
"r/WW"
|
||||
],
|
||||
"languages": [
|
||||
"nld"
|
||||
@@ -805,7 +805,7 @@
|
||||
"name": "SD",
|
||||
"is_main": true,
|
||||
"broadcast_area": [
|
||||
"r/INT"
|
||||
"r/WW"
|
||||
],
|
||||
"languages": [],
|
||||
"timezones": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"build" : "tests/__data__/output/playlists.md",
|
||||
"files" : ["tests/__data__/output/.readme/template.md"]
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
# Playlists
|
||||
|
||||
### Grouped by category
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
Playlist in which each channel has its _category_ as a group title:
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.category.m3u
|
||||
```
|
||||
|
||||
Same thing, but split up into separate files:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
#include "tests/__data__/output/.readme/_categories.md"
|
||||
|
||||
</details>
|
||||
|
||||
### Grouped by language
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
Playlist in which each channel has its _language_ as a group title:
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.language.m3u
|
||||
```
|
||||
|
||||
Same thing, but split up into separate files:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
#include "tests/__data__/output/.readme/_languages.md"
|
||||
|
||||
</details>
|
||||
|
||||
### Grouped by country
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
Playlist in which each channel has its _country_ as a group title:
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.country.m3u
|
||||
```
|
||||
|
||||
Same thing, but split up into separate files:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
#include "tests/__data__/output/.readme/_countries.md"
|
||||
|
||||
</details>
|
||||
|
||||
### Grouped by subdivision
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
#include "tests/__data__/output/.readme/_subdivisions.md"
|
||||
|
||||
</details>
|
||||
|
||||
### Grouped by region
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
Playlist in which each channel has its _region_ as a group title:
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/index.region.m3u
|
||||
```
|
||||
|
||||
Same thing, but split up into separate files:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
#include "tests/__data__/output/.readme/_regions.md"
|
||||
|
||||
</details>
|
||||
|
||||
### Grouped by sources
|
||||
|
||||
Playlists in which channels are grouped by broadcast source.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
<br>
|
||||
|
||||
To use the playlist, simply replace `<FILENAME>` in the link below with the name of one of the files in the [streams](streams) folder.
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/sources/<FILENAME>.m3u
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Also, any of our internal playlists are available in raw form (without any filtering or sorting) at this link:
|
||||
|
||||
```
|
||||
https://iptv-org.github.io/iptv/raw/<FILENAME>.m3u
|
||||
```
|
||||
@@ -38,9 +38,12 @@
|
||||
{"type":"country","filepath":"countries/cg.m3u","count":1}
|
||||
{"type":"country","filepath":"countries/ro.m3u","count":1}
|
||||
{"type":"subdivision","filepath":"subdivisions/ca-on.m3u","count":1}
|
||||
{"type":"city","filepath":"cities/adcan.m3u","count":1}
|
||||
{"type":"city","filepath":"cities/hk9sk.m3u","count":1}
|
||||
{"type":"country","filepath":"countries/ru.m3u","count":2}
|
||||
{"type":"country","filepath":"countries/rw.m3u","count":1}
|
||||
{"type":"country","filepath":"countries/re.m3u","count":1}
|
||||
{"type":"country","filepath":"countries/int.m3u","count":3}
|
||||
{"type":"country","filepath":"countries/undefined.m3u","count":2}
|
||||
{"type":"country","filepath":"countries/bl.m3u","count":1}
|
||||
{"type":"country","filepath":"countries/sh.m3u","count":1}
|
||||
@@ -74,7 +77,6 @@
|
||||
{"type":"region","filepath":"regions/oce.m3u","count":0}
|
||||
{"type":"region","filepath":"regions/undefined.m3u","count":2}
|
||||
{"type":"region","filepath":"regions/sas.m3u","count":1}
|
||||
{"type":"region","filepath":"regions/int.m3u","count":1}
|
||||
{"type":"region","filepath":"regions/southam.m3u","count":0}
|
||||
{"type":"region","filepath":"regions/ssa.m3u","count":0}
|
||||
{"type":"region","filepath":"regions/wafr.m3u","count":0}
|
||||
@@ -4,7 +4,8 @@ import { EOL } from 'node:os'
|
||||
import * as fs from 'fs-extra'
|
||||
import * as glob from 'glob'
|
||||
|
||||
const ENV_VAR = 'cross-env STREAMS_DIR=tests/__data__/input/playlist_generate DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output/.gh-pages LOGS_DIR=tests/__data__/output/logs'
|
||||
const ENV_VAR =
|
||||
'cross-env STREAMS_DIR=tests/__data__/input/playlist_generate DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output/.gh-pages LOGS_DIR=tests/__data__/output/logs'
|
||||
|
||||
beforeEach(() => {
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
@@ -26,7 +27,7 @@ describe('playlist:generate', () => {
|
||||
})
|
||||
|
||||
playlists.forEach((filepath: string) => {
|
||||
expect(content(`tests/__data__/output/${filepath}`)).toBe(
|
||||
expect(content(`tests/__data__/output/${filepath}`), filepath).toBe(
|
||||
content(`tests/__data__/expected/playlist_generate/${filepath}`)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -2,19 +2,11 @@ import { pathToFileURL } from 'node:url'
|
||||
import { execSync } from 'child_process'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
const ENV_VAR = 'cross-env DATA_DIR=tests/__data__/input/data LOGS_DIR=tests/__data__/input/readme_update README_DIR=tests/__data__/output/.readme'
|
||||
const ENV_VAR =
|
||||
'cross-env DATA_DIR=tests/__data__/input/data LOGS_DIR=tests/__data__/input/readme_update ROOT_DIR=tests/__data__/output'
|
||||
|
||||
beforeEach(() => {
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
fs.mkdirSync('tests/__data__/output/.readme')
|
||||
fs.copyFileSync(
|
||||
'tests/__data__/input/readme_update/.readme/config.json',
|
||||
'tests/__data__/output/.readme/config.json'
|
||||
)
|
||||
fs.copyFileSync(
|
||||
'tests/__data__/input/readme_update/.readme/template.md',
|
||||
'tests/__data__/output/.readme/template.md'
|
||||
)
|
||||
})
|
||||
|
||||
describe('readme:update', () => {
|
||||
@@ -23,8 +15,8 @@ describe('readme:update', () => {
|
||||
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||
|
||||
expect(content('tests/__data__/output/playlists.md')).toEqual(
|
||||
content('tests/__data__/expected/readme_update/playlists.md')
|
||||
expect(content('tests/__data__/output/PLAYLISTS.md')).toEqual(
|
||||
content('tests/__data__/expected/readme_update/PLAYLISTS.md')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user