Merge pull request #26208 from iptv-org/patch-2025.08.3

Patch 2025.08.3
This commit is contained in:
Sphinx
2025-08-28 00:09:47 -04:00
committed by GitHub
82 changed files with 1232 additions and 1046 deletions

3
.readme/.gitignore vendored
View File

@@ -1,5 +1,4 @@
_categories.md
_countries.md
_languages.md
_regions.md
_subdivisions.md
_regions.md

View File

@@ -1,4 +0,0 @@
{
"build" : "PLAYLISTS.md",
"files" : ["./.readme/template.md"]
}

View File

@@ -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
View File

@@ -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",

View File

@@ -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",

View File

@@ -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')
])
}

View File

@@ -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,

View File

@@ -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()
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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')

View 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
)
})
}
}

View File

@@ -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'

View File

@@ -1,4 +1,5 @@
export * from './categoriesGenerator'
export * from './citiesGenerator'
export * from './countriesGenerator'
export * from './indexCategoryGenerator'
export * from './indexCountryGenerator'

View File

@@ -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

View File

@@ -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'

View File

@@ -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
)
}
}

View File

@@ -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
View 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
}
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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'

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 }

View 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)
}
}

View File

@@ -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())
}
}

View File

@@ -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'

View File

@@ -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',

View File

@@ -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())
}
}

View 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)
}
}

View File

@@ -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
View 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
}

View File

@@ -17,4 +17,5 @@ export type DataLoaderData = {
timezones: object | object[]
guides: object | object[]
streams: object | object[]
cities: object | object[]
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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 = {

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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>

View 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"}]

View File

@@ -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

View File

@@ -1,4 +0,0 @@
{
"build" : "tests/__data__/output/playlists.md",
"files" : ["tests/__data__/output/.readme/template.md"]
}

View File

@@ -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
```

View File

@@ -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}

View File

@@ -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}`)
)
})

View File

@@ -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')
)
})
})