mirror of
https://github.com/iptv-org/epg
synced 2026-05-07 01:46:59 -04:00
Update scripts
This commit is contained in:
144
scripts/commands/guides/update.ts
Normal file
144
scripts/commands/guides/update.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import { HTMLTableRow, HTMLTableDataItem, HTMLTableColumn } from '../../types/htmlTable'
|
||||||
|
import epgGrabber, { EPGGrabber } from 'epg-grabber'
|
||||||
|
import AxiosMockAdapter from 'axios-mock-adapter'
|
||||||
|
import { Storage } from '@freearhey/storage-js'
|
||||||
|
import { Channel, Worker } from '../../models'
|
||||||
|
import { Collection } from '@freearhey/core'
|
||||||
|
import { ROOT_DIR } from '../../constants'
|
||||||
|
import { Logger } from '@freearhey/core'
|
||||||
|
import { HTMLTable } from '../../core'
|
||||||
|
import epgParser from 'epg-parser'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const logger = new Logger({ level: process.env.NODE_ENV === 'test' ? -999 : 3 })
|
||||||
|
const rootStorage = new Storage(ROOT_DIR)
|
||||||
|
const workers = new Map<string, Worker>()
|
||||||
|
|
||||||
|
logger.info('loading workers.txt...')
|
||||||
|
const workersTxt = await rootStorage.load('workers.txt')
|
||||||
|
|
||||||
|
workersTxt.split('\r\n').forEach((host: string) => {
|
||||||
|
if (!host) return
|
||||||
|
|
||||||
|
const worker = new Worker({ host })
|
||||||
|
|
||||||
|
workers.set(host, worker)
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const worker of workers.values()) {
|
||||||
|
logger.info(`processing "${worker.host}"...`)
|
||||||
|
|
||||||
|
const client = axios.create({
|
||||||
|
baseURL: worker.getBaseUrl(),
|
||||||
|
timeout: 60000
|
||||||
|
})
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
const mock = new AxiosMockAdapter(client)
|
||||||
|
if (worker.host === 'example.com') {
|
||||||
|
mock.onGet('worker.json').reply(404)
|
||||||
|
} else {
|
||||||
|
const testStorage = new Storage('tests/__data__/input/guides_update')
|
||||||
|
mock.onGet('worker.json').reply(200, await testStorage.load('worker.json'))
|
||||||
|
mock.onGet('channels.xml').reply(200, await testStorage.load('channels.xml'))
|
||||||
|
mock.onGet('guide.xml').reply(200, await testStorage.load('guide.xml'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const workerJson = await client
|
||||||
|
.get('worker.json')
|
||||||
|
.then(res => res.data)
|
||||||
|
.catch(err => {
|
||||||
|
worker.status = err.status
|
||||||
|
logger.error(err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!workerJson) {
|
||||||
|
worker.status = 'MISSING_WORKER_CONFIG'
|
||||||
|
logger.error('Unable to load "workers.json"')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.channelsPath = workerJson.channels
|
||||||
|
worker.guidePath = workerJson.guide
|
||||||
|
|
||||||
|
if (!worker.channelsPath) {
|
||||||
|
worker.status = 'MISSING_CHANNELS_PATH'
|
||||||
|
logger.error('The "channels" property is missing from the workers config')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!worker.guidePath) {
|
||||||
|
worker.status = 'MISSING_GUIDE_PATH'
|
||||||
|
logger.error('The "guide" property is missing from the workers config')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelsXml = await client
|
||||||
|
.get(worker.channelsPath)
|
||||||
|
.then(res => res.data)
|
||||||
|
.catch(err => {
|
||||||
|
worker.status = err.status
|
||||||
|
logger.error(err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!channelsXml) continue
|
||||||
|
|
||||||
|
const parsedChannels = EPGGrabber.parseChannelsXML(channelsXml)
|
||||||
|
worker.channels = new Collection(parsedChannels).map(
|
||||||
|
(channel: epgGrabber.Channel) => new Channel(channel.toObject())
|
||||||
|
)
|
||||||
|
|
||||||
|
const guideXml = await client
|
||||||
|
.get(worker.guidePath)
|
||||||
|
.then(res => res.data)
|
||||||
|
.catch(err => {
|
||||||
|
worker.status = err.status
|
||||||
|
logger.error(err.message)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!guideXml) continue
|
||||||
|
|
||||||
|
const parsedGuide = epgParser.parse(guideXml)
|
||||||
|
worker.lastUpdated = parsedGuide.date
|
||||||
|
|
||||||
|
worker.status = 'OK'
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('creating guides table...')
|
||||||
|
const rows = new Collection<HTMLTableRow>()
|
||||||
|
workers.forEach((worker: Worker) => {
|
||||||
|
rows.add(
|
||||||
|
new Collection<HTMLTableDataItem>([
|
||||||
|
{ value: worker.host },
|
||||||
|
{ value: worker.getStatusEmoji(), align: 'center' },
|
||||||
|
{ value: worker.getChannelsCount().toString(), align: 'right' },
|
||||||
|
{ value: worker.getLastUpdated(), align: 'left' },
|
||||||
|
{
|
||||||
|
value:
|
||||||
|
worker.status === 'OK'
|
||||||
|
? `<a href="${worker.getChannelsUrl()}">${worker.channelsPath}</a><br><a href="${worker.getGuideUrl()}">${worker.guidePath}</a>`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info('updating guides.md...')
|
||||||
|
const table = new HTMLTable(
|
||||||
|
rows,
|
||||||
|
new Collection<HTMLTableColumn>([
|
||||||
|
{ name: 'Host', align: 'left' },
|
||||||
|
{ name: 'Status', align: 'left' },
|
||||||
|
{ name: 'Channels', align: 'left' },
|
||||||
|
{ name: 'Last Updated', align: 'left' },
|
||||||
|
{ name: 'Links', align: 'left' }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
const guidesTemplate = await new Storage().load('scripts/templates/_guides.md')
|
||||||
|
const guidesContent = guidesTemplate.replace('_TABLE_', table.toString())
|
||||||
|
await rootStorage.save('GUIDES.md', guidesContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -3,3 +3,4 @@ export * from './issue'
|
|||||||
export * from './site'
|
export * from './site'
|
||||||
export * from './channel'
|
export * from './channel'
|
||||||
export * from './program'
|
export * from './program'
|
||||||
|
export * from './worker'
|
||||||
|
|||||||
68
scripts/models/worker.ts
Normal file
68
scripts/models/worker.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
|
import { Collection } from '@freearhey/core'
|
||||||
|
import { Channel } from './channel'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
|
export interface WorkerData {
|
||||||
|
host: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Worker {
|
||||||
|
host: string
|
||||||
|
channelsPath?: string
|
||||||
|
guidePath?: string
|
||||||
|
channels?: Collection<Channel>
|
||||||
|
status?: string
|
||||||
|
lastUpdated?: string
|
||||||
|
|
||||||
|
constructor(data: WorkerData) {
|
||||||
|
this.host = data.host
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaseUrl(): string {
|
||||||
|
return `https://${this.host}`
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigUrl(): string {
|
||||||
|
const url = new URL('worker.json', this.getBaseUrl())
|
||||||
|
|
||||||
|
return url.href
|
||||||
|
}
|
||||||
|
|
||||||
|
getChannelsUrl(): string {
|
||||||
|
if (!this.channelsPath) return ''
|
||||||
|
|
||||||
|
const url = new URL(this.channelsPath, this.getBaseUrl())
|
||||||
|
|
||||||
|
return url.href
|
||||||
|
}
|
||||||
|
|
||||||
|
getGuideUrl(): string {
|
||||||
|
if (!this.guidePath) return ''
|
||||||
|
|
||||||
|
const url = new URL(this.guidePath, this.getBaseUrl())
|
||||||
|
|
||||||
|
return url.href
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusEmoji(): string {
|
||||||
|
if (!this.status) return '⚪'
|
||||||
|
if (this.status === 'OK') return '🟢'
|
||||||
|
|
||||||
|
return '🔴'
|
||||||
|
}
|
||||||
|
|
||||||
|
getChannelsCount(): number {
|
||||||
|
if (!this.channels) return 0
|
||||||
|
|
||||||
|
return this.channels.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
getLastUpdated(): string {
|
||||||
|
if (!this.lastUpdated) return '-'
|
||||||
|
|
||||||
|
return dayjs().to(dayjs(this.lastUpdated))
|
||||||
|
}
|
||||||
|
}
|
||||||
5
scripts/templates/_guides.md
Normal file
5
scripts/templates/_guides.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Guides
|
||||||
|
|
||||||
|
_TABLE_
|
||||||
|
|
||||||
|
[How can I add my server to the list?](CONTRIBUTING.md#how-to-add-my-server-to-the-guides-md)
|
||||||
Reference in New Issue
Block a user