mirror of
https://github.com/iptv-org/iptv-org.github.io.git
synced 2025-05-13 18:40:06 -04:00
Update src/
This commit is contained in:
parent
09b07e9b24
commit
86743c74f5
132 changed files with 4418 additions and 1907 deletions
50
src/models/blocklistRecord.ts
Normal file
50
src/models/blocklistRecord.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import type { BlocklistRecordData, BlocklistRecordSerializedData } from '~/types/blocklistRecord'
|
||||
|
||||
export class BlocklistRecord {
|
||||
channelId: string
|
||||
reason: string
|
||||
refUrl: string
|
||||
|
||||
constructor(data?: BlocklistRecordData) {
|
||||
if (!data) return
|
||||
|
||||
this.channelId = data.channel
|
||||
this.reason = data.reason
|
||||
this.refUrl = data.ref
|
||||
}
|
||||
|
||||
getRefLabel(): string {
|
||||
let refLabel = ''
|
||||
|
||||
const isIssue = /issues|pull/.test(this.refUrl)
|
||||
const isAttachment = /github\.zendesk\.com\/attachments\/token/.test(this.refUrl)
|
||||
if (isIssue) {
|
||||
const parts = this.refUrl.split('/')
|
||||
const issueId = parts.pop()
|
||||
refLabel = `#${issueId}`
|
||||
} else if (isAttachment) {
|
||||
const [, filename] = this.refUrl.match(/\?name=(.*)/) || [null, undefined]
|
||||
refLabel = filename
|
||||
} else {
|
||||
refLabel = this.refUrl.split('/').pop()
|
||||
}
|
||||
|
||||
return refLabel
|
||||
}
|
||||
|
||||
serialize(): BlocklistRecordSerializedData {
|
||||
return {
|
||||
channelId: this.channelId,
|
||||
reason: this.reason,
|
||||
refUrl: this.refUrl
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: BlocklistRecordSerializedData): this {
|
||||
this.channelId = data.channelId
|
||||
this.reason = data.reason
|
||||
this.refUrl = data.refUrl
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
195
src/models/broadcastArea.ts
Normal file
195
src/models/broadcastArea.ts
Normal file
|
@ -0,0 +1,195 @@
|
|||
import type { BroadcastAreaData, BroadcastAreaSerializedData } from '~/types/broadcastArea'
|
||||
import type { SubdivisionSerializedData } from '~/types/subdivision'
|
||||
import type { CountrySerializedData } from '~/types/country'
|
||||
import type { RegionSerializedData } from '~/types/region'
|
||||
import { type Dictionary, Collection } from '@freearhey/core/browser'
|
||||
import { Region, Country, Subdivision } from './'
|
||||
|
||||
export class BroadcastArea {
|
||||
code: string
|
||||
name?: string
|
||||
countries?: Collection
|
||||
subdivisions?: Collection
|
||||
regions?: Collection
|
||||
|
||||
constructor(data?: BroadcastAreaData) {
|
||||
if (!data) return
|
||||
|
||||
this.code = data.code
|
||||
}
|
||||
|
||||
withName(
|
||||
countriesKeyByCode: Dictionary,
|
||||
subdivisionsKeyByCode: Dictionary,
|
||||
regionsKeyByCode: Dictionary
|
||||
): this {
|
||||
const [type, code] = this.code.split('/')
|
||||
|
||||
switch (type) {
|
||||
case 's': {
|
||||
const subdivision: Subdivision = subdivisionsKeyByCode.get(code)
|
||||
if (subdivision) this.name = subdivision.name
|
||||
break
|
||||
}
|
||||
case 'c': {
|
||||
const country: Country = countriesKeyByCode.get(code)
|
||||
if (country) this.name = country.name
|
||||
break
|
||||
}
|
||||
case 'r': {
|
||||
const region: Region = regionsKeyByCode.get(code)
|
||||
if (region) this.name = region.name
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withLocations(
|
||||
countriesKeyByCode: Dictionary,
|
||||
subdivisionsKeyByCode: Dictionary,
|
||||
regionsKeyByCode: Dictionary,
|
||||
regions: Collection
|
||||
): this {
|
||||
const [type, code] = this.code.split('/')
|
||||
|
||||
let _countries = new Collection()
|
||||
let _regions = new Collection()
|
||||
let _subdivisions = new Collection()
|
||||
|
||||
regions = regions.filter((region: Region) => region.code !== 'INT')
|
||||
|
||||
switch (type) {
|
||||
case 's': {
|
||||
const subdivision: Subdivision = subdivisionsKeyByCode.get(code)
|
||||
if (!subdivision) break
|
||||
_subdivisions.add(subdivision)
|
||||
const country: Country = countriesKeyByCode.get(subdivision.countryCode)
|
||||
if (!country) break
|
||||
_countries.add(country)
|
||||
const countryRegions = regions.filter((region: Region) =>
|
||||
region.countryCodes.includes(country.code)
|
||||
)
|
||||
countryRegions.forEach((region: Region) => {
|
||||
_regions.add(region)
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'c': {
|
||||
const country = countriesKeyByCode.get(code)
|
||||
if (!country) break
|
||||
_countries.add(country)
|
||||
const countryRegions = regions.filter((region: Region) =>
|
||||
region.countryCodes.includes(country.code)
|
||||
)
|
||||
countryRegions.forEach((region: Region) => {
|
||||
_regions.add(region)
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'r': {
|
||||
const region: Region = regionsKeyByCode.get(code)
|
||||
if (!region) break
|
||||
_regions.add(region)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.countries = _countries.uniqBy((country: Country) => country.code)
|
||||
this.regions = _regions.uniqBy((region: Region) => region.code)
|
||||
this.subdivisions = _subdivisions.uniqBy((subdivision: Subdivision) => subdivision.code)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.name || ''
|
||||
}
|
||||
|
||||
getCountries(): Collection {
|
||||
if (!this.countries) return new Collection()
|
||||
|
||||
return this.countries
|
||||
}
|
||||
|
||||
getRegions(): Collection {
|
||||
if (!this.regions) return new Collection()
|
||||
|
||||
return this.regions
|
||||
}
|
||||
|
||||
getSubdivisions(): Collection {
|
||||
if (!this.subdivisions) return new Collection()
|
||||
|
||||
return this.subdivisions
|
||||
}
|
||||
|
||||
getLocationCodes(): Collection {
|
||||
let locationCodes = new Collection()
|
||||
|
||||
this.getCountries().forEach((country: Country) => {
|
||||
locationCodes.add(country.code)
|
||||
})
|
||||
|
||||
this.getRegions().forEach((region: Region) => {
|
||||
locationCodes.add(region.code)
|
||||
})
|
||||
|
||||
this.getSubdivisions().forEach((subdivision: Subdivision) => {
|
||||
locationCodes.add(subdivision.code)
|
||||
})
|
||||
|
||||
return locationCodes
|
||||
}
|
||||
|
||||
getLocationNames(): Collection {
|
||||
let locationNames = new Collection()
|
||||
|
||||
this.getCountries().forEach((country: Country) => {
|
||||
locationNames.add(country.name)
|
||||
})
|
||||
|
||||
this.getRegions().forEach((region: Region) => {
|
||||
locationNames.add(region.name)
|
||||
})
|
||||
|
||||
this.getSubdivisions().forEach((subdivision: Subdivision) => {
|
||||
locationNames.add(subdivision.name)
|
||||
})
|
||||
|
||||
return locationNames
|
||||
}
|
||||
|
||||
serialize(): BroadcastAreaSerializedData {
|
||||
return {
|
||||
code: this.code,
|
||||
name: this.getName(),
|
||||
countries: this.getCountries()
|
||||
.map((country: Country) => country.serialize())
|
||||
.all(),
|
||||
subdivisions: this.getSubdivisions()
|
||||
.map((subdivision: Subdivision) => subdivision.serialize())
|
||||
.all(),
|
||||
regions: this.getRegions()
|
||||
.map((region: Region) => region.serialize())
|
||||
.all()
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: BroadcastAreaSerializedData): this {
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.countries = new Collection(data.countries).map((data: CountrySerializedData) =>
|
||||
new Country().deserialize(data)
|
||||
)
|
||||
this.subdivisions = new Collection(data.subdivisions).map((data: SubdivisionSerializedData) =>
|
||||
new Subdivision().deserialize(data)
|
||||
)
|
||||
this.regions = new Collection(data.regions).map((data: RegionSerializedData) =>
|
||||
new Region().deserialize(data)
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
27
src/models/category.ts
Normal file
27
src/models/category.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import type { CategoryData, CategorySerializedData } from '~/types/category'
|
||||
|
||||
export class Category {
|
||||
id: string
|
||||
name: string
|
||||
|
||||
constructor(data?: CategoryData) {
|
||||
if (!data) return
|
||||
|
||||
this.id = data.id
|
||||
this.name = data.name
|
||||
}
|
||||
|
||||
serialize(): CategorySerializedData {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: CategorySerializedData): this {
|
||||
this.id = data.id
|
||||
this.name = data.name
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
export class Channel {
|
||||
constructor(data) {
|
||||
const _streams = Array.isArray(data.streams) ? data.streams : []
|
||||
const _guides = Array.isArray(data.guides) ? data.guides : []
|
||||
const _blocklistRecords = Array.isArray(data.blocklistRecords) ? data.blocklistRecords : []
|
||||
|
||||
this.id = data.id
|
||||
this.name = data.name
|
||||
this.alt_names = this.alt_name = data.altNames
|
||||
this.network = data.network
|
||||
this.owners = this.owner = data.owners
|
||||
this.city = data.city
|
||||
this.country = [data.country?.code, data.country?.name].filter(Boolean)
|
||||
this.subdivision = data.subdivision?.code || null
|
||||
this.languages = this.language = [
|
||||
...data.languages.map(language => language.code),
|
||||
...data.languages.map(language => language.name)
|
||||
]
|
||||
this.categories = this.category = data.categories.map(category => category.name)
|
||||
this.broadcast_area = [
|
||||
...data.broadcastArea.map(area => `${area.type}/${area.code}`).filter(Boolean),
|
||||
...data.broadcastArea.map(area => area.name).filter(Boolean),
|
||||
...data.regionCountries.map(country => country.code).filter(Boolean),
|
||||
...data.regionCountries.map(country => country.name).filter(Boolean)
|
||||
]
|
||||
this.is_nsfw = data.isNSFW
|
||||
this.launched = data.launched
|
||||
this.closed = data.closed
|
||||
this.is_closed = !!data.closed || !!data.replacedBy
|
||||
this.replaced_by = data.replacedBy
|
||||
this.website = data.website
|
||||
this.logo = data.logo
|
||||
this.streams = _streams.length
|
||||
this.guides = _guides.length
|
||||
this.is_blocked = _blocklistRecords.length > 0
|
||||
|
||||
this._hasUniqueName = data.hasUniqueName
|
||||
this._displayName = data.hasUniqueName ? data.name : `${data.name} (${data.country?.name})`
|
||||
this._country = data.country
|
||||
this._subdivision = data.subdivision || null
|
||||
this._languages = data.languages
|
||||
this._categories = data.categories
|
||||
this._broadcastArea = data.broadcastArea
|
||||
this._streams = _streams
|
||||
this._guides = _guides
|
||||
this._blocklistRecords = _blocklistRecords
|
||||
this._guideNames = _guides.map(guide => guide.site_name).filter(Boolean)
|
||||
this._streamUrls = _streams.map(stream => stream.url).filter(Boolean)
|
||||
}
|
||||
|
||||
toObject() {
|
||||
const { ...object } = this
|
||||
|
||||
return object
|
||||
}
|
||||
}
|
537
src/models/channel.ts
Normal file
537
src/models/channel.ts
Normal file
|
@ -0,0 +1,537 @@
|
|||
import type { ChannelSearchable, ChannelSerializedData, ChannelData } from '../types/channel'
|
||||
import type { BlocklistRecordSerializedData } from '~/types/blocklistRecord'
|
||||
import type { HTMLPreviewField } from '../types/htmlPreviewField'
|
||||
import type { CategorySerializedData } from '~/types/category'
|
||||
import type { FeedSerializedData } from '~/types/feed'
|
||||
import { Collection, type Dictionary } from '@freearhey/core/browser'
|
||||
import dayjs, { type Dayjs } from 'dayjs'
|
||||
import {
|
||||
BlocklistRecord,
|
||||
BroadcastArea,
|
||||
Subdivision,
|
||||
Category,
|
||||
Language,
|
||||
Country,
|
||||
Stream,
|
||||
Guide,
|
||||
Feed
|
||||
} from '.'
|
||||
|
||||
export class Channel {
|
||||
id: string
|
||||
name: string
|
||||
altNames: Collection = new Collection()
|
||||
networkName?: string
|
||||
ownerNames: Collection = new Collection()
|
||||
countryCode: string
|
||||
country?: Country
|
||||
subdivisionCode?: string
|
||||
subdivision?: Subdivision
|
||||
cityName?: string
|
||||
categoryIds: Collection = new Collection()
|
||||
categories: Collection = new Collection()
|
||||
isNSFW: boolean
|
||||
launchedDateString?: string
|
||||
launchedDate?: Dayjs
|
||||
closedDateString?: string
|
||||
closedDate?: Dayjs
|
||||
replacedByStreamId?: string
|
||||
replacedByChannelId?: string
|
||||
websiteUrl?: string
|
||||
logoUrl: string
|
||||
blocklistRecords: Collection = new Collection()
|
||||
feeds: Collection = new Collection()
|
||||
hasUniqueName: boolean = true
|
||||
|
||||
constructor(data?: ChannelData) {
|
||||
if (!data) return
|
||||
|
||||
this.id = data.id
|
||||
this.name = data.name
|
||||
this.altNames = new Collection(data.alt_names)
|
||||
this.networkName = data.network
|
||||
this.ownerNames = new Collection(data.owners)
|
||||
this.countryCode = data.country
|
||||
this.subdivisionCode = data.subdivision
|
||||
this.cityName = data.city
|
||||
this.categoryIds = new Collection(data.categories)
|
||||
this.isNSFW = data.is_nsfw
|
||||
this.launchedDateString = data.launched
|
||||
this.launchedDate = data.launched ? dayjs(data.launched) : undefined
|
||||
this.closedDateString = data.closed
|
||||
this.closedDate = data.closed ? dayjs(data.closed) : undefined
|
||||
this.replacedByStreamId = data.replaced_by || undefined
|
||||
const [replacedByChannelId] = data.replaced_by ? data.replaced_by.split('@') : [undefined]
|
||||
this.replacedByChannelId = replacedByChannelId
|
||||
this.websiteUrl = data.website
|
||||
this.logoUrl = data.logo
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
withCategories(categoriesKeyById: Dictionary): this {
|
||||
this.categories = this.categoryIds.map((id: string) => categoriesKeyById.get(id))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withBlocklistRecords(blocklistGroupedByChannelId: Dictionary): this {
|
||||
this.blocklistRecords = new Collection(blocklistGroupedByChannelId.get(this.id))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withFeeds(feedsGroupedByChannelId: Dictionary): this {
|
||||
this.feeds = new Collection(feedsGroupedByChannelId.get(this.id))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
setHasUniqueName(channelsGroupedByName: Dictionary): this {
|
||||
this.hasUniqueName = new Collection(channelsGroupedByName.get(this.name)).count() === 1
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getUniqueName(): string {
|
||||
if (this.hasUniqueName) return this.name
|
||||
if (!this.country) return this.name
|
||||
|
||||
return `${this.name} (${this.country.name})`
|
||||
}
|
||||
|
||||
getCategories(): Collection {
|
||||
if (!this.categories) return new Collection()
|
||||
|
||||
return this.categories
|
||||
}
|
||||
|
||||
getStreams(): Collection {
|
||||
let streams = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
streams = streams.concat(feed.getStreams())
|
||||
})
|
||||
|
||||
return streams
|
||||
}
|
||||
|
||||
getGuides(): Collection {
|
||||
let guides = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
guides = guides.concat(feed.getGuides())
|
||||
})
|
||||
|
||||
return guides
|
||||
}
|
||||
|
||||
getLanguages(): Collection {
|
||||
let languages = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
languages = languages.concat(feed.getLanguages())
|
||||
})
|
||||
|
||||
return languages.uniqBy((language: Language) => language.code)
|
||||
}
|
||||
|
||||
getLanguageCodes(): Collection {
|
||||
return this.getLanguages().map((language: Language) => language.code)
|
||||
}
|
||||
|
||||
getLanguageNames(): Collection {
|
||||
return this.getLanguages().map((language: Language) => language.name)
|
||||
}
|
||||
|
||||
getBroadcastAreaCodes(): Collection {
|
||||
let broadcastAreaCodes = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
broadcastAreaCodes = broadcastAreaCodes.concat(feed.broadcastAreaCodes)
|
||||
})
|
||||
|
||||
return broadcastAreaCodes.uniq()
|
||||
}
|
||||
|
||||
hasGuides(): boolean {
|
||||
return this.getGuides().notEmpty()
|
||||
}
|
||||
|
||||
hasFeeds(): boolean {
|
||||
return this.getFeeds().notEmpty()
|
||||
}
|
||||
|
||||
hasStreams(): boolean {
|
||||
return this.getStreams().notEmpty()
|
||||
}
|
||||
|
||||
getDisplayName(): string {
|
||||
return this.name
|
||||
}
|
||||
|
||||
getPagePath(): string {
|
||||
const [channelSlug, countryCode] = this.id.split('.')
|
||||
if (!channelSlug || !countryCode) return ''
|
||||
|
||||
return `/channels/${countryCode}/${channelSlug}`
|
||||
}
|
||||
|
||||
getPageUrl(): string {
|
||||
const [channelSlug, countryCode] = this.id.split('.') || [null, null]
|
||||
if (!channelSlug || !countryCode || typeof window === 'undefined') return ''
|
||||
|
||||
return `${window.location.protocol}//${window.location.host}/channels/${countryCode}/${channelSlug}`
|
||||
}
|
||||
|
||||
isClosed(): boolean {
|
||||
return !!this.closedDateString || !!this.replacedByStreamId
|
||||
}
|
||||
|
||||
isBlocked(): boolean {
|
||||
return this.blocklistRecords ? this.blocklistRecords.notEmpty() : false
|
||||
}
|
||||
|
||||
getCountryName(): string {
|
||||
return this.country ? this.country.name : ''
|
||||
}
|
||||
|
||||
getGuideSiteNames(): Collection {
|
||||
return this.getGuides().map((guide: Guide) => guide.siteName)
|
||||
}
|
||||
|
||||
getStreamUrls(): Collection {
|
||||
return this.getStreams().map((stream: Stream) => stream.url)
|
||||
}
|
||||
|
||||
getFeeds(): Collection {
|
||||
if (!this.feeds) return new Collection()
|
||||
|
||||
return this.feeds
|
||||
}
|
||||
|
||||
getBroadcastLocationCodes(): Collection {
|
||||
let broadcastLocationCodes = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
broadcastLocationCodes = broadcastLocationCodes.concat(feed.getBroadcastLocationCodes())
|
||||
})
|
||||
|
||||
return broadcastLocationCodes.uniq()
|
||||
}
|
||||
|
||||
getBroadcastLocationNames(): Collection {
|
||||
let broadcastLocationNames = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
broadcastLocationNames = broadcastLocationNames.concat(feed.getBroadcastLocationNames())
|
||||
})
|
||||
|
||||
return broadcastLocationNames.uniq()
|
||||
}
|
||||
|
||||
getVideoFormats(): Collection {
|
||||
let videoFormats = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
videoFormats.add(feed.videoFormat)
|
||||
})
|
||||
|
||||
return videoFormats.uniq()
|
||||
}
|
||||
|
||||
getTimezoneIds(): Collection {
|
||||
let timezoneIds = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
timezoneIds = timezoneIds.concat(feed.timezoneIds)
|
||||
})
|
||||
|
||||
return timezoneIds.uniq()
|
||||
}
|
||||
|
||||
getBroadcastArea(): Collection {
|
||||
let broadcastArea = new Collection()
|
||||
|
||||
this.getFeeds().forEach((feed: Feed) => {
|
||||
broadcastArea = broadcastArea.concat(feed.getBroadcastArea())
|
||||
})
|
||||
|
||||
return broadcastArea.uniqBy((broadcastArea: BroadcastArea) => broadcastArea.code)
|
||||
}
|
||||
|
||||
getSearchable(): ChannelSearchable {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
alt_names: this.altNames.all(),
|
||||
alt_name: this.altNames.all(),
|
||||
network: this.networkName,
|
||||
owner: this.ownerNames.all(),
|
||||
owners: this.ownerNames.all(),
|
||||
country: this.countryCode,
|
||||
subdivision: this.subdivisionCode,
|
||||
city: this.cityName,
|
||||
category: this.categoryIds.all(),
|
||||
categories: this.categoryIds.all(),
|
||||
launched: this.launchedDateString,
|
||||
closed: this.closedDateString,
|
||||
replaced_by: this.replacedByStreamId,
|
||||
website: this.websiteUrl,
|
||||
is_nsfw: this.isNSFW,
|
||||
is_closed: this.isClosed(),
|
||||
is_blocked: this.isBlocked(),
|
||||
feeds: this.getFeeds().count(),
|
||||
streams: this.getStreams().count(),
|
||||
guides: this.getGuides().count(),
|
||||
language: this.getLanguageCodes().all(),
|
||||
languages: this.getLanguageCodes().all(),
|
||||
broadcast_area: this.getBroadcastAreaCodes().all(),
|
||||
video_format: this.getVideoFormats().all(),
|
||||
video_formats: this.getVideoFormats().all(),
|
||||
timezone: this.getTimezoneIds().all(),
|
||||
timezones: this.getTimezoneIds().all(),
|
||||
_languageNames: this.getLanguageNames().all(),
|
||||
_broadcastLocationCodes: this.getBroadcastLocationCodes().all(),
|
||||
_broadcastLocationNames: this.getBroadcastLocationNames().all(),
|
||||
_countryName: this.getCountryName(),
|
||||
_guideSiteNames: this.getGuideSiteNames().all(),
|
||||
_streamUrls: this.getStreamUrls().all()
|
||||
}
|
||||
}
|
||||
|
||||
serialize(props = { withFeeds: true }): ChannelSerializedData {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
altNames: this.altNames.all(),
|
||||
networkName: this.networkName,
|
||||
ownerNames: this.ownerNames.all(),
|
||||
countryCode: this.countryCode,
|
||||
country: this.country ? this.country.serialize() : null,
|
||||
subdivisionCode: this.subdivisionCode,
|
||||
subdivision: this.subdivision ? this.subdivision.serialize() : null,
|
||||
cityName: this.cityName,
|
||||
categoryIds: this.categoryIds.all(),
|
||||
categories: this.categories.map((category: Category) => category.serialize()).all(),
|
||||
isNSFW: this.isNSFW,
|
||||
launchedDateString: this.launchedDateString,
|
||||
launchedDate: this.launchedDate ? this.launchedDate.toJSON() : null,
|
||||
closedDateString: this.closedDateString,
|
||||
closedDate: this.closedDate ? this.closedDate.toJSON() : null,
|
||||
replacedByStreamId: this.replacedByStreamId,
|
||||
replacedByChannelId: this.replacedByChannelId,
|
||||
websiteUrl: this.websiteUrl,
|
||||
logoUrl: this.logoUrl,
|
||||
blocklistRecords: this.blocklistRecords
|
||||
.map((blocklistRecord: BlocklistRecord) => blocklistRecord.serialize())
|
||||
.all(),
|
||||
feeds: props.withFeeds
|
||||
? this.getFeeds()
|
||||
.map((feed: Feed) => feed.serialize())
|
||||
.all()
|
||||
: [],
|
||||
hasUniqueName: this.hasUniqueName
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: ChannelSerializedData): this {
|
||||
this.id = data.id || ''
|
||||
this.name = data.name
|
||||
this.altNames = new Collection(data.altNames)
|
||||
this.networkName = data.networkName
|
||||
this.ownerNames = new Collection(data.ownerNames)
|
||||
this.countryCode = data.countryCode
|
||||
this.country = new Country().deserialize(data.country)
|
||||
this.subdivisionCode = data.subdivisionCode
|
||||
this.cityName = data.cityName
|
||||
this.categoryIds = new Collection(data.categoryIds)
|
||||
this.categories = new Collection(data.categories).map((data: CategorySerializedData) =>
|
||||
new Category().deserialize(data)
|
||||
)
|
||||
this.isNSFW = data.isNSFW
|
||||
this.launchedDateString = data.launchedDateString
|
||||
this.launchedDate = data.launchedDate ? dayjs(data.launchedDate) : undefined
|
||||
this.closedDateString = data.closedDateString
|
||||
this.closedDate = data.closedDate ? dayjs(data.closedDate) : undefined
|
||||
this.replacedByStreamId = data.replacedByStreamId
|
||||
this.replacedByChannelId = data.replacedByChannelId
|
||||
this.websiteUrl = data.websiteUrl
|
||||
this.logoUrl = data.logoUrl
|
||||
this.blocklistRecords = new Collection(data.blocklistRecords).map(
|
||||
(data: BlocklistRecordSerializedData) => new BlocklistRecord().deserialize(data)
|
||||
)
|
||||
this.feeds = new Collection(data.feeds).map((data: FeedSerializedData) =>
|
||||
new Feed().deserialize(data)
|
||||
)
|
||||
this.hasUniqueName = data.hasUniqueName
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getFieldset(): HTMLPreviewField[] {
|
||||
return [
|
||||
{
|
||||
name: 'logo',
|
||||
type: 'image',
|
||||
value: { src: this.logoUrl, alt: `${this.name} logo`, title: this.logoUrl }
|
||||
},
|
||||
{ name: 'id', type: 'string', value: this.id, title: this.id },
|
||||
{ name: 'name', type: 'string', value: this.name, title: this.name },
|
||||
{ name: 'alt_names', type: 'string[]', value: this.altNames.all() },
|
||||
{
|
||||
name: 'network',
|
||||
type: 'link',
|
||||
value: this.networkName
|
||||
? { label: this.networkName, query: `network:${normalize(this.networkName)}` }
|
||||
: null
|
||||
},
|
||||
{
|
||||
name: 'owners',
|
||||
type: 'link[]',
|
||||
value: this.ownerNames
|
||||
.map((name: string) => ({
|
||||
label: name,
|
||||
query: `owner:${normalize(name)}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'country',
|
||||
type: 'link',
|
||||
value: this.country
|
||||
? { label: this.country.name, query: `country:${this.country.code}` }
|
||||
: null
|
||||
},
|
||||
{
|
||||
name: 'subdivision',
|
||||
type: 'link',
|
||||
value: this.subdivision
|
||||
? { label: this.subdivision.name, query: `subdivision:${this.subdivision.code}` }
|
||||
: null
|
||||
},
|
||||
{
|
||||
name: 'city',
|
||||
type: 'link',
|
||||
value: this.cityName ? { label: this.cityName, query: `city:${this.cityName}` } : null
|
||||
},
|
||||
{
|
||||
name: 'broadcast_area',
|
||||
type: 'link[]',
|
||||
value: this.getBroadcastArea()
|
||||
.map((broadcastArea: BroadcastArea) => ({
|
||||
label: broadcastArea.getName(),
|
||||
query: `broadcast_area:${broadcastArea.code}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'timezones',
|
||||
type: 'link[]',
|
||||
value: this.getTimezoneIds()
|
||||
.map((id: string) => ({
|
||||
label: id,
|
||||
query: `timezone:${id}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'languages',
|
||||
type: 'link[]',
|
||||
value: this.getLanguages()
|
||||
.map((language: Language) => ({
|
||||
label: language.name,
|
||||
query: `language:${language.code}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'categories',
|
||||
type: 'link[]',
|
||||
value: this.categories
|
||||
.map((category: Category) => ({
|
||||
label: category.name,
|
||||
query: `category:${category.id}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'is_nsfw',
|
||||
type: 'link',
|
||||
value: { label: this.isNSFW.toString(), query: `is_nsfw:${this.isNSFW.toString()}` }
|
||||
},
|
||||
{
|
||||
name: 'video_formats',
|
||||
type: 'link[]',
|
||||
value: this.getVideoFormats()
|
||||
.map((format: string) => ({
|
||||
label: format,
|
||||
query: `video_format:${format}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'launched',
|
||||
type: 'string',
|
||||
value: this.launchedDate ? this.launchedDate.format('D MMMM YYYY') : null,
|
||||
title: this.launchedDateString
|
||||
},
|
||||
{
|
||||
name: 'closed',
|
||||
type: 'string',
|
||||
value: this.closedDate ? this.closedDate.format('D MMMM YYYY') : null,
|
||||
title: this.closedDateString
|
||||
},
|
||||
{
|
||||
name: 'replaced_by',
|
||||
type: 'link',
|
||||
value: this.replacedByStreamId
|
||||
? {
|
||||
label: this.replacedByStreamId,
|
||||
query: `id:${this.replacedByChannelId.replace('.', '\\.')}`
|
||||
}
|
||||
: null
|
||||
},
|
||||
{
|
||||
name: 'website',
|
||||
type: 'external_link',
|
||||
value: this.websiteUrl
|
||||
? { href: this.websiteUrl, title: this.websiteUrl, label: this.websiteUrl }
|
||||
: null
|
||||
}
|
||||
].filter((field: HTMLPreviewField) =>
|
||||
Array.isArray(field.value) ? field.value.length : field.value
|
||||
)
|
||||
}
|
||||
|
||||
getStructuredData() {
|
||||
return {
|
||||
'@context': 'https://schema.org/',
|
||||
'@type': 'TelevisionChannel',
|
||||
image: this.logoUrl,
|
||||
identifier: this.id,
|
||||
name: this.name,
|
||||
alternateName: this.altNames.map((name: string) => ({ '@value': name })),
|
||||
genre: this.categories.map((category: Category) => ({ '@value': category.name })),
|
||||
sameAs: this.websiteUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(value: string) {
|
||||
value = value.includes(' ') ? `"${value}"` : value
|
||||
|
||||
return encodeURIComponent(value)
|
||||
}
|
54
src/models/country.ts
Normal file
54
src/models/country.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import type { CountryData, CountrySerializedData } from '~/types/country'
|
||||
import { Collection, type Dictionary } from '@freearhey/core/browser'
|
||||
import { Channel } from './channel'
|
||||
|
||||
export class Country {
|
||||
code: string
|
||||
name: string
|
||||
flagEmoji: string
|
||||
languageCode: string
|
||||
channels: Collection = new Collection()
|
||||
|
||||
constructor(data?: CountryData) {
|
||||
if (!data) return
|
||||
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.flagEmoji = data.flag
|
||||
this.languageCode = data.lang
|
||||
}
|
||||
|
||||
withChannels(channelsGroupedByCountryCode: Dictionary): this {
|
||||
this.channels = new Collection(channelsGroupedByCountryCode.get(this.code))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getChannels(): Collection {
|
||||
if (!this.channels) return new Collection()
|
||||
|
||||
return this.channels
|
||||
}
|
||||
|
||||
getChannelsWithStreams(): Collection {
|
||||
return this.getChannels().filter((channel: Channel) => channel.hasStreams())
|
||||
}
|
||||
|
||||
serialize(): CountrySerializedData {
|
||||
return {
|
||||
code: this.code,
|
||||
name: this.name,
|
||||
flagEmoji: this.flagEmoji,
|
||||
languageCode: this.languageCode
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: CountrySerializedData): this {
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.flagEmoji = data.flagEmoji
|
||||
this.languageCode = data.languageCode
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
259
src/models/feed.ts
Normal file
259
src/models/feed.ts
Normal file
|
@ -0,0 +1,259 @@
|
|||
import { Collection, type Dictionary } from '@freearhey/core/browser'
|
||||
import type { FeedData, FeedSerializedData } from '~/types/feed'
|
||||
import { Guide, Stream, Language, BroadcastArea, Channel } from './'
|
||||
import type { HTMLPreviewField } from '~/types/htmlPreviewField'
|
||||
|
||||
export class Feed {
|
||||
channelId: string
|
||||
channel: Channel
|
||||
id: string
|
||||
name: string
|
||||
isMain: boolean
|
||||
broadcastAreaCodes: Collection
|
||||
broadcastArea?: Collection
|
||||
timezoneIds: Collection
|
||||
languageCodes: Collection
|
||||
languages?: Collection
|
||||
videoFormat: string
|
||||
streams?: Collection
|
||||
guides?: Collection
|
||||
|
||||
constructor(data?: FeedData) {
|
||||
if (!data) return
|
||||
|
||||
this.channelId = data.channel
|
||||
this.id = data.id
|
||||
this.name = data.name
|
||||
this.isMain = data.is_main
|
||||
this.broadcastAreaCodes = new Collection(data.broadcast_area)
|
||||
this.timezoneIds = new Collection(data.timezones)
|
||||
this.languageCodes = new Collection(data.languages)
|
||||
this.videoFormat = data.video_format
|
||||
}
|
||||
|
||||
withChannel(channelsKeyById: Dictionary): this {
|
||||
if (!this.channelId) return this
|
||||
|
||||
this.channel = channelsKeyById.get(this.channelId)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withStreams(streamsGroupedByStreamId: Dictionary): this {
|
||||
this.streams = new Collection(streamsGroupedByStreamId.get(`${this.channelId}@${this.id}`))
|
||||
|
||||
if (this.isMain) {
|
||||
this.streams = this.streams.concat(
|
||||
new Collection(streamsGroupedByStreamId.get(this.channelId))
|
||||
)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withGuides(guidesGroupedByStreamId: Dictionary): this {
|
||||
this.guides = new Collection(guidesGroupedByStreamId.get(`${this.channelId}@${this.id}`))
|
||||
|
||||
if (this.isMain) {
|
||||
this.guides = this.guides.concat(new Collection(guidesGroupedByStreamId.get(this.channelId)))
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withLanguages(languagesKeyByCode: Dictionary): this {
|
||||
this.languages = this.languageCodes
|
||||
.map((code: string) => languagesKeyByCode.get(code))
|
||||
.filter(Boolean)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withBroadcastArea(
|
||||
countriesKeyByCode: Dictionary,
|
||||
subdivisionsKeyByCode: Dictionary,
|
||||
regionsKeyByCode: Dictionary,
|
||||
regions: Collection
|
||||
): this {
|
||||
this.broadcastArea = this.broadcastAreaCodes
|
||||
.map((code: string) =>
|
||||
new BroadcastArea({ code })
|
||||
.withName(countriesKeyByCode, subdivisionsKeyByCode, regionsKeyByCode)
|
||||
.withLocations(countriesKeyByCode, subdivisionsKeyByCode, regionsKeyByCode, regions)
|
||||
)
|
||||
.filter(Boolean)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getUUID(): string {
|
||||
return this.channelId + this.id
|
||||
}
|
||||
|
||||
getBroadcastArea(): Collection {
|
||||
if (!this.broadcastArea) return new Collection()
|
||||
|
||||
return this.broadcastArea
|
||||
}
|
||||
|
||||
getBroadcastLocationCodes(): Collection {
|
||||
let broadcastLocationCodes = new Collection()
|
||||
|
||||
this.getBroadcastArea().forEach((broadcastArea: BroadcastArea) => {
|
||||
broadcastLocationCodes = broadcastLocationCodes.concat(broadcastArea.getLocationCodes())
|
||||
})
|
||||
|
||||
return broadcastLocationCodes.uniq()
|
||||
}
|
||||
|
||||
getBroadcastLocationNames(): Collection {
|
||||
let broadcastLocationNames = new Collection()
|
||||
|
||||
this.getBroadcastArea().forEach((broadcastArea: BroadcastArea) => {
|
||||
broadcastLocationNames = broadcastLocationNames.concat(broadcastArea.getLocationNames())
|
||||
})
|
||||
|
||||
return broadcastLocationNames.uniq()
|
||||
}
|
||||
|
||||
getStreams(): Collection {
|
||||
if (!this.streams) return new Collection()
|
||||
|
||||
return this.streams
|
||||
}
|
||||
|
||||
hasStreams(): boolean {
|
||||
return this.getStreams().notEmpty()
|
||||
}
|
||||
|
||||
getGuides(): Collection {
|
||||
if (!this.guides) return new Collection()
|
||||
|
||||
return this.guides
|
||||
}
|
||||
|
||||
hasGuides(): boolean {
|
||||
return this.getGuides().notEmpty()
|
||||
}
|
||||
|
||||
getLanguages(): Collection {
|
||||
if (!this.languages) return new Collection()
|
||||
|
||||
return this.languages
|
||||
}
|
||||
|
||||
getLanguageCodes(): Collection {
|
||||
return this.getLanguages().map((language: Language) => language.code)
|
||||
}
|
||||
|
||||
getLanguageNames(): Collection {
|
||||
return this.getLanguages().map((language: Language) => language.name)
|
||||
}
|
||||
|
||||
getDisplayName(): string {
|
||||
if (!this.channel) return this.name
|
||||
|
||||
return [this.channel.name, this.name].join(' ')
|
||||
}
|
||||
|
||||
getPageUrl(): string {
|
||||
const [channelSlug, countryCode] = this.channelId.split('.') || [null, null]
|
||||
if (!channelSlug || !countryCode || typeof window === 'undefined') return ''
|
||||
|
||||
return `${window.location.protocol}//${window.location.host}/channels/${countryCode}/${channelSlug}#${this.id}`
|
||||
}
|
||||
|
||||
getFieldset(): HTMLPreviewField[] {
|
||||
return [
|
||||
{ name: 'id', type: 'string', value: this.id, title: this.id },
|
||||
{ name: 'name', type: 'string', value: this.name, title: this.name },
|
||||
{
|
||||
name: 'is_main',
|
||||
type: 'string',
|
||||
value: this.isMain.toString(),
|
||||
title: this.isMain.toString()
|
||||
},
|
||||
{
|
||||
name: 'broadcast_area',
|
||||
type: 'link[]',
|
||||
value: this.getBroadcastArea()
|
||||
.map((broadcastArea: BroadcastArea) => ({
|
||||
label: broadcastArea.getName(),
|
||||
query: `broadcast_area:${broadcastArea.code}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'timezones',
|
||||
type: 'link[]',
|
||||
value: this.timezoneIds
|
||||
.map((id: string) => ({
|
||||
label: id,
|
||||
query: `timezone:${id}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'languages',
|
||||
type: 'link[]',
|
||||
value: this.getLanguages()
|
||||
.map((language: Language) => ({
|
||||
label: language.name,
|
||||
query: `language:${language.code}`
|
||||
}))
|
||||
.all()
|
||||
},
|
||||
{
|
||||
name: 'video_format',
|
||||
type: 'link',
|
||||
value: { label: this.videoFormat, query: `video_format:${this.videoFormat}` }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
serialize(): FeedSerializedData {
|
||||
return {
|
||||
channelId: this.channelId,
|
||||
channel: this.channel ? this.channel.serialize({ withFeeds: false }) : null,
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
isMain: this.isMain,
|
||||
broadcastAreaCodes: this.broadcastAreaCodes.all(),
|
||||
broadcastArea: this.getBroadcastArea()
|
||||
.map((broadcastArea: BroadcastArea) => broadcastArea.serialize())
|
||||
.all(),
|
||||
timezoneIds: this.timezoneIds.all(),
|
||||
languageCodes: this.languageCodes.all(),
|
||||
languages: this.getLanguages()
|
||||
.map((language: Language) => language.serialize())
|
||||
.all(),
|
||||
videoFormat: this.videoFormat,
|
||||
streams: this.getStreams()
|
||||
.map((stream: Stream) => stream.serialize())
|
||||
.all(),
|
||||
guides: this.getGuides()
|
||||
.map((guide: Guide) => guide.serialize())
|
||||
.all()
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: FeedSerializedData): this {
|
||||
this.channelId = data.channelId
|
||||
this.channel = data.channel ? new Channel().deserialize(data.channel) : undefined
|
||||
this.id = data.id
|
||||
this.name = data.name
|
||||
this.isMain = data.isMain
|
||||
this.broadcastAreaCodes = new Collection(data.broadcastAreaCodes)
|
||||
this.broadcastArea = new Collection(data.broadcastArea).map(data =>
|
||||
new BroadcastArea().deserialize(data)
|
||||
)
|
||||
this.timezoneIds = new Collection(data.timezoneIds)
|
||||
this.languageCodes = new Collection(data.languageCodes)
|
||||
this.languages = new Collection(data.languages).map(data => new Language().deserialize(data))
|
||||
this.videoFormat = data.videoFormat
|
||||
this.streams = new Collection(data.streams).map(data => new Stream().deserialize(data))
|
||||
this.guides = new Collection(data.guides).map(data => new Guide().deserialize(data))
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
58
src/models/guide.ts
Normal file
58
src/models/guide.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import type { GuideData, GuideSerializedData } from '~/types/guide'
|
||||
|
||||
export class Guide {
|
||||
channelId?: string
|
||||
feedId?: string
|
||||
siteDomain: string
|
||||
siteId: string
|
||||
siteName: string
|
||||
languageCode: string
|
||||
|
||||
constructor(data?: GuideData) {
|
||||
if (!data) return
|
||||
|
||||
this.channelId = data.channel
|
||||
this.feedId = data.feed
|
||||
this.siteDomain = data.site
|
||||
this.siteId = data.site_id
|
||||
this.siteName = data.site_name
|
||||
this.languageCode = data.lang
|
||||
}
|
||||
|
||||
getUUID(): string {
|
||||
return this.getId() + this.siteId
|
||||
}
|
||||
|
||||
getId(): string | undefined {
|
||||
if (!this.channelId) return undefined
|
||||
if (!this.feedId) return this.channelId
|
||||
|
||||
return `${this.channelId}@${this.feedId}`
|
||||
}
|
||||
|
||||
getUrl() {
|
||||
return `https://${this.siteDomain}`
|
||||
}
|
||||
|
||||
serialize(): GuideSerializedData {
|
||||
return {
|
||||
channelId: this.channelId,
|
||||
feedId: this.feedId,
|
||||
siteDomain: this.siteDomain,
|
||||
siteId: this.siteId,
|
||||
siteName: this.siteName,
|
||||
languageCode: this.languageCode
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: GuideSerializedData): this {
|
||||
this.channelId = data.channelId
|
||||
this.feedId = data.feedId
|
||||
this.siteDomain = data.siteDomain
|
||||
this.siteId = data.siteId
|
||||
this.siteName = data.siteName
|
||||
this.languageCode = data.languageCode
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export * from './channel'
|
11
src/models/index.ts
Normal file
11
src/models/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
export * from './channel'
|
||||
export * from './country'
|
||||
export * from './subdivision'
|
||||
export * from './language'
|
||||
export * from './category'
|
||||
export * from './region'
|
||||
export * from './stream'
|
||||
export * from './guide'
|
||||
export * from './blocklistRecord'
|
||||
export * from './feed'
|
||||
export * from './broadcastArea'
|
27
src/models/language.ts
Normal file
27
src/models/language.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import type { LanguageData, LanguageSerializedData } from '~/types/language'
|
||||
|
||||
export class Language {
|
||||
code: string
|
||||
name: string
|
||||
|
||||
constructor(data?: LanguageData) {
|
||||
if (!data) return
|
||||
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
}
|
||||
|
||||
serialize(): LanguageSerializedData {
|
||||
return {
|
||||
code: this.code,
|
||||
name: this.name
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: LanguageSerializedData): this {
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
32
src/models/region.ts
Normal file
32
src/models/region.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import type { RegionData, RegionSerializedData } from '~/types/region'
|
||||
import { Collection } from '@freearhey/core/browser'
|
||||
|
||||
export class Region {
|
||||
code: string
|
||||
name: string
|
||||
countryCodes: Collection
|
||||
|
||||
constructor(data?: RegionData) {
|
||||
if (!data) return
|
||||
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.countryCodes = new Collection(data.countries)
|
||||
}
|
||||
|
||||
serialize(): RegionSerializedData {
|
||||
return {
|
||||
code: this.code,
|
||||
name: this.name,
|
||||
countryCodes: this.countryCodes.all()
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: RegionSerializedData): this {
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.countryCodes = new Collection(data.countryCodes)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
149
src/models/stream.ts
Normal file
149
src/models/stream.ts
Normal file
|
@ -0,0 +1,149 @@
|
|||
import type { JsonDataViewerField } from '~/types/jsonDataViewerField'
|
||||
import type { StreamData, StreamSerializedData } from '~/types/stream'
|
||||
import type { Dictionary } from '@freearhey/core/browser'
|
||||
import { Link } from 'iptv-playlist-generator'
|
||||
import type { Channel } from './channel'
|
||||
import type { Feed } from './feed'
|
||||
import type { Category } from './category'
|
||||
|
||||
export class Stream {
|
||||
channelId?: string
|
||||
feedId?: string
|
||||
url: string
|
||||
referrer?: string
|
||||
userAgent?: string
|
||||
quality?: string
|
||||
channel?: Channel
|
||||
feed?: Feed
|
||||
|
||||
constructor(data?: StreamData) {
|
||||
if (!data) return
|
||||
|
||||
this.channelId = data.channel
|
||||
this.feedId = data.feed
|
||||
this.url = data.url
|
||||
this.referrer = data.referrer
|
||||
this.userAgent = data.user_agent
|
||||
this.quality = data.quality
|
||||
}
|
||||
|
||||
withChannel(channelsKeyById: Dictionary): this {
|
||||
if (!this.channelId) return this
|
||||
|
||||
this.channel = channelsKeyById.get(this.channelId)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
withFeed(feedsKeyById: Dictionary): this {
|
||||
if (!this.feedId) return this
|
||||
|
||||
this.feed = feedsKeyById.get(this.feedId)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getUUID(): string {
|
||||
return this.url
|
||||
}
|
||||
|
||||
getId(): string | undefined {
|
||||
if (!this.channelId) return undefined
|
||||
if (!this.feedId) return this.channelId
|
||||
|
||||
return `${this.channelId}@${this.feedId}`
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
channel: this.channelId,
|
||||
feed: this.feedId,
|
||||
url: this.url,
|
||||
referrer: this.referrer,
|
||||
user_agent: this.userAgent,
|
||||
quality: this.quality
|
||||
}
|
||||
}
|
||||
|
||||
getFieldset(): JsonDataViewerField[] {
|
||||
let fieldset = []
|
||||
|
||||
const data = this.toJSON()
|
||||
for (let key in data) {
|
||||
fieldset.push({
|
||||
name: key,
|
||||
value: data[key]
|
||||
})
|
||||
}
|
||||
|
||||
return fieldset
|
||||
}
|
||||
|
||||
getQuality(): string {
|
||||
if (!this.quality) return ''
|
||||
|
||||
return this.quality
|
||||
}
|
||||
|
||||
getVerticalResolution(): number {
|
||||
return parseInt(this.getQuality().replace(/p|i/, ''))
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
if (!this.channel) return ''
|
||||
if (!this.feed) return this.channel.name
|
||||
|
||||
return `${this.channel.name} ${this.feed.name}`
|
||||
}
|
||||
|
||||
getPlaylistLink(): Link {
|
||||
if (!this.channel) return ''
|
||||
|
||||
const link = new Link(this.url)
|
||||
|
||||
link.title = this.getTitle()
|
||||
link.attrs = {
|
||||
'tvg-id': this.getId(),
|
||||
'tvg-logo': this.channel.logoUrl,
|
||||
'group-title': this.channel
|
||||
.getCategories()
|
||||
.map((category: Category) => category.name)
|
||||
.sort()
|
||||
.join(';')
|
||||
}
|
||||
|
||||
if (this.userAgent) {
|
||||
link.attrs['user-agent'] = this.userAgent
|
||||
link.vlcOpts['http-user-agent'] = this.userAgent
|
||||
}
|
||||
|
||||
if (this.referrer) {
|
||||
link.attrs['referrer'] = this.referrer
|
||||
link.vlcOpts['http-referrer'] = this.referrer
|
||||
}
|
||||
|
||||
return link
|
||||
}
|
||||
|
||||
serialize(): StreamSerializedData {
|
||||
return {
|
||||
channelId: this.channelId,
|
||||
feedId: this.feedId,
|
||||
url: this.url,
|
||||
referrer: this.referrer,
|
||||
userAgent: this.userAgent,
|
||||
quality: this.quality
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: StreamSerializedData): this {
|
||||
this.channelId = data.channelId
|
||||
this.feedId = data.feedId
|
||||
this.url = data.url
|
||||
this.referrer = data.referrer
|
||||
this.userAgent = data.userAgent
|
||||
this.quality = data.quality
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
31
src/models/subdivision.ts
Normal file
31
src/models/subdivision.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import type { SubdivisionData, SubdivisionSerializedData } from '~/types/subdivision'
|
||||
|
||||
export class Subdivision {
|
||||
code: string
|
||||
name: string
|
||||
countryCode: string
|
||||
|
||||
constructor(data?: SubdivisionData) {
|
||||
if (!data) return
|
||||
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.countryCode = data.country
|
||||
}
|
||||
|
||||
serialize(): SubdivisionSerializedData {
|
||||
return {
|
||||
code: this.code,
|
||||
name: this.name,
|
||||
countryCode: this.countryCode
|
||||
}
|
||||
}
|
||||
|
||||
deserialize(data: SubdivisionSerializedData): this {
|
||||
this.code = data.code
|
||||
this.name = data.name
|
||||
this.countryCode = data.countryCode
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue