diff --git a/.readme/.gitignore b/.readme/.gitignore index e57faaff6..a2ac44134 100644 --- a/.readme/.gitignore +++ b/.readme/.gitignore @@ -1,5 +1,4 @@ _categories.md _countries.md _languages.md -_regions.md -_subdivisions.md \ No newline at end of file +_regions.md \ No newline at end of file diff --git a/.readme/config.json b/.readme/config.json deleted file mode 100644 index ff55cfec7..000000000 --- a/.readme/config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "build" : "PLAYLISTS.md", - "files" : ["./.readme/template.md"] -} \ No newline at end of file diff --git a/.readme/template.md b/.readme/template.md index dc5cad228..c7b26655e 100644 --- a/.readme/template.md +++ b/.readme/template.md @@ -40,16 +40,17 @@ Same thing, but split up into separate files: -### 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.
Expand -
+ +#### 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: #include "./.readme/_countries.md" -
- -### Grouped by subdivision - -Playlists in which channels are grouped by subdivision for which they are broadcasted. - -
-Expand -
- - -#include "./.readme/_subdivisions.md" - -
- -### Grouped by region - -Playlists in which channels are grouped by the region for which they are broadcasted. - -
-Expand -
+#### 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: diff --git a/package-lock.json b/package-lock.json index a96f29a72..76b8c282a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index f9fafdaee..4c68d9e66 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/commands/api/load.ts b/scripts/commands/api/load.ts index e4d89120a..39cf0a2e8 100644 --- a/scripts/commands/api/load.ts +++ b/scripts/commands/api/load.ts @@ -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') ]) } diff --git a/scripts/commands/playlist/generate.ts b/scripts/commands/playlist/generate.ts index 7165c4b98..58677a59c 100644 --- a/scripts/commands/playlist/generate.ts +++ b/scripts/commands/playlist/generate.ts @@ -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, diff --git a/scripts/commands/readme/update.ts b/scripts/commands/readme/update.ts index 86eddfe36..d47f6ba48 100644 --- a/scripts/commands/readme/update.ts +++ b/scripts/commands/readme/update.ts @@ -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() } diff --git a/scripts/core/dataLoader.ts b/scripts/core/dataLoader.ts index d23264769..89b45c00c 100644 --- a/scripts/core/dataLoader.ts +++ b/scripts/core/dataLoader.ts @@ -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 } } diff --git a/scripts/core/dataProcessor.ts b/scripts/core/dataProcessor.ts index d36569fd5..dfb796ba7 100644 --- a/scripts/core/dataProcessor.ts +++ b/scripts/core/dataProcessor.ts @@ -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 diff --git a/scripts/core/markdown.ts b/scripts/core/markdown.ts index 36834d8b7..e22999940 100644 --- a/scripts/core/markdown.ts +++ b/scripts/core/markdown.ts @@ -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') diff --git a/scripts/generators/citiesGenerator.ts b/scripts/generators/citiesGenerator.ts new file mode 100644 index 000000000..7d0891fbc --- /dev/null +++ b/scripts/generators/citiesGenerator.ts @@ -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 { + 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 + ) + }) + } +} diff --git a/scripts/generators/countriesGenerator.ts b/scripts/generators/countriesGenerator.ts index 4cd539dea..39d761258 100644 --- a/scripts/generators/countriesGenerator.ts +++ b/scripts/generators/countriesGenerator.ts @@ -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' diff --git a/scripts/generators/index.ts b/scripts/generators/index.ts index e01127ba1..7f09a6715 100644 --- a/scripts/generators/index.ts +++ b/scripts/generators/index.ts @@ -1,4 +1,5 @@ export * from './categoriesGenerator' +export * from './citiesGenerator' export * from './countriesGenerator' export * from './indexCategoryGenerator' export * from './indexCountryGenerator' diff --git a/scripts/generators/indexCountryGenerator.ts b/scripts/generators/indexCountryGenerator.ts index 43e55c2e2..b476d828a 100644 --- a/scripts/generators/indexCountryGenerator.ts +++ b/scripts/generators/indexCountryGenerator.ts @@ -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 diff --git a/scripts/generators/indexRegionGenerator.ts b/scripts/generators/indexRegionGenerator.ts index 3155d37d3..27a9ae0fe 100644 --- a/scripts/generators/indexRegionGenerator.ts +++ b/scripts/generators/indexRegionGenerator.ts @@ -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' diff --git a/scripts/generators/regionsGenerator.ts b/scripts/generators/regionsGenerator.ts index 6711c67ff..60dab3d61 100644 --- a/scripts/generators/regionsGenerator.ts +++ b/scripts/generators/regionsGenerator.ts @@ -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 - ) } } diff --git a/scripts/models/broadcastArea.ts b/scripts/models/broadcastArea.ts index 2b96b7f91..fa5b43f0e 100644 --- a/scripts/models/broadcastArea.ts +++ b/scripts/models/broadcastArea.ts @@ -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) } } diff --git a/scripts/models/city.ts b/scripts/models/city.ts new file mode 100644 index 000000000..6ce9173ac --- /dev/null +++ b/scripts/models/city.ts @@ -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 + } +} diff --git a/scripts/models/country.ts b/scripts/models/country.ts index 780c4413f..b9699f723 100644 --- a/scripts/models/country.ts +++ b/scripts/models/country.ts @@ -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, diff --git a/scripts/models/feed.ts b/scripts/models/feed.ts index f42c4af91..a6713e265 100644 --- a/scripts/models/feed.ts +++ b/scripts/models/feed.ts @@ -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 { diff --git a/scripts/models/index.ts b/scripts/models/index.ts index 4e11d28b9..e8fe34628 100644 --- a/scripts/models/index.ts +++ b/scripts/models/index.ts @@ -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' diff --git a/scripts/models/region.ts b/scripts/models/region.ts index ace44bc52..5fe52ad5a 100644 --- a/scripts/models/region.ts +++ b/scripts/models/region.ts @@ -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 } diff --git a/scripts/models/stream.ts b/scripts/models/stream.ts index cd734493b..a465081ea 100644 --- a/scripts/models/stream.ts +++ b/scripts/models/stream.ts @@ -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 } diff --git a/scripts/models/subdivision.ts b/scripts/models/subdivision.ts index b43d1c88d..92cfdc9d6 100644 --- a/scripts/models/subdivision.ts +++ b/scripts/models/subdivision.ts @@ -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 } diff --git a/scripts/tables/categoryTable.ts b/scripts/tables/categoriesTable.ts similarity index 61% rename from scripts/tables/categoryTable.ts rename to scripts/tables/categoriesTable.ts index f82f3ffd4..0b763e416 100644 --- a/scripts/tables/categoryTable.ts +++ b/scripts/tables/categoriesTable.ts @@ -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 } diff --git a/scripts/tables/countriesTable.ts b/scripts/tables/countriesTable.ts new file mode 100644 index 000000000..0be11c1b3 --- /dev/null +++ b/scripts/tables/countriesTable.ts @@ -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} ${item.link}` + + item.children + .orderBy(item => item.index) + .forEach(item => { + row += `\r\n\ - ${item.name} ${item.link}` + + item.children + .orderBy(item => item.index) + .forEach(item => { + row += `\r\n\ - ${item.name} ${item.link}` + }) + }) + + return row + }) + .join('\r\n') + + const readmeStorage = new Storage(README_DIR) + await readmeStorage.save('_countries.md', output) + } +} diff --git a/scripts/tables/countryTable.ts b/scripts/tables/countryTable.ts deleted file mode 100644 index d0075e62c..000000000 --- a/scripts/tables/countryTable.ts +++ /dev/null @@ -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, - `https://iptv-org.github.io/iptv/${logItem.filepath}` - ]) - } else { - data.add([ - 'ZZ', - 'Undefined', - logItem.count, - `https://iptv-org.github.io/iptv/${logItem.filepath}` - ]) - } - }) - - 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()) - } -} diff --git a/scripts/tables/index.ts b/scripts/tables/index.ts index 6da33e822..f25c0a3a2 100644 --- a/scripts/tables/index.ts +++ b/scripts/tables/index.ts @@ -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' diff --git a/scripts/tables/languageTable.ts b/scripts/tables/languagesTable.ts similarity index 68% rename from scripts/tables/languageTable.ts rename to scripts/tables/languagesTable.ts index 2014ba676..762190745 100644 --- a/scripts/tables/languageTable.ts +++ b/scripts/tables/languagesTable.ts @@ -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', diff --git a/scripts/tables/regionTable.ts b/scripts/tables/regionTable.ts deleted file mode 100644 index 84eeaaa4a..000000000 --- a/scripts/tables/regionTable.ts +++ /dev/null @@ -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, - `https://iptv-org.github.io/iptv/${logItem.filepath}` - ]) - } else { - data.add([ - 'ZZZ', - 'Undefined', - logItem.count, - `https://iptv-org.github.io/iptv/${logItem.filepath}` - ]) - } - }) - - 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()) - } -} diff --git a/scripts/tables/regionsTable.ts b/scripts/tables/regionsTable.ts new file mode 100644 index 000000000..25f2e71bc --- /dev/null +++ b/scripts/tables/regionsTable.ts @@ -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} ${item.link}` + }) + .join('\r\n') + + const readmeStorage = new Storage(README_DIR) + await readmeStorage.save('_regions.md', output) + } +} diff --git a/scripts/tables/subdivisionTable.ts b/scripts/tables/subdivisionTable.ts deleted file mode 100644 index 925d9094e..000000000 --- a/scripts/tables/subdivisionTable.ts +++ /dev/null @@ -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
\r\n${country.name}\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, - `https://iptv-org.github.io/iptv/${logItem.filepath}` - ]) - } - } - }) - - const table = new HTMLTable(data.all(), [ - { name: 'Subdivision' }, - { name: 'Channels', align: 'right' }, - { name: 'Playlist', nowrap: true } - ]) - - output += table.toString() - - output += '\r\n
' - }) - - const readmeStorage = new Storage(README_DIR) - await readmeStorage.save('_subdivisions.md', output.trim()) - } -} diff --git a/scripts/types/city.d.ts b/scripts/types/city.d.ts new file mode 100644 index 000000000..5c33ba5a9 --- /dev/null +++ b/scripts/types/city.d.ts @@ -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 +} diff --git a/scripts/types/dataLoader.d.ts b/scripts/types/dataLoader.d.ts index ee7ab0f93..708361de9 100644 --- a/scripts/types/dataLoader.d.ts +++ b/scripts/types/dataLoader.d.ts @@ -17,4 +17,5 @@ export type DataLoaderData = { timezones: object | object[] guides: object | object[] streams: object | object[] + cities: object | object[] } diff --git a/scripts/types/dataProcessor.d.ts b/scripts/types/dataProcessor.d.ts index 25f21d1aa..bc76dc28b 100644 --- a/scripts/types/dataProcessor.d.ts +++ b/scripts/types/dataProcessor.d.ts @@ -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 } diff --git a/scripts/types/feed.d.ts b/scripts/types/feed.d.ts index 5c6722dde..ef4aea466 100644 --- a/scripts/types/feed.d.ts +++ b/scripts/types/feed.d.ts @@ -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 } diff --git a/scripts/types/region.d.ts b/scripts/types/region.d.ts index e6773429e..798224ee7 100644 --- a/scripts/types/region.d.ts +++ b/scripts/types/region.d.ts @@ -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 = { diff --git a/scripts/types/subdivision.d.ts b/scripts/types/subdivision.d.ts index bf46831f7..b2a25982d 100644 --- a/scripts/types/subdivision.d.ts +++ b/scripts/types/subdivision.d.ts @@ -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 } diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/cities/adcan.m3u similarity index 100% rename from tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u rename to tests/__data__/expected/playlist_generate/.gh-pages/cities/adcan.m3u diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u index 8aeaaae90..754a6969c 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/int.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/int.m3u new file mode 100644 index 000000000..4e507cf64 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/int.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u index 1848f2801..8cc0cf262 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u index 3fd36e214..d8d2d7f6e 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u index 8aeaaae90..2151d32ae 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/asean.m3u similarity index 100% rename from tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u rename to tests/__data__/expected/playlist_generate/.gh-pages/regions/asean.m3u diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u index 6f6d448e8..202cd4e3f 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u @@ -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] diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u index a9387b8b4..4e507cf64 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cenamer.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cenamer.m3u new file mode 100644 index 000000000..c549c09ce --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cenamer.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u index 6f6d448e8..202cd4e3f 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u @@ -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] diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u index 87d85279d..9a8e34439 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u @@ -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] diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u index 87d85279d..9a8e34439 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u @@ -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] diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u index 8aeaaae90..2151d32ae 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u index 8aeaaae90..2151d32ae 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/southam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/southam.m3u new file mode 100644 index 000000000..c549c09ce --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/southam.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u deleted file mode 100644 index 177057250..000000000 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u +++ /dev/null @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u index fcd718794..c549c09ce 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u index 8aeaaae90..4686c68f4 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u @@ -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 diff --git a/tests/__data__/expected/playlist_generate/logs/generators.log b/tests/__data__/expected/playlist_generate/logs/generators.log index 2177b05c0..6dc3d2af6 100644 --- a/tests/__data__/expected/playlist_generate/logs/generators.log +++ b/tests/__data__/expected/playlist_generate/logs/generators.log @@ -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} diff --git a/tests/__data__/expected/readme_update/playlists.md b/tests/__data__/expected/readme_update/playlists.md index 74dd5d970..120e27f2a 100644 --- a/tests/__data__/expected/readme_update/playlists.md +++ b/tests/__data__/expected/readme_update/playlists.md @@ -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. +
Expand
-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. +
Expand
-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:
-### Grouped by country +### Grouped by broadcast area + +Playlists in which channels are grouped by broadcast area.
Expand -
-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: - - - - - - - - - - - - - - - - - - -
CountryChannelsPlaylist
πŸ‡¨πŸ‡² Cameroon1https://iptv-org.github.io/iptv/countries/cm.m3u
πŸ‡¨πŸ‡¦ Canada2https://iptv-org.github.io/iptv/countries/ca.m3u
πŸ‡¨πŸ‡» Cape Verde1https://iptv-org.github.io/iptv/countries/cv.m3u
πŸ‡¨πŸ‡¬ Republic of the Congo1https://iptv-org.github.io/iptv/countries/cg.m3u
πŸ‡·πŸ‡ͺ RΓ©union1https://iptv-org.github.io/iptv/countries/re.m3u
πŸ‡·πŸ‡΄ Romania1https://iptv-org.github.io/iptv/countries/ro.m3u
πŸ‡·πŸ‡Ί Russia2https://iptv-org.github.io/iptv/countries/ru.m3u
πŸ‡·πŸ‡Ό Rwanda1https://iptv-org.github.io/iptv/countries/rw.m3u
πŸ‡§πŸ‡± Saint BarthΓ©lemy1https://iptv-org.github.io/iptv/countries/bl.m3u
πŸ‡ΈπŸ‡­ Saint Helena1https://iptv-org.github.io/iptv/countries/sh.m3u
πŸ‡°πŸ‡³ Saint Kitts and Nevis1https://iptv-org.github.io/iptv/countries/kn.m3u
Undefined2https://iptv-org.github.io/iptv/countries/undefined.m3u
+- πŸ‡¦πŸ‡© Andorra https://iptv-org.github.io/iptv/countries/ad.m3u + - Canillo https://iptv-org.github.io/iptv/subdivisions/ad-02.m3u + - Canillo https://iptv-org.github.io/iptv/cities/adcan.m3u +- πŸ‡¨πŸ‡² Cameroon https://iptv-org.github.io/iptv/countries/cm.m3u +- πŸ‡¨πŸ‡¦ Canada https://iptv-org.github.io/iptv/countries/ca.m3u + - Ontario https://iptv-org.github.io/iptv/subdivisions/ca-on.m3u +- πŸ‡¨πŸ‡» Cape Verde https://iptv-org.github.io/iptv/countries/cv.m3u +- πŸ‡­πŸ‡° Hong Kong https://iptv-org.github.io/iptv/countries/hk.m3u + - Sai Kung https://iptv-org.github.io/iptv/cities/hk9sk.m3u +- πŸ‡¨πŸ‡¬ Republic of the Congo https://iptv-org.github.io/iptv/countries/cg.m3u +- πŸ‡·πŸ‡ͺ RΓ©union https://iptv-org.github.io/iptv/countries/re.m3u +- πŸ‡·πŸ‡΄ Romania https://iptv-org.github.io/iptv/countries/ro.m3u +- πŸ‡·πŸ‡Ί Russia https://iptv-org.github.io/iptv/countries/ru.m3u +- πŸ‡·πŸ‡Ό Rwanda https://iptv-org.github.io/iptv/countries/rw.m3u +- πŸ‡§πŸ‡± Saint BarthΓ©lemy https://iptv-org.github.io/iptv/countries/bl.m3u +- πŸ‡ΈπŸ‡­ Saint Helena https://iptv-org.github.io/iptv/countries/sh.m3u +- πŸ‡°πŸ‡³ Saint Kitts and Nevis https://iptv-org.github.io/iptv/countries/kn.m3u +- 🌐 International https://iptv-org.github.io/iptv/countries/int.m3u +- Undefined https://iptv-org.github.io/iptv/countries/undefined.m3u -
- -### Grouped by subdivision - -
-Expand -
- - -
-Canada - - - - - - - -
SubdivisionChannelsPlaylist
Ontario1https://iptv-org.github.io/iptv/subdivisions/ca-on.m3u
-
- -
- -### Grouped by region - -
-Expand -
- -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: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RegionChannelsPlaylist
Africa0https://iptv-org.github.io/iptv/regions/afr.m3u
Americas1https://iptv-org.github.io/iptv/regions/amer.m3u
Arab world0https://iptv-org.github.io/iptv/regions/arab.m3u
Asia2https://iptv-org.github.io/iptv/regions/asia.m3u
Asia-Pacific1https://iptv-org.github.io/iptv/regions/apac.m3u
Association of Southeast Asian Nations0https://iptv-org.github.io/iptv/regions/asean.m3u
Caribbean0https://iptv-org.github.io/iptv/regions/carib.m3u
Central America0https://iptv-org.github.io/iptv/regions/cenamer.m3u
Central Asia0https://iptv-org.github.io/iptv/regions/cas.m3u
Commonwealth of Independent States1https://iptv-org.github.io/iptv/regions/cis.m3u
Europe3https://iptv-org.github.io/iptv/regions/eur.m3u
Europe, the Middle East and Africa3https://iptv-org.github.io/iptv/regions/emea.m3u
Hispanic America0https://iptv-org.github.io/iptv/regions/hispam.m3u
Latin America0https://iptv-org.github.io/iptv/regions/latam.m3u
Latin America and the Caribbean0https://iptv-org.github.io/iptv/regions/lac.m3u
Maghreb0https://iptv-org.github.io/iptv/regions/maghreb.m3u
Middle East0https://iptv-org.github.io/iptv/regions/mideast.m3u
Middle East and North Africa0https://iptv-org.github.io/iptv/regions/mena.m3u
Nordics0https://iptv-org.github.io/iptv/regions/nord.m3u
North America1https://iptv-org.github.io/iptv/regions/noram.m3u
Northern America1https://iptv-org.github.io/iptv/regions/nam.m3u
Oceania0https://iptv-org.github.io/iptv/regions/oce.m3u
South America0https://iptv-org.github.io/iptv/regions/southam.m3u
South Asia1https://iptv-org.github.io/iptv/regions/sas.m3u
Sub-Saharan Africa0https://iptv-org.github.io/iptv/regions/ssa.m3u
West Africa0https://iptv-org.github.io/iptv/regions/wafr.m3u
Worldwide1https://iptv-org.github.io/iptv/regions/int.m3u
Undefined2https://iptv-org.github.io/iptv/regions/undefined.m3u
+- Africa https://iptv-org.github.io/iptv/regions/afr.m3u +- Americas https://iptv-org.github.io/iptv/regions/amer.m3u +- Arab world https://iptv-org.github.io/iptv/regions/arab.m3u +- Asia https://iptv-org.github.io/iptv/regions/asia.m3u +- Asia-Pacific https://iptv-org.github.io/iptv/regions/apac.m3u +- Association of Southeast Asian Nations https://iptv-org.github.io/iptv/regions/asean.m3u +- Caribbean https://iptv-org.github.io/iptv/regions/carib.m3u +- Central America https://iptv-org.github.io/iptv/regions/cenamer.m3u +- Central Asia https://iptv-org.github.io/iptv/regions/cas.m3u +- Commonwealth of Independent States https://iptv-org.github.io/iptv/regions/cis.m3u +- Europe https://iptv-org.github.io/iptv/regions/eur.m3u +- Europe, the Middle East and Africa https://iptv-org.github.io/iptv/regions/emea.m3u +- Hispanic America https://iptv-org.github.io/iptv/regions/hispam.m3u +- Latin America https://iptv-org.github.io/iptv/regions/latam.m3u +- Latin America and the Caribbean https://iptv-org.github.io/iptv/regions/lac.m3u +- Maghreb https://iptv-org.github.io/iptv/regions/maghreb.m3u +- Middle East https://iptv-org.github.io/iptv/regions/mideast.m3u +- Middle East and North Africa https://iptv-org.github.io/iptv/regions/mena.m3u +- Nordics https://iptv-org.github.io/iptv/regions/nord.m3u +- North America https://iptv-org.github.io/iptv/regions/noram.m3u +- Northern America https://iptv-org.github.io/iptv/regions/nam.m3u +- Oceania https://iptv-org.github.io/iptv/regions/oce.m3u +- South America https://iptv-org.github.io/iptv/regions/southam.m3u +- South Asia https://iptv-org.github.io/iptv/regions/sas.m3u +- Sub-Saharan Africa https://iptv-org.github.io/iptv/regions/ssa.m3u +- West Africa https://iptv-org.github.io/iptv/regions/wafr.m3u
diff --git a/tests/__data__/input/data/cities.json b/tests/__data__/input/data/cities.json new file mode 100644 index 000000000..2bf660f0e --- /dev/null +++ b/tests/__data__/input/data/cities.json @@ -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"}] \ No newline at end of file diff --git a/tests/__data__/input/data/feeds.json b/tests/__data__/input/data/feeds.json index 7472d177a..1c2ce8806 100644 --- a/tests/__data__/input/data/feeds.json +++ b/tests/__data__/input/data/feeds.json @@ -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": [ diff --git a/tests/__data__/input/data/regions.json b/tests/__data__/input/data/regions.json index 0741930a1..964603166 100644 --- a/tests/__data__/input/data/regions.json +++ b/tests/__data__/input/data/regions.json @@ -1 +1 @@ -[{"code":"AFR","name":"Africa","countries":["AO","BF","BI","BJ","BW","CD","CF","CG","CI","CM","CV","DJ","DZ","EG","EH","ER","ET","GA","GH","GM","GN","GQ","GW","KE","KM","LR","LS","LY","MA","MG","ML","MR","MU","MW","MZ","NA","NE","NG","RE","RW","SC","SD","SH","SL","SN","SO","SS","ST","SZ","TD","TF","TG","TN","TZ","UG","YT","ZA","ZM","ZW"]},{"code":"AMER","name":"Americas","countries":["AG","AI","AR","AW","BB","BL","BM","BO","BR","BS","BV","BZ","CA","CL","CO","CR","CU","CW","DM","DO","EC","FK","GD","GF","GL","GP","GS","GT","GY","HN","HT","JM","KN","KY","LC","MF","MQ","MS","MX","NI","PA","PE","PM","PR","PY","SR","SV","SX","TC","TT","US","UY","VC","VE","VG","VI"]},{"code":"APAC","name":"Asia-Pacific","countries":["AF","AS","AU","BD","BN","BT","CK","CN","FJ","FM","GU","ID","IN","JP","KH","KI","KP","KR","LA","LK","MH","MM","MN","MP","MV","MY","NC","NF","NP","NR","NU","NZ","PF","PG","PH","PK","PN","PW","SB","SG","TH","TK","TL","TO","TV","TW","VN","VU","WF","WS"]},{"code":"ARAB","name":"Arab world","countries":["AE","BH","DJ","DZ","EG","IQ","JO","KM","KW","LB","LY","MA","MR","OM","PS","QA","SA","SD","SO","SY","TN","YE"]},{"code":"ASEAN","name":"Association of Southeast Asian Nations","countries":["BN","KH","ID","LA","MY","MM","PH","SG","TH","VN"]},{"code":"ASIA","name":"Asia","countries":["AE","AF","AM","AZ","BD","BH","BN","BT","CN","CY","GE","ID","IL","IN","IQ","IR","JO","JP","KG","KH","KP","KR","KW","KZ","LA","LB","LK","MM","MN","MV","MY","NP","OM","PH","PK","PS","QA","RU","SA","SG","SY","TH","TJ","TL","TM","TR","TW","UZ","VN","YE"]},{"code":"CARIB","name":"Caribbean","countries":["AG","AI","AW","BB","BL","BS","CU","CW","DM","DO","GD","GP","HT","JM","KN","KY","LC","MF","MQ","MS","PR","SX","TC","TT","VC","VG","VI"]},{"code":"CAS","name":"Central Asia","countries":["KG","KZ","TJ","TM","UZ"]},{"code":"CENAMER","name":"Central America","countries":["BZ","CR","SV","GT","HN","NI","PA"]},{"code":"CIS","name":"Commonwealth of Independent States","countries":["AM","AZ","BY","KG","KZ","MD","RU","TJ","UZ"]},{"code":"EMEA","name":"Europe, the Middle East and Africa","countries":["AD","AE","AL","AM","AO","AT","AZ","BA","BE","BF","BG","BH","BI","BJ","BW","BY","CD","CF","CG","CH","CI","CM","CV","CY","CZ","DE","DJ","DK","DZ","EE","EG","EH","ER","ES","ET","FI","FR","GA","GE","GH","GM","GN","GQ","GR","GW","HR","HU","IE","IQ","IR","IS","IT","JO","KE","KM","KW","KZ","LB","LI","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MG","MK","ML","MR","MT","MU","MW","MZ","NA","NE","NG","NL","NO","OM","PL","PS","PT","QA","RE","RO","RS","RU","RW","SA","SC","SD","SE","SH","SI","SK","SL","SM","SN","SO","SS","ST","SY","SZ","TD","TF","TG","TN","TR","TZ","UA","UG","UK","VA","YE","YT","ZA","ZM","ZW"]},{"code":"EUR","name":"Europe","countries":["AD","AL","AM","AT","AZ","BA","BE","BG","BY","CH","CY","CZ","DE","DK","EE","ES","FI","FR","GE","GR","HR","HU","IE","IS","IT","KZ","LI","LT","LU","LV","MC","MD","ME","MK","MT","NL","NO","PL","PT","RO","RS","RU","SE","SI","SK","SM","TR","UA","UK","VA"]},{"code":"HISPAM","name":"Hispanic America","countries":["AR","BO","CL","CO","CR","CU","DO","EC","GT","HN","MX","NI","PA","PE","PR","PY","SV","UY","VE"]},{"code":"INT","name":"Worldwide","countries":["AD","AE","AF","AG","AI","AL","AM","AO","AQ","AR","AS","AT","AU","AW","AX","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","UK","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MF","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","XK","YE","YT","ZA","ZM","ZW"]},{"code":"LAC","name":"Latin America and the Caribbean","countries":["AG","AI","AR","AW","BB","BL","BO","BR","BS","CL","CO","CR","CU","CW","DM","DO","EC","GD","GF","GP","GT","HN","HT","JM","KN","KY","LC","MF","MQ","MS","MX","NI","PA","PE","PR","PY","SV","SX","TC","TT","UY","VC","VE","VG","VI"]},{"code":"LATAM","name":"Latin America","countries":["AR","BL","BO","BR","CL","CO","CR","CU","DO","EC","GF","GP","GT","HN","HT","MF","MQ","MX","NI","PA","PE","PR","PY","SV","UY","VE"]},{"code":"MAGHREB","name":"Maghreb","countries":["DZ","LY","MA","MR","TN"]},{"code":"MENA","name":"Middle East and North Africa","countries":["AE","BH","CY","DJ","DZ","EG","EH","IL","IQ","IR","JO","KW","LB","LY","MA","OM","PS","QA","SA","SD","SY","TN","TR","YE"]},{"code":"MIDEAST","name":"Middle East","countries":["AE","BH","CY","EG","IL","IQ","IR","JO","KW","LB","OM","PS","QA","SA","SY","TR","YE"]},{"code":"NAM","name":"Northern America","countries":["BM","CA","GL","PM","US"]},{"code":"NORAM","name":"North America","countries":["AG","AI","AW","BB","BL","BM","BS","BZ","CA","CR","CU","CW","DM","DO","GD","GL","GP","GT","HN","HT","JM","KN","KY","LC","MF","MQ","MS","MX","NI","PA","PM","PR","SV","SX","TC","TT","US","VC","VG","VI"]},{"code":"NORD","name":"Nordics","countries":["AX","DK","FO","FI","IS","NO","SE"]},{"code":"OCE","name":"Oceania","countries":["AS","AU","CK","FJ","FM","GU","KI","MH","MP","NC","NF","NR","NU","NZ","PF","PG","PN","PW","SB","TK","TO","TV","VU","WF","WS"]},{"code":"SAS","name":"South Asia","countries":["AF","BD","BT","IN","LK","MV","NP","PK"]},{"code":"SOUTHAM","name":"South America","countries":["AR","BO","BR","CL","CO","EC","PY","PE","UY","VE","BV","FK","GF","GY","GS","SR"]},{"code":"SSA","name":"Sub-Saharan Africa","countries":["AO","BF","BI","BJ","BW","CD","CF","CG","CI","CM","CV","DJ","ER","ET","GA","GH","GM","GN","GQ","GW","KE","KM","LR","LS","MG","ML","MR","MU","MW","MZ","NA","NE","NG","RW","SC","SD","SL","SN","SO","SS","ST","SZ","TD","TG","TZ","UG","ZA","ZM","ZW"]},{"code":"WAFR","name":"West Africa","countries":["BF","BJ","CI","CV","GH","GM","GN","GW","LR","ML","MR","NE","NG","SH","SL","SN","TG"]}] \ No newline at end of file +[{"code":"AFR","name":"Africa","countries":["AO","BF","BI","BJ","BW","CD","CF","CG","CI","CM","CV","DJ","DZ","EG","EH","ER","ET","GA","GH","GM","GN","GQ","GW","KE","KM","LR","LS","LY","MA","MG","ML","MR","MU","MW","MZ","NA","NE","NG","RE","RW","SC","SD","SH","SL","SN","SO","SS","ST","SZ","TD","TF","TG","TN","TZ","UG","YT","ZA","ZM","ZW"]},{"code":"AMER","name":"Americas","countries":["AG","AI","AR","AW","BB","BL","BM","BO","BR","BS","BV","BZ","CA","CL","CO","CR","CU","CW","DM","DO","EC","FK","GD","GF","GL","GP","GS","GT","GY","HN","HT","JM","KN","KY","LC","MF","MQ","MS","MX","NI","PA","PE","PM","PR","PY","SR","SV","SX","TC","TT","US","UY","VC","VE","VG","VI"]},{"code":"APAC","name":"Asia-Pacific","countries":["AF","AS","AU","BD","BN","BT","CK","CN","FJ","FM","GU","ID","IN","JP","KH","KI","KP","KR","LA","LK","MH","MM","MN","MP","MV","MY","NC","NF","NP","NR","NU","NZ","PF","PG","PH","PK","PN","PW","SB","SG","TH","TK","TL","TO","TV","TW","VN","VU","WF","WS"]},{"code":"ARAB","name":"Arab world","countries":["AE","BH","DJ","DZ","EG","IQ","JO","KM","KW","LB","LY","MA","MR","OM","PS","QA","SA","SD","SO","SY","TN","YE"]},{"code":"ASEAN","name":"Association of Southeast Asian Nations","countries":["BN","KH","ID","LA","MY","MM","PH","SG","TH","VN"]},{"code":"ASIA","name":"Asia","countries":["AE","AF","AM","AZ","BD","BH","BN","BT","CN","CY","GE","ID","IL","IN","IQ","IR","JO","JP","KG","KH","KP","KR","KW","KZ","LA","LB","LK","MM","MN","MV","MY","NP","OM","PH","PK","PS","QA","RU","SA","SG","SY","TH","TJ","TL","TM","TR","TW","UZ","VN","YE"]},{"code":"CARIB","name":"Caribbean","countries":["AG","AI","AW","BB","BL","BS","CU","CW","DM","DO","GD","GP","HT","JM","KN","KY","LC","MF","MQ","MS","PR","SX","TC","TT","VC","VG","VI"]},{"code":"CAS","name":"Central Asia","countries":["KG","KZ","TJ","TM","UZ"]},{"code":"CENAMER","name":"Central America","countries":["BZ","CR","SV","GT","HN","NI","PA"]},{"code":"CIS","name":"Commonwealth of Independent States","countries":["AM","AZ","BY","KG","KZ","MD","RU","TJ","UZ"]},{"code":"EMEA","name":"Europe, the Middle East and Africa","countries":["AD","AE","AL","AM","AO","AT","AZ","BA","BE","BF","BG","BH","BI","BJ","BW","BY","CD","CF","CG","CH","CI","CM","CV","CY","CZ","DE","DJ","DK","DZ","EE","EG","EH","ER","ES","ET","FI","FR","GA","GE","GH","GM","GN","GQ","GR","GW","HR","HU","IE","IQ","IR","IS","IT","JO","KE","KM","KW","KZ","LB","LI","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MG","MK","ML","MR","MT","MU","MW","MZ","NA","NE","NG","NL","NO","OM","PL","PS","PT","QA","RE","RO","RS","RU","RW","SA","SC","SD","SE","SH","SI","SK","SL","SM","SN","SO","SS","ST","SY","SZ","TD","TF","TG","TN","TR","TZ","UA","UG","UK","VA","YE","YT","ZA","ZM","ZW"]},{"code":"WW","name":"Worldwide","countries":["AD","AE","AF","AG","AI","AL","AM","AO","AQ","AR","AS","AT","AU","AW","AX","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BL","BM","BN","BO","BQ","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CW","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","GA","UK","GD","GE","GF","GG","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IM","IN","IO","IQ","IR","IS","IT","JE","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","ME","MF","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RS","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","SS","ST","SV","SX","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TL","TM","TN","TO","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","XK","YE","YT","ZA","ZM","ZW"]},{"code":"EUR","name":"Europe","countries":["AD","AL","AM","AT","AZ","BA","BE","BG","BY","CH","CY","CZ","DE","DK","EE","ES","FI","FR","GE","GR","HR","HU","IE","IS","IT","KZ","LI","LT","LU","LV","MC","MD","ME","MK","MT","NL","NO","PL","PT","RO","RS","RU","SE","SI","SK","SM","TR","UA","UK","VA"]},{"code":"HISPAM","name":"Hispanic America","countries":["AR","BO","CL","CO","CR","CU","DO","EC","GT","HN","MX","NI","PA","PE","PR","PY","SV","UY","VE"]},{"code":"LAC","name":"Latin America and the Caribbean","countries":["AG","AI","AR","AW","BB","BL","BO","BR","BS","CL","CO","CR","CU","CW","DM","DO","EC","GD","GF","GP","GT","HN","HT","JM","KN","KY","LC","MF","MQ","MS","MX","NI","PA","PE","PR","PY","SV","SX","TC","TT","UY","VC","VE","VG","VI"]},{"code":"LATAM","name":"Latin America","countries":["AR","BL","BO","BR","CL","CO","CR","CU","DO","EC","GF","GP","GT","HN","HT","MF","MQ","MX","NI","PA","PE","PR","PY","SV","UY","VE"]},{"code":"MAGHREB","name":"Maghreb","countries":["DZ","LY","MA","MR","TN"]},{"code":"MENA","name":"Middle East and North Africa","countries":["AE","BH","CY","DJ","DZ","EG","EH","IL","IQ","IR","JO","KW","LB","LY","MA","OM","PS","QA","SA","SD","SY","TN","TR","YE"]},{"code":"MIDEAST","name":"Middle East","countries":["AE","BH","CY","EG","IL","IQ","IR","JO","KW","LB","OM","PS","QA","SA","SY","TR","YE"]},{"code":"NAM","name":"Northern America","countries":["BM","CA","GL","PM","US"]},{"code":"NORAM","name":"North America","countries":["AG","AI","AW","BB","BL","BM","BS","BZ","CA","CR","CU","CW","DM","DO","GD","GL","GP","GT","HN","HT","JM","KN","KY","LC","MF","MQ","MS","MX","NI","PA","PM","PR","SV","SX","TC","TT","US","VC","VG","VI"]},{"code":"NORD","name":"Nordics","countries":["AX","DK","FO","FI","IS","NO","SE"]},{"code":"OCE","name":"Oceania","countries":["AS","AU","CK","FJ","FM","GU","KI","MH","MP","NC","NF","NR","NU","NZ","PF","PG","PN","PW","SB","TK","TO","TV","VU","WF","WS"]},{"code":"SAS","name":"South Asia","countries":["AF","BD","BT","IN","LK","MV","NP","PK"]},{"code":"SOUTHAM","name":"South America","countries":["AR","BO","BR","CL","CO","EC","PY","PE","UY","VE","BV","FK","GF","GY","GS","SR"]},{"code":"SSA","name":"Sub-Saharan Africa","countries":["AO","BF","BI","BJ","BW","CD","CF","CG","CI","CM","CV","DJ","ER","ET","GA","GH","GM","GN","GQ","GW","KE","KM","LR","LS","MG","ML","MR","MU","MW","MZ","NA","NE","NG","RW","SC","SD","SL","SN","SO","SS","ST","SZ","TD","TG","TZ","UG","ZA","ZM","ZW"]},{"code":"WAFR","name":"West Africa","countries":["BF","BJ","CI","CV","GH","GM","GN","GW","LR","ML","MR","NE","NG","SH","SL","SN","TG"]}] \ No newline at end of file diff --git a/tests/__data__/input/readme_update/.readme/config.json b/tests/__data__/input/readme_update/.readme/config.json deleted file mode 100644 index e02806b64..000000000 --- a/tests/__data__/input/readme_update/.readme/config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "build" : "tests/__data__/output/playlists.md", - "files" : ["tests/__data__/output/.readme/template.md"] -} \ No newline at end of file diff --git a/tests/__data__/input/readme_update/.readme/template.md b/tests/__data__/input/readme_update/.readme/template.md deleted file mode 100644 index d1291f2f4..000000000 --- a/tests/__data__/input/readme_update/.readme/template.md +++ /dev/null @@ -1,110 +0,0 @@ -# Playlists - -### Grouped by category - -
-Expand -
- -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: - - -#include "tests/__data__/output/.readme/_categories.md" - -
- -### Grouped by language - -
-Expand -
- -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: - - -#include "tests/__data__/output/.readme/_languages.md" - -
- -### Grouped by country - -
-Expand -
- -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: - - -#include "tests/__data__/output/.readme/_countries.md" - -
- -### Grouped by subdivision - -
-Expand -
- - -#include "tests/__data__/output/.readme/_subdivisions.md" - -
- -### Grouped by region - -
-Expand -
- -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: - - -#include "tests/__data__/output/.readme/_regions.md" - -
- -### Grouped by sources - -Playlists in which channels are grouped by broadcast source. - -
-Expand -
- -To use the playlist, simply replace `` in the link below with the name of one of the files in the [streams](streams) folder. - -``` -https://iptv-org.github.io/iptv/sources/.m3u -``` - -
- -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/.m3u -``` diff --git a/tests/__data__/input/readme_update/generators.log b/tests/__data__/input/readme_update/generators.log index 27beaa364..de76b7f76 100644 --- a/tests/__data__/input/readme_update/generators.log +++ b/tests/__data__/input/readme_update/generators.log @@ -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} \ No newline at end of file diff --git a/tests/commands/playlist/generate.test.ts b/tests/commands/playlist/generate.test.ts index 5489e54ca..39f38a8f3 100644 --- a/tests/commands/playlist/generate.test.ts +++ b/tests/commands/playlist/generate.test.ts @@ -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}`) ) }) diff --git a/tests/commands/readme/update.test.ts b/tests/commands/readme/update.test.ts index 87af26972..8031c8737 100644 --- a/tests/commands/readme/update.test.ts +++ b/tests/commands/readme/update.test.ts @@ -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') ) }) })