Files
iptv/scripts/commands/playlist/validate.ts

144 lines
4.4 KiB
TypeScript
Raw Normal View History

2025-10-08 21:25:22 +03:00
import { Logger, Collection, Dictionary } from '@freearhey/core'
import { Storage } from '@freearhey/storage-js'
import { PlaylistParser } from '../../core'
import { data, loadData } from '../../api'
import { ROOT_DIR } from '../../constants'
2026-03-11 07:08:31 +03:00
import { isURI } from '../../utils.js'
2025-10-08 21:25:22 +03:00
import { Stream } from '../../models'
import * as sdk from '@iptv-org/sdk'
import { program } from 'commander'
import chalk from 'chalk'
program.argument('[filepath...]', 'Path to file to validate').parse(process.argv)
type LogItem = {
type: string
line: number
message: string
}
async function main() {
const logger = new Logger()
2026-03-16 02:28:25 +03:00
const rootStorage = new Storage(ROOT_DIR)
const parser = new PlaylistParser({
storage: rootStorage
})
2025-10-08 21:25:22 +03:00
logger.info('loading data from api...')
await loadData()
logger.info('loading streams...')
2026-03-16 02:28:25 +03:00
const selectedFiles = program.args.length
? program.args
: await rootStorage.list('streams/**/*.m3u')
const selectedStreams = await parser.parse(selectedFiles)
logger.info(`found ${selectedStreams.count()} streams`)
logger.info('creating buffer...')
const files = await rootStorage.list('streams/**/*.m3u')
2025-10-08 21:25:22 +03:00
const streams = await parser.parse(files)
2026-03-16 02:28:25 +03:00
const buffer = new Dictionary<Stream>()
streams.forEach((stream: Stream) => {
if (!selectedFiles.includes(stream.filepath)) {
buffer.set(stream.url, stream)
}
})
2025-10-08 21:25:22 +03:00
2025-12-04 18:32:25 +03:00
const errors = new Collection()
const warnings = new Collection()
2026-03-16 02:28:25 +03:00
const streamsGroupedByFilepath = selectedStreams.groupBy((stream: Stream) => stream.getFilepath())
2025-10-08 21:25:22 +03:00
for (const filepath of streamsGroupedByFilepath.keys()) {
const streams = streamsGroupedByFilepath.get(filepath)
if (!streams) continue
const log = new Collection<LogItem>()
streams.forEach((stream: Stream) => {
if (stream.channel) {
const channel = data.channelsKeyById.get(stream.channel)
if (!channel) {
log.add({
type: 'warning',
line: stream.getLine(),
message: `"${stream.tvgId}" is not in the database`
})
}
}
2026-03-16 02:28:25 +03:00
const isDuplicate = stream.url && buffer.has(stream.url)
if (isDuplicate) {
const origin = buffer.get(stream.url)
2025-10-08 21:25:22 +03:00
log.add({
2026-03-16 02:28:25 +03:00
type: 'error',
2025-10-08 21:25:22 +03:00
line: stream.getLine(),
2026-03-16 02:28:25 +03:00
message: `"${stream.url}" is already in the "${origin.filepath}"`
2025-10-08 21:25:22 +03:00
})
} else {
2026-03-16 02:28:25 +03:00
buffer.set(stream.url, stream)
2025-10-08 21:25:22 +03:00
}
2026-03-11 07:08:31 +03:00
if (!isURI(stream.url)) {
log.add({
type: 'error',
line: stream.getLine(),
message: `"${stream.url}" is not a valid URL`
})
}
2025-10-08 21:25:22 +03:00
if (stream.channel) {
const blocklistRecords = new Collection(
data.blocklistRecordsGroupedByChannel.get(stream.channel)
)
blocklistRecords.forEach((blocklistRecord: sdk.Models.BlocklistRecord) => {
if (blocklistRecord.reason === 'dmca') {
log.add({
type: 'error',
line: stream.getLine(),
message: `"${blocklistRecord.channel}" is on the blocklist due to claims of copyright holders (${blocklistRecord.ref})`
})
} else if (blocklistRecord.reason === 'nsfw') {
log.add({
type: 'error',
line: stream.getLine(),
message: `"${blocklistRecord.channel}" is on the blocklist due to NSFW content (${blocklistRecord.ref})`
})
}
})
}
})
if (log.isNotEmpty()) {
console.log(`\n${chalk.underline(filepath)}`)
log.forEach((logItem: LogItem) => {
const position = logItem.line.toString().padEnd(6, ' ')
const type = logItem.type.padEnd(9, ' ')
const status = logItem.type === 'error' ? chalk.red(type) : chalk.yellow(type)
console.log(` ${chalk.gray(position)}${status}${logItem.message}`)
})
2025-12-04 18:32:25 +03:00
log.forEach((logItem: LogItem) => {
if (logItem.type === 'error') errors.add(logItem)
else if (logItem.type === 'warning') warnings.add(logItem)
})
2025-10-08 21:25:22 +03:00
}
}
if (errors.count() || warnings.count()) {
console.log(
chalk.red(
`\n${
errors.count() + warnings.count()
} problems (${errors.count()} errors, ${warnings.count()} warnings)`
)
)
if (errors.count()) {
process.exit(1)
}
}
}
main()