mirror of
https://github.com/iptv-org/iptv
synced 2025-12-17 02:47:33 -05:00
Update scripts
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Collection, Dictionary } from '@freearhey/core'
|
||||
import { Category, Country, Feed, Guide, Stream, Subdivision } from './index'
|
||||
import { Category, Country, Feed, Guide, Logo, Stream, Subdivision } from './index'
|
||||
import type { ChannelData, ChannelSearchableData, ChannelSerializedData } from '../types/channel'
|
||||
|
||||
export class Channel {
|
||||
@@ -19,9 +19,10 @@ export class Channel {
|
||||
launched?: string
|
||||
closed?: string
|
||||
replacedBy?: string
|
||||
isClosed: boolean
|
||||
website?: string
|
||||
logo: string
|
||||
feeds?: Collection
|
||||
logos: Collection = new Collection()
|
||||
|
||||
constructor(data?: ChannelData) {
|
||||
if (!data) return
|
||||
@@ -40,7 +41,7 @@ export class Channel {
|
||||
this.closed = data.closed || undefined
|
||||
this.replacedBy = data.replaced_by || undefined
|
||||
this.website = data.website || undefined
|
||||
this.logo = data.logo
|
||||
this.isClosed = !!data.closed || !!data.replaced_by
|
||||
}
|
||||
|
||||
withSubdivision(subdivisionsKeyByCode: Dictionary): this {
|
||||
@@ -71,6 +72,12 @@ export class Channel {
|
||||
return this
|
||||
}
|
||||
|
||||
withLogos(logosGroupedByChannelId: Dictionary): this {
|
||||
if (this.id) this.logos = new Collection(logosGroupedByChannelId.get(this.id))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getCountry(): Country | undefined {
|
||||
return this.country
|
||||
}
|
||||
@@ -142,6 +149,35 @@ export class Channel {
|
||||
return this.isNSFW === false
|
||||
}
|
||||
|
||||
getLogos(): Collection {
|
||||
function feed(logo: Logo): number {
|
||||
if (!logo.feed) return 1
|
||||
if (logo.feed.isMain) return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function format(logo: Logo): number {
|
||||
const levelByFormat = { SVG: 0, PNG: 3, APNG: 1, WebP: 1, AVIF: 1, JPEG: 2, GIF: 1 }
|
||||
|
||||
return logo.format ? levelByFormat[logo.format] : 0
|
||||
}
|
||||
|
||||
function size(logo: Logo): number {
|
||||
return Math.abs(512 - logo.width) + Math.abs(512 - logo.height)
|
||||
}
|
||||
|
||||
return this.logos.orderBy([feed, format, size], ['desc', 'desc', 'asc'], false)
|
||||
}
|
||||
|
||||
getLogo(): Logo | undefined {
|
||||
return this.getLogos().first()
|
||||
}
|
||||
|
||||
hasLogo(): boolean {
|
||||
return this.getLogos().notEmpty()
|
||||
}
|
||||
|
||||
getSearchable(): ChannelSearchableData {
|
||||
return {
|
||||
id: this.id,
|
||||
@@ -171,8 +207,7 @@ export class Channel {
|
||||
launched: this.launched,
|
||||
closed: this.closed,
|
||||
replacedBy: this.replacedBy,
|
||||
website: this.website,
|
||||
logo: this.logo
|
||||
website: this.website
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +227,6 @@ export class Channel {
|
||||
this.closed = data.closed
|
||||
this.replacedBy = data.replacedBy
|
||||
this.website = data.website
|
||||
this.logo = data.logo
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export * from './feed'
|
||||
export * from './guide'
|
||||
export * from './issue'
|
||||
export * from './language'
|
||||
export * from './logo'
|
||||
export * from './playlist'
|
||||
export * from './region'
|
||||
export * from './stream'
|
||||
|
||||
40
scripts/models/logo.ts
Normal file
40
scripts/models/logo.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Collection, type Dictionary } from '@freearhey/core'
|
||||
import type { LogoData } from '../types/logo'
|
||||
import { type Feed } from './feed'
|
||||
|
||||
export class Logo {
|
||||
channelId: string
|
||||
feedId?: string
|
||||
feed: Feed
|
||||
tags: Collection
|
||||
width: number
|
||||
height: number
|
||||
format?: string
|
||||
url: string
|
||||
|
||||
constructor(data?: LogoData) {
|
||||
if (!data) return
|
||||
|
||||
this.channelId = data.channel
|
||||
this.feedId = data.feed || undefined
|
||||
this.tags = new Collection(data.tags)
|
||||
this.width = data.width
|
||||
this.height = data.height
|
||||
this.format = data.format || undefined
|
||||
this.url = data.url
|
||||
}
|
||||
|
||||
withFeed(feedsKeyById: Dictionary): this {
|
||||
if (!this.feedId) return this
|
||||
|
||||
this.feed = feedsKeyById.get(this.feedId)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
getStreamId(): string {
|
||||
if (!this.feedId) return this.channelId
|
||||
|
||||
return `${this.channelId}@${this.feedId}`
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Feed, Channel, Category, Region, Subdivision, Country, Language } from './index'
|
||||
import { Feed, Channel, Category, Region, Subdivision, Country, Language, Logo } from './index'
|
||||
import { URL, Collection, Dictionary } from '@freearhey/core'
|
||||
import type { StreamData } from '../types/stream'
|
||||
import parser from 'iptv-playlist-parser'
|
||||
import { IssueData } from '../core'
|
||||
import path from 'node:path'
|
||||
|
||||
export class Stream {
|
||||
name?: string
|
||||
@@ -12,6 +13,7 @@ export class Stream {
|
||||
channel?: Channel
|
||||
feedId?: string
|
||||
feed?: Feed
|
||||
logos: Collection = new Collection()
|
||||
filepath?: string
|
||||
line?: number
|
||||
label?: string
|
||||
@@ -21,6 +23,7 @@ export class Stream {
|
||||
userAgent?: string
|
||||
groupTitle: string = 'Undefined'
|
||||
removed: boolean = false
|
||||
directives: Collection = new Collection()
|
||||
|
||||
constructor(data?: StreamData) {
|
||||
if (!data) return
|
||||
@@ -38,6 +41,7 @@ export class Stream {
|
||||
this.verticalResolution = verticalResolution || undefined
|
||||
this.isInterlaced = isInterlaced || undefined
|
||||
this.label = data.label || undefined
|
||||
this.directives = new Collection(data.directives)
|
||||
}
|
||||
|
||||
update(issueData: IssueData): this {
|
||||
@@ -46,7 +50,8 @@ export class Stream {
|
||||
quality: issueData.getString('quality'),
|
||||
httpUserAgent: issueData.getString('httpUserAgent'),
|
||||
httpReferrer: issueData.getString('httpReferrer'),
|
||||
newStreamUrl: issueData.getString('newStreamUrl')
|
||||
newStreamUrl: issueData.getString('newStreamUrl'),
|
||||
directives: issueData.getArray('directives')
|
||||
}
|
||||
|
||||
if (data.label !== undefined) this.label = data.label
|
||||
@@ -54,11 +59,43 @@ export class Stream {
|
||||
if (data.httpUserAgent !== undefined) this.userAgent = data.httpUserAgent
|
||||
if (data.httpReferrer !== undefined) this.referrer = data.httpReferrer
|
||||
if (data.newStreamUrl !== undefined) this.url = data.newStreamUrl
|
||||
if (data.directives !== undefined) this.directives = new Collection(data.directives)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fromPlaylistItem(data: parser.PlaylistItem): this {
|
||||
function parseTitle(title: string): {
|
||||
name: string
|
||||
label: string
|
||||
quality: string
|
||||
} {
|
||||
const [, label] = title.match(/ \[(.*)\]$/) || [null, '']
|
||||
title = title.replace(new RegExp(` \\[${escapeRegExp(label)}\\]$`), '')
|
||||
const [, quality] = title.match(/ \(([0-9]+p)\)$/) || [null, '']
|
||||
title = title.replace(new RegExp(` \\(${quality}\\)$`), '')
|
||||
|
||||
return { name: title, label, quality }
|
||||
}
|
||||
|
||||
function parseDirectives(string: string) {
|
||||
let directives = new Collection()
|
||||
|
||||
if (!string) return directives
|
||||
|
||||
const supportedDirectives = ['#EXTVLCOPT', '#KODIPROP']
|
||||
const lines = string.split('\r\n')
|
||||
const regex = new RegExp(`^${supportedDirectives.join('|')}`, 'i')
|
||||
|
||||
lines.forEach((line: string) => {
|
||||
if (regex.test(line)) {
|
||||
directives.add(line.trim())
|
||||
}
|
||||
})
|
||||
|
||||
return directives
|
||||
}
|
||||
|
||||
if (!data.name) throw new Error('"name" property is required')
|
||||
if (!data.url) throw new Error('"url" property is required')
|
||||
|
||||
@@ -77,6 +114,7 @@ export class Stream {
|
||||
this.url = data.url
|
||||
this.referrer = data.http.referrer || undefined
|
||||
this.userAgent = data.http['user-agent'] || undefined
|
||||
this.directives = parseDirectives(data.raw)
|
||||
|
||||
return this
|
||||
}
|
||||
@@ -99,6 +137,12 @@ export class Stream {
|
||||
return this
|
||||
}
|
||||
|
||||
withLogos(logosGroupedByStreamId: Dictionary): this {
|
||||
if (this.id) this.logos = new Collection(logosGroupedByStreamId.get(this.id))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
setId(id: string): this {
|
||||
this.id = id
|
||||
|
||||
@@ -130,6 +174,12 @@ export class Stream {
|
||||
return this.line || -1
|
||||
}
|
||||
|
||||
getFilename(): string {
|
||||
if (!this.filepath) return ''
|
||||
|
||||
return path.basename(this.filepath)
|
||||
}
|
||||
|
||||
setFilepath(filepath: string): this {
|
||||
this.filepath = filepath
|
||||
|
||||
@@ -294,8 +344,35 @@ export class Stream {
|
||||
return this.feed ? this.feed.isInternational() : false
|
||||
}
|
||||
|
||||
getLogo(): string {
|
||||
return this?.channel?.logo || ''
|
||||
getLogos(): Collection {
|
||||
function format(logo: Logo): number {
|
||||
const levelByFormat = { SVG: 0, PNG: 3, APNG: 1, WebP: 1, AVIF: 1, JPEG: 2, GIF: 1 }
|
||||
|
||||
return logo.format ? levelByFormat[logo.format] : 0
|
||||
}
|
||||
|
||||
function size(logo: Logo): number {
|
||||
return Math.abs(512 - logo.width) + Math.abs(512 - logo.height)
|
||||
}
|
||||
|
||||
return this.logos.orderBy([format, size], ['desc', 'asc'], false)
|
||||
}
|
||||
|
||||
getLogo(): Logo | undefined {
|
||||
return this.getLogos().first()
|
||||
}
|
||||
|
||||
hasLogo(): boolean {
|
||||
return this.getLogos().notEmpty()
|
||||
}
|
||||
|
||||
getLogoUrl(): string {
|
||||
let logo: Logo | undefined
|
||||
|
||||
if (this.hasLogo()) logo = this.getLogo()
|
||||
else logo = this?.channel?.getLogo()
|
||||
|
||||
return logo ? logo.url : ''
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
@@ -339,7 +416,7 @@ export class Stream {
|
||||
let output = `#EXTINF:-1 tvg-id="${this.getId()}"`
|
||||
|
||||
if (options.public) {
|
||||
output += ` tvg-logo="${this.getLogo()}" group-title="${this.groupTitle}"`
|
||||
output += ` tvg-logo="${this.getLogoUrl()}" group-title="${this.groupTitle}"`
|
||||
}
|
||||
|
||||
if (this.referrer) {
|
||||
@@ -352,13 +429,9 @@ export class Stream {
|
||||
|
||||
output += `,${this.getTitle()}`
|
||||
|
||||
if (this.referrer) {
|
||||
output += `\r\n#EXTVLCOPT:http-referrer=${this.referrer}`
|
||||
}
|
||||
|
||||
if (this.userAgent) {
|
||||
output += `\r\n#EXTVLCOPT:http-user-agent=${this.userAgent}`
|
||||
}
|
||||
this.directives.forEach((prop: string) => {
|
||||
output += `\r\n${prop}`
|
||||
})
|
||||
|
||||
output += `\r\n${this.url}`
|
||||
|
||||
@@ -366,19 +439,6 @@ export class Stream {
|
||||
}
|
||||
}
|
||||
|
||||
function parseTitle(title: string): {
|
||||
name: string
|
||||
label: string
|
||||
quality: string
|
||||
} {
|
||||
const [, label] = title.match(/ \[(.*)\]$/) || [null, '']
|
||||
title = title.replace(new RegExp(` \\[${escapeRegExp(label)}\\]$`), '')
|
||||
const [, quality] = title.match(/ \(([0-9]+p)\)$/) || [null, '']
|
||||
title = title.replace(new RegExp(` \\(${quality}\\)$`), '')
|
||||
|
||||
return { name: title, label, quality }
|
||||
}
|
||||
|
||||
function escapeRegExp(text) {
|
||||
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user