Update scripts

This commit is contained in:
freearhey
2025-08-23 17:47:03 +03:00
parent f665512bfc
commit 63262842e7
21 changed files with 417 additions and 245 deletions

View File

@@ -1,11 +1,101 @@
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)
}
case 's': {
const subdivision: Subdivision = subdivisionsKeyByCode.get(code)
if (!subdivision) return
citiesIncluded = citiesIncluded.concat(subdivision.getCities())
subdivisionsIncluded.add(subdivision)
}
case 'c': {
const country: Country = countriesKeyByCode.get(code)
if (!country) return
citiesIncluded = citiesIncluded.concat(country.getCities())
subdivisionsIncluded = subdivisionsIncluded.concat(country.getSubdivisions())
countriesIncluded.add(country)
regionsIncluded = regionsIncluded.concat(country.getRegions())
}
case 'r': {
const region: Region = regionsKeyByCode.get(code)
if (!region) return
countriesIncluded = countriesIncluded.concat(region.getCountries())
regionsIncluded = regionsIncluded.concat(region.getRegions())
}
}
})
this.citiesIncluded = citiesIncluded.uniqBy((city: City) => city.code)
this.subdivisionsIncluded = subdivisionsIncluded.uniqBy(
(subdivision: Subdivision) => subdivision.code
)
this.countriesIncluded = countriesIncluded.uniqBy((country: Country) => country.code)
this.regionsIncluded = regionsIncluded.uniqBy((region: Region) => region.code)
return this
}
getCountries(): Collection {
return this.countriesIncluded || new Collection()
}
getSubdivisions(): Collection {
return this.subdivisionsIncluded || new Collection()
}
getCities(): Collection {
return this.citiesIncluded || new Collection()
}
getRegions(): Collection {
return this.regionsIncluded || new Collection()
}
includesCountry(country: Country): boolean {
return this.getCountries().includes((_country: Country) => _country.code === country.code)
}
includesSubdivision(subdivision: Subdivision): boolean {
return this.getSubdivisions().includes(
(_subdivision: Subdivision) => _subdivision.code === subdivision.code
)
}
includesRegion(region: Region): boolean {
return this.getRegions().includes((_region: Region) => _region.code === region.code)
}
includesCity(city: City): boolean {
return this.getCities().includes((_city: City) => _city.code === city.code)
}
}

78
scripts/models/city.ts Normal file
View File

@@ -0,0 +1,78 @@
import { Collection, Dictionary } from '@freearhey/core'
import { Country, Region, Subdivision } from '.'
import type { CityData, CitySerializedData } from '../types/city'
export class City {
code: string
name: string
countryCode: string
country?: Country
subdivisionCode?: string
subdivision?: Subdivision
wikidataId: string
regions?: Collection
constructor(data?: CityData) {
if (!data) return
this.code = data.code
this.name = data.name
this.countryCode = data.country
this.subdivisionCode = data.subdivision || undefined
this.wikidataId = data.wikidata_id
}
withCountry(countriesKeyByCode: Dictionary): this {
this.country = countriesKeyByCode.get(this.countryCode)
return this
}
withSubdivision(subdivisionsKeyByCode: Dictionary): this {
if (!this.subdivisionCode) return this
this.subdivision = subdivisionsKeyByCode.get(this.subdivisionCode)
return this
}
withRegions(regions: Collection): this {
this.regions = regions.filter((region: Region) =>
region.countryCodes.includes(this.countryCode)
)
return this
}
getRegions(): Collection {
if (!this.regions) return new Collection()
return this.regions
}
serialize(): CitySerializedData {
return {
code: this.code,
name: this.name,
countryCode: this.countryCode,
country: this.country ? this.country.serialize() : undefined,
subdivisionCode: this.subdivisionCode || null,
subdivision: this.subdivision ? this.subdivision.serialize() : undefined,
wikidataId: this.wikidataId
}
}
deserialize(data: CitySerializedData): this {
this.code = data.code
this.name = data.name
this.countryCode = data.countryCode
this.country = data.country ? new Country().deserialize(data.country) : undefined
this.subdivisionCode = data.subdivisionCode || undefined
this.subdivision = data.subdivision
? new Subdivision().deserialize(data.subdivision)
: undefined
this.wikidataId = data.wikidataId
return this
}
}

View File

@@ -12,6 +12,7 @@ export class Country {
language?: Language
subdivisions?: Collection
regions?: Collection
cities?: Collection
constructor(data?: CountryData) {
if (!data) return
@@ -23,15 +24,19 @@ export class Country {
}
withSubdivisions(subdivisionsGroupedByCountryCode: Dictionary): this {
this.subdivisions = subdivisionsGroupedByCountryCode.get(this.code) || new Collection()
this.subdivisions = new Collection(subdivisionsGroupedByCountryCode.get(this.code))
return this
}
withRegions(regions: Collection): this {
this.regions = regions.filter(
(region: Region) => region.code !== 'INT' && region.includesCountryCode(this.code)
)
this.regions = regions.filter((region: Region) => region.includesCountryCode(this.code))
return this
}
withCities(citiesGroupedByCountryCode: Dictionary): this {
this.cities = new Collection(citiesGroupedByCountryCode.get(this.code))
return this
}
@@ -54,6 +59,10 @@ export class Country {
return this.subdivisions || new Collection()
}
getCities(): Collection {
return this.cities || new Collection()
}
serialize(): CountrySerializedData {
return {
code: this.code,

View File

@@ -1,4 +1,4 @@
import { Country, Language, Region, Channel, Subdivision } from './index'
import { Country, Language, Region, Channel, Subdivision, BroadcastArea } 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,22 @@ export class Feed {
)
}
isInternational(): boolean {
return this.broadcastAreaCodes.includes('r/INT')
}
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)
}
getGuides(): Collection {

View File

@@ -2,6 +2,7 @@ export * from './blocklistRecord'
export * from './broadcastArea'
export * from './category'
export * from './channel'
export * from './city'
export * from './country'
export * from './feed'
export * from './guide'

View File

@@ -1,15 +1,18 @@
import { Collection, Dictionary } from '@freearhey/core'
import { Country, Subdivision } from '.'
import { City, Country, Subdivision } from '.'
import type { RegionData, RegionSerializedData } from '../types/region'
import { CountrySerializedData } from '../types/country'
import { SubdivisionSerializedData } from '../types/subdivision'
import { CitySerializedData } from '../types/city'
export class Region {
code: string
name: string
countryCodes: Collection
countries: Collection = new Collection()
subdivisions: Collection = new Collection()
countries?: Collection
subdivisions?: Collection
cities?: Collection
regions?: Collection
constructor(data?: RegionData) {
if (!data) return
@@ -33,30 +36,61 @@ 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'
}
serialize(): RegionSerializedData {
return {
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 +105,9 @@ export class Region {
this.subdivisions = new Collection(data.subdivisions).map((data: SubdivisionSerializedData) =>
new Subdivision().deserialize(data)
)
this.cities = new Collection(data.cities).map((data: CitySerializedData) =>
new City().deserialize(data)
)
return this
}

View File

@@ -342,10 +342,6 @@ export class Stream {
return this.feed ? this.feed.isBroadcastInRegion(region) : false
}
isInternational(): boolean {
return this.feed ? this.feed.isInternational() : false
}
getLogos(): Collection {
function format(logo: Logo): number {
const levelByFormat = { SVG: 0, PNG: 3, APNG: 1, WebP: 1, AVIF: 1, JPEG: 2, GIF: 1 }

View File

@@ -1,12 +1,15 @@
import { SubdivisionData, SubdivisionSerializedData } from '../types/subdivision'
import { Dictionary } from '@freearhey/core'
import { Country } from '.'
import { Dictionary, Collection } from '@freearhey/core'
import { Country, Region } from '.'
export class Subdivision {
code: string
name: string
countryCode: string
country?: Country
parentCode?: string
regions?: Collection
cities?: Collection
constructor(data?: SubdivisionData) {
if (!data) return
@@ -14,6 +17,7 @@ export class Subdivision {
this.code = data.code
this.name = data.name
this.countryCode = data.country
this.parentCode = data.parent || undefined
}
withCountry(countriesKeyByCode: Dictionary): this {
@@ -22,12 +26,39 @@ export class Subdivision {
return this
}
withRegions(regions: Collection): this {
this.regions = regions.filter((region: Region) =>
region.countryCodes.includes(this.countryCode)
)
return this
}
withCities(citiesGroupedBySubdivisionCode: Dictionary): this {
this.cities = new Collection(citiesGroupedBySubdivisionCode.get(this.code))
return this
}
getRegions(): Collection {
if (!this.regions) return new Collection()
return this.regions
}
getCities(): Collection {
if (!this.cities) return new Collection()
return this.cities
}
serialize(): SubdivisionSerializedData {
return {
code: this.code,
name: this.name,
countryCode: this.code,
country: this.country ? this.country.serialize() : undefined
countryCode: this.countryCode,
country: this.country ? this.country.serialize() : undefined,
parentCode: this.parentCode || null
}
}
@@ -36,6 +67,7 @@ export class Subdivision {
this.name = data.name
this.countryCode = data.countryCode
this.country = data.country ? new Country().deserialize(data.country) : undefined
this.parentCode = data.parentCode || undefined
return this
}