Update scripts

This commit is contained in:
freearhey
2025-12-04 18:32:25 +03:00
parent a27ba0c7d0
commit 20d3ecc074
9 changed files with 60 additions and 85 deletions

View File

@@ -1,5 +1,5 @@
import { PlaylistParser, StreamTester, CliTable } from '../../core' import { PlaylistParser, StreamTester, CliTable } from '../../core'
import type { TestResult } from '../../core/streamTester' import type { StreamTesterResult } from '../../core/streamTester'
import { ROOT_DIR, STREAMS_DIR } from '../../constants' import { ROOT_DIR, STREAMS_DIR } from '../../constants'
import { Logger, Collection } from '@freearhey/core' import { Logger, Collection } from '@freearhey/core'
import { program, OptionValues } from 'commander' import { program, OptionValues } from 'commander'
@@ -92,10 +92,10 @@ async function runTest(stream: Stream) {
const key = stream.getUniqKey() const key = stream.getUniqKey()
results[key] = chalk.white('LOADING...') results[key] = chalk.white('LOADING...')
const result: TestResult = await tester.test(stream) const result: StreamTesterResult = await tester.test(stream)
let status = '' let status = ''
const errorStatusCodes = ['ENOTFOUND', 'HTTP_404_NOT_FOUND'] const errorStatusCodes = ['ENOTFOUND', 'HTTP_404_NOT_FOUND', 'HTTP_404_UNKONWN_ERROR']
if (result.status.ok) status = chalk.green('OK') if (result.status.ok) status = chalk.green('OK')
else if (errorStatusCodes.includes(result.status.code)) { else if (errorStatusCodes.includes(result.status.code)) {
status = chalk.red(result.status.code) status = chalk.red(result.status.code)
@@ -144,7 +144,7 @@ function drawTable() {
} }
} }
function onFinish(error: Error) { function onFinish(error: Error | null | undefined) {
clearInterval(interval) clearInterval(interval)
if (error) { if (error) {

View File

@@ -71,9 +71,9 @@ async function removeStreams({
requests.forEach((issue: Issue) => { requests.forEach((issue: Issue) => {
const data = issue.data const data = issue.data
if (data.missing('streamUrl')) return if (data.missing('stream_url')) return
const streamUrls = data.getString('streamUrl') || '' const streamUrls = data.getString('stream_url') || ''
let changed = false let changed = false
streamUrls streamUrls
@@ -104,14 +104,14 @@ async function editStreams({
requests.forEach((issue: Issue) => { requests.forEach((issue: Issue) => {
const data = issue.data const data = issue.data
if (data.missing('streamUrl')) return if (data.missing('stream_url')) return
const stream: Stream = streams.first( const stream: Stream = streams.first(
(_stream: Stream) => _stream.url === data.getString('streamUrl') (_stream: Stream) => _stream.url === data.getString('stream_url')
) )
if (!stream) return if (!stream) return
const streamId = data.getString('streamId') || '' const streamId = data.getString('stream_id') || ''
const [channelId, feedId] = streamId.split('@') const [channelId, feedId] = streamId.split('@')
if (channelId) { if (channelId) {
@@ -138,12 +138,12 @@ async function addStreams({
) )
requests.forEach((issue: Issue) => { requests.forEach((issue: Issue) => {
const data = issue.data const data = issue.data
if (data.missing('streamId') || data.missing('streamUrl')) return if (data.missing('stream_id') || data.missing('stream_url')) return
if (streams.includes((_stream: Stream) => _stream.url === data.getString('streamUrl'))) return if (streams.includes((_stream: Stream) => _stream.url === data.getString('stream_url'))) return
const streamUrl = data.getString('streamUrl') || '' const streamUrl = data.getString('stream_url') || ''
if (!isURI(streamUrl)) return if (!isURI(streamUrl)) return
const streamId = data.getString('streamId') || '' const streamId = data.getString('stream_id') || ''
const [channelId, feedId] = streamId.split('@') const [channelId, feedId] = streamId.split('@')
const channel: sdk.Models.Channel | undefined = apiData.channelsKeyById.get(channelId) const channel: sdk.Models.Channel | undefined = apiData.channelsKeyById.get(channelId)
@@ -151,9 +151,8 @@ async function addStreams({
const label = data.getString('label') || '' const label = data.getString('label') || ''
const quality = data.getString('quality') || null const quality = data.getString('quality') || null
const httpUserAgent = data.getString('httpUserAgent') || null const httpUserAgent = data.getString('http_user_agent') || null
const httpReferrer = data.getString('httpReferrer') || null const httpReferrer = data.getString('http_referrer') || null
const directives = data.getArray('directives') || []
const stream = new Stream({ const stream = new Stream({
channel: channelId, channel: channelId,
@@ -166,7 +165,7 @@ async function addStreams({
}) })
stream.label = label stream.label = label
stream.setDirectives(directives).updateTitle().updateFilepath() stream.updateTitle().updateFilepath()
streams.add(stream) streams.add(stream)
processedIssues.add(issue.number) processedIssues.add(issue.number)

View File

@@ -31,8 +31,8 @@ async function main() {
const streams = await parser.parse(files) const streams = await parser.parse(files)
logger.info(`found ${streams.count()} streams`) logger.info(`found ${streams.count()} streams`)
let errors = new Collection() const errors = new Collection()
let warnings = new Collection() const warnings = new Collection()
const streamsGroupedByFilepath = streams.groupBy((stream: Stream) => stream.getFilepath()) const streamsGroupedByFilepath = streams.groupBy((stream: Stream) => stream.getFilepath())
for (const filepath of streamsGroupedByFilepath.keys()) { for (const filepath of streamsGroupedByFilepath.keys()) {
const streams = streamsGroupedByFilepath.get(filepath) const streams = streamsGroupedByFilepath.get(filepath)
@@ -97,8 +97,10 @@ async function main() {
console.log(` ${chalk.gray(position)}${status}${logItem.message}`) console.log(` ${chalk.gray(position)}${status}${logItem.message}`)
}) })
errors = errors.concat(log.filter((logItem: LogItem) => logItem.type === 'error')) log.forEach((logItem: LogItem) => {
warnings = warnings.concat(log.filter((logItem: LogItem) => logItem.type === 'warning')) if (logItem.type === 'error') errors.add(logItem)
else if (logItem.type === 'warning') warnings.add(logItem)
})
} }
} }

View File

@@ -47,7 +47,7 @@ async function main() {
issue.labels.find((label: string) => label === 'streams:remove') issue.labels.find((label: string) => label === 'streams:remove')
) )
removeRequests.forEach((issue: Issue) => { removeRequests.forEach((issue: Issue) => {
const streamUrls = issue.data.getArray('streamUrl') || [] const streamUrls = issue.data.getArray('stream_url') || []
if (!streamUrls.length) { if (!streamUrls.length) {
const result = { const result = {
@@ -82,8 +82,8 @@ async function main() {
const addRequests = issues.filter(issue => issue.labels.includes('streams:add')) const addRequests = issues.filter(issue => issue.labels.includes('streams:add'))
const addRequestsBuffer = new Dictionary() const addRequestsBuffer = new Dictionary()
addRequests.forEach((issue: Issue) => { addRequests.forEach((issue: Issue) => {
const streamId = issue.data.getString('streamId') || '' const streamId = issue.data.getString('stream_id') || ''
const streamUrl = issue.data.getString('streamUrl') || '' const streamUrl = issue.data.getString('stream_url') || ''
const [channelId] = streamId.split('@') const [channelId] = streamId.split('@')
const result = { const result = {
@@ -114,8 +114,8 @@ async function main() {
issue.labels.find((label: string) => label === 'streams:edit') issue.labels.find((label: string) => label === 'streams:edit')
) )
editRequests.forEach((issue: Issue) => { editRequests.forEach((issue: Issue) => {
const streamId = issue.data.getString('streamId') || '' const streamId = issue.data.getString('stream_id') || ''
const streamUrl = issue.data.getString('streamUrl') || '' const streamUrl = issue.data.getString('stream_url') || ''
const [channelId] = streamId.split('@') const [channelId] = streamId.split('@')
const result = { const result = {
@@ -140,7 +140,7 @@ async function main() {
) )
const channelSearchRequestsBuffer = new Dictionary() const channelSearchRequestsBuffer = new Dictionary()
channelSearchRequests.forEach((issue: Issue) => { channelSearchRequests.forEach((issue: Issue) => {
const streamId = issue.data.getString('streamId') || issue.data.getString('channelId') || '' const streamId = issue.data.getString('stream_id') || issue.data.getString('channel_id') || ''
const [channelId, feedId] = streamId.split('@') const [channelId, feedId] = streamId.split('@')
const result = { const result = {

View File

@@ -3,20 +3,18 @@ import { IssueData } from './issueData'
import { Issue } from '../models' import { Issue } from '../models'
const FIELDS = new Dictionary({ const FIELDS = new Dictionary({
'Stream ID': 'streamId', 'Stream ID': 'stream_id',
'Channel ID': 'channelId', 'Channel ID': 'channel_id',
'Feed ID': 'feedId', 'Feed ID': 'feed_id',
'Stream URL': 'streamUrl', 'Stream URL': 'stream_url',
'New Stream URL': 'newStreamUrl',
Label: 'label', Label: 'label',
Quality: 'quality', Quality: 'quality',
'HTTP User-Agent': 'httpUserAgent', 'HTTP User-Agent': 'http_user_agent',
'HTTP User Agent': 'httpUserAgent', 'HTTP User Agent': 'http_user_agent',
'HTTP Referrer': 'httpReferrer', 'HTTP Referrer': 'http_referrer',
'What happened to the stream?': 'reason', 'What happened to the stream?': 'reason',
Reason: 'reason', Reason: 'reason',
Notes: 'notes', Notes: 'notes'
Directives: 'directives'
}) })
export class IssueParser { export class IssueParser {

View File

@@ -20,7 +20,9 @@ export class PlaylistParser {
for (const filepath of files) { for (const filepath of files) {
if (!this.storage.existsSync(filepath)) continue if (!this.storage.existsSync(filepath)) continue
const _parsed: Collection<Stream> = await this.parseFile(filepath) const _parsed: Collection<Stream> = await this.parseFile(filepath)
parsed.concat(_parsed) _parsed.forEach((item: Stream) => {
parsed.add(item)
})
} }
return parsed return parsed

View File

@@ -25,7 +25,9 @@ export class LanguagesGenerator implements Generator {
const languages = new Collection<sdk.Models.Language>() const languages = new Collection<sdk.Models.Language>()
streams.forEach((stream: Stream) => { streams.forEach((stream: Stream) => {
languages.concat(stream.getLanguages()) stream.getLanguages().forEach((language: sdk.Models.Language) => {
languages.add(language)
})
}) })
languages languages

View File

@@ -7,7 +7,6 @@ import { data } from '../api'
import path from 'node:path' import path from 'node:path'
export class Stream extends sdk.Models.Stream { export class Stream extends sdk.Models.Stream {
directives: Collection<string>
filepath?: string filepath?: string
line?: number line?: number
groupTitle: string = 'Undefined' groupTitle: string = 'Undefined'
@@ -19,18 +18,14 @@ export class Stream extends sdk.Models.Stream {
const data = { const data = {
label: issueData.getString('label'), label: issueData.getString('label'),
quality: issueData.getString('quality'), quality: issueData.getString('quality'),
httpUserAgent: issueData.getString('httpUserAgent'), httpUserAgent: issueData.getString('http_user_agent'),
httpReferrer: issueData.getString('httpReferrer'), httpReferrer: issueData.getString('http_referrer')
newStreamUrl: issueData.getString('newStreamUrl'),
directives: issueData.getArray('directives')
} }
if (data.label !== undefined) this.label = data.label if (data.label !== undefined) this.label = data.label
if (data.quality !== undefined) this.quality = data.quality if (data.quality !== undefined) this.quality = data.quality
if (data.httpUserAgent !== undefined) this.user_agent = data.httpUserAgent if (data.httpUserAgent !== undefined) this.user_agent = data.httpUserAgent
if (data.httpReferrer !== undefined) this.referrer = data.httpReferrer if (data.httpReferrer !== undefined) this.referrer = data.httpReferrer
if (data.newStreamUrl !== undefined) this.url = data.newStreamUrl
if (data.directives !== undefined) this.setDirectives(data.directives)
return this return this
} }
@@ -54,24 +49,6 @@ export class Stream extends sdk.Models.Stream {
return { title, label, quality } return { title, label, quality }
} }
function parseDirectives(string: string): Collection<string> {
const directives = new Collection<string>()
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.name) throw new Error('"name" property is required')
if (!data.url) throw new Error('"url" property is required') if (!data.url) throw new Error('"url" property is required')
@@ -91,7 +68,6 @@ export class Stream extends sdk.Models.Stream {
stream.tvgId = data.tvg.id stream.tvgId = data.tvg.id
stream.line = data.line stream.line = data.line
stream.label = label || null stream.label = label || null
stream.directives = parseDirectives(data.raw)
return stream return stream
} }
@@ -235,7 +211,9 @@ export class Stream extends sdk.Models.Stream {
.intersects(new Collection<string>(region.countries)) .intersects(new Collection<string>(region.countries))
.isNotEmpty() .isNotEmpty()
) )
regions.concat(relatedRegions) relatedRegions.forEach(region => {
regions.add(region)
})
break break
} }
case 'country': { case 'country': {
@@ -246,7 +224,9 @@ export class Stream extends sdk.Models.Stream {
(code: string) => code === country.code (code: string) => code === country.code
) )
) )
regions.concat(countryRegions) countryRegions.forEach(region => {
regions.add(region)
})
break break
} }
case 'subdivision': { case 'subdivision': {
@@ -257,7 +237,9 @@ export class Stream extends sdk.Models.Stream {
(code: string) => code === subdivision.country (code: string) => code === subdivision.country
) )
) )
regions.concat(subdivisionRegions) subdivisionRegions.forEach(region => {
regions.add(region)
})
break break
} }
case 'city': { case 'city': {
@@ -268,7 +250,9 @@ export class Stream extends sdk.Models.Stream {
(code: string) => code === city.country (code: string) => code === city.country
) )
) )
regions.concat(cityRegions) cityRegions.forEach(region => {
regions.add(region)
})
break break
} }
} }
@@ -306,14 +290,6 @@ export class Stream extends sdk.Models.Stream {
return !!found return !!found
} }
setDirectives(directives: string[]): this {
this.directives = new Collection(directives).filter((directive: string) =>
/^(#KODIPROP|#EXTVLCOPT)/.test(directive)
)
return this
}
updateTvgId(): this { updateTvgId(): this {
if (!this.channel) return this if (!this.channel) return this
if (this.feed) { if (this.feed) {
@@ -418,20 +394,16 @@ export class Stream extends sdk.Models.Stream {
output += ` tvg-logo="${this.getTvgLogo()}" group-title="${this.groupTitle}"` output += ` tvg-logo="${this.getTvgLogo()}" group-title="${this.groupTitle}"`
} }
output += `,${this.getFullTitle()}`
if (this.referrer) { if (this.referrer) {
output += ` http-referrer="${this.referrer}"` output += `\r\n#EXTVLCOPT:http-referrer=${this.referrer}`
} }
if (this.user_agent) { if (this.user_agent) {
output += ` http-user-agent="${this.user_agent}"` output += `\r\n#EXTVLCOPT:http-user-agent=${this.user_agent}`
} }
output += `,${this.getFullTitle()}`
this.directives.forEach((prop: string) => {
output += `\r\n${prop}`
})
output += `\r\n${this.url}` output += `\r\n${this.url}`
return output return output