mirror of
https://github.com/iptv-org/iptv
synced 2026-05-07 01:57:21 -04:00
Update scripts
This commit is contained in:
@@ -82,7 +82,7 @@ async function main() {
|
||||
logger.info('adding the missing quality...')
|
||||
const progressBar = new cliProgress.SingleBar({
|
||||
clearOnComplete: true,
|
||||
format: `[{bar}] {percentage}% | {value}/{total}`
|
||||
format: '[{bar}] {percentage}% | {value}/{total}'
|
||||
})
|
||||
progressBar.start(streams.count(), 0)
|
||||
await eachLimit(streams.all(), options.parallel, async (stream: Stream) => {
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import { IssueLoader, PlaylistParser } from '../../core'
|
||||
import { isURI, getStreamInfo, loadIssues } from '../../utils'
|
||||
import { Playlist, Issue, Stream } from '../../models'
|
||||
import { loadData, data as apiData } from '../../api'
|
||||
import { Logger, Collection } from '@freearhey/core'
|
||||
import { isURI, getStreamInfo } from '../../utils'
|
||||
import { Storage } from '@freearhey/storage-js'
|
||||
import { STREAMS_DIR } from '../../constants'
|
||||
import { PlaylistParser } from '../../core'
|
||||
import * as sdk from '@iptv-org/sdk'
|
||||
|
||||
const processedIssues = new Collection()
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger({ level: -999 })
|
||||
const issueLoader = new IssueLoader()
|
||||
|
||||
logger.info('loading issues...')
|
||||
const issues = await issueLoader.load()
|
||||
const issues = await loadIssues()
|
||||
|
||||
logger.info('loading data from api...')
|
||||
await loadData()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { isURI, truncate, loadIssues, loadDiscussions } from '../../utils'
|
||||
import { Logger, Collection, Dictionary } from '@freearhey/core'
|
||||
import { IssueLoader, PlaylistParser } from '../../core'
|
||||
import { Storage } from '@freearhey/storage-js'
|
||||
import { isURI, truncate } from '../../utils'
|
||||
import { STREAMS_DIR } from '../../constants'
|
||||
import { Issue, Stream } from '../../models'
|
||||
import { Discussion, Issue, Stream } from '../../models'
|
||||
import { PlaylistParser } from '../../core'
|
||||
import { data, loadData } from '../../api'
|
||||
|
||||
const status = {
|
||||
@@ -22,11 +22,13 @@ const status = {
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger()
|
||||
const issueLoader = new IssueLoader()
|
||||
let report = new Collection()
|
||||
|
||||
logger.info('loading issues...')
|
||||
const issues = await issueLoader.load()
|
||||
const issues = await loadIssues()
|
||||
|
||||
logger.info('loading discussions...')
|
||||
const discussions = await loadDiscussions()
|
||||
|
||||
logger.info('loading data from api...')
|
||||
await loadData()
|
||||
@@ -135,16 +137,17 @@ async function main() {
|
||||
})
|
||||
|
||||
logger.info('checking channel search requests...')
|
||||
const channelSearchRequests = issues.filter(issue =>
|
||||
issue.labels.find((label: string) => label === 'channel search')
|
||||
const channelSearchRequests = discussions.filter(
|
||||
(discussion: Discussion) => discussion.category === 'Channel Search'
|
||||
)
|
||||
const channelSearchRequestsBuffer = new Dictionary()
|
||||
channelSearchRequests.forEach((issue: Issue) => {
|
||||
const streamId = issue.data.getString('stream_id') || issue.data.getString('channel_id') || ''
|
||||
channelSearchRequests.forEach((discussion: Discussion) => {
|
||||
const streamId =
|
||||
discussion.data.getString('stream_id') || discussion.data.getString('channel_id') || ''
|
||||
const [channelId, feedId] = streamId.split('@')
|
||||
|
||||
const result = {
|
||||
issueNumber: issue.number,
|
||||
issueNumber: discussion.number,
|
||||
type: 'channel search',
|
||||
streamId: streamId || undefined,
|
||||
streamUrl: undefined,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Dictionary } from '@freearhey/core'
|
||||
|
||||
export class IssueData {
|
||||
export class DataSet {
|
||||
_data: Dictionary<string>
|
||||
constructor(data: Dictionary<string>) {
|
||||
this._data = data
|
||||
@@ -1,8 +1,6 @@
|
||||
export * from './cliTable'
|
||||
export * from './htmlTable'
|
||||
export * from './issueData'
|
||||
export * from './issueLoader'
|
||||
export * from './issueParser'
|
||||
export * from './dataSet'
|
||||
export * from './logParser'
|
||||
export * from './markdown'
|
||||
export * from './numberParser'
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'
|
||||
import { paginateRest } from '@octokit/plugin-paginate-rest'
|
||||
import { TESTING, OWNER, REPO } from '../constants'
|
||||
import { Collection } from '@freearhey/core'
|
||||
import { Octokit } from '@octokit/core'
|
||||
import { IssueParser } from './'
|
||||
|
||||
const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
|
||||
const octokit = new CustomOctokit()
|
||||
|
||||
export class IssueLoader {
|
||||
async load(props?: { labels: string | string[] }) {
|
||||
let labels = ''
|
||||
if (props && props.labels) {
|
||||
labels = Array.isArray(props.labels) ? props.labels.join(',') : props.labels
|
||||
}
|
||||
let issues: object[] = []
|
||||
if (TESTING) {
|
||||
issues = (await import('../../tests/__data__/input/issues.js')).default
|
||||
} else {
|
||||
issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
per_page: 100,
|
||||
labels,
|
||||
status: 'open',
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const parser = new IssueParser()
|
||||
|
||||
return new Collection(issues).map(parser.parse)
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import { Dictionary } from '@freearhey/core'
|
||||
import { IssueData } from './issueData'
|
||||
import { Issue } from '../models'
|
||||
|
||||
const FIELDS = new Dictionary({
|
||||
'Stream ID': 'stream_id',
|
||||
'Channel ID': 'channel_id',
|
||||
'Feed ID': 'feed_id',
|
||||
'Stream URL': 'stream_url',
|
||||
Label: 'label',
|
||||
Quality: 'quality',
|
||||
'HTTP User-Agent': 'http_user_agent',
|
||||
'HTTP User Agent': 'http_user_agent',
|
||||
'HTTP Referrer': 'http_referrer',
|
||||
'What happened to the stream?': 'reason',
|
||||
Reason: 'reason',
|
||||
Notes: 'notes'
|
||||
})
|
||||
|
||||
export class IssueParser {
|
||||
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
|
||||
const fields = typeof issue.body === 'string' ? issue.body.split('###') : []
|
||||
|
||||
const data = new Dictionary<string>()
|
||||
fields.forEach((field: string) => {
|
||||
const parsed = typeof field === 'string' ? field.split(/\r?\n/).filter(Boolean) : []
|
||||
let _label = parsed.shift()
|
||||
_label = _label ? _label.replace(/ \(optional\)| \(required\)/, '').trim() : ''
|
||||
let _value = parsed.join('\r\n')
|
||||
_value = _value ? _value.trim() : ''
|
||||
|
||||
if (!_label || !_value) return data
|
||||
|
||||
const id = FIELDS.get(_label)
|
||||
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
||||
|
||||
if (!id) return
|
||||
|
||||
data.set(id, value)
|
||||
})
|
||||
|
||||
const labels = issue.labels.map(label => label.name)
|
||||
|
||||
return new Issue({ number: issue.number, labels, data: new IssueData(data) })
|
||||
}
|
||||
}
|
||||
19
scripts/models/discussion.ts
Normal file
19
scripts/models/discussion.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { DataSet } from '../core'
|
||||
|
||||
type DiscussionProps = {
|
||||
number: number
|
||||
category: string
|
||||
data: DataSet
|
||||
}
|
||||
|
||||
export class Discussion {
|
||||
number: number
|
||||
category: string
|
||||
data: DataSet
|
||||
|
||||
constructor({ number, category, data }: DiscussionProps) {
|
||||
this.number = number
|
||||
this.category = category
|
||||
this.data = data
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './issue'
|
||||
export * from './playlist'
|
||||
export * from './stream'
|
||||
export * from './discussion'
|
||||
|
||||
165
scripts/utils.ts
165
scripts/utils.ts
@@ -1,10 +1,16 @@
|
||||
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'
|
||||
import axios, { AxiosProxyConfig, AxiosRequestConfig } from 'axios'
|
||||
import { paginateGraphQL } from '@octokit/plugin-paginate-graphql'
|
||||
import { parse as parsePlaylist, setOptions } from 'hls-parser'
|
||||
import { parse as parseManifest } from 'mpd-parser'
|
||||
import { paginateRest } from '@octokit/plugin-paginate-rest'
|
||||
import { Collection, Dictionary } from '@freearhey/core'
|
||||
import { SocksProxyAgent } from 'socks-proxy-agent'
|
||||
import { ProxyParser } from './core/proxyParser.js'
|
||||
import { TESTING } from './constants.js'
|
||||
import { parse as parseManifest } from 'mpd-parser'
|
||||
import { TESTING, OWNER, REPO } from './constants'
|
||||
import { ProxyParser, DataSet } from './core'
|
||||
import { Discussion, Issue } from './models'
|
||||
import normalizeUrl from 'normalize-url'
|
||||
import { Octokit } from '@octokit/core'
|
||||
import { orderBy } from 'es-toolkit'
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs'
|
||||
@@ -90,7 +96,9 @@ export async function getStreamInfo(
|
||||
const response = await axios(url, request)
|
||||
|
||||
data = response.data
|
||||
} catch {}
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) return undefined
|
||||
@@ -115,7 +123,9 @@ export async function getStreamInfo(
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
} else if (url.includes('.mpd')) {
|
||||
const manifest = parseManifest(data, {
|
||||
manifestUri: url,
|
||||
@@ -138,3 +148,148 @@ export async function getStreamInfo(
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
export async function loadIssues(props?: { labels: string | string[] }) {
|
||||
const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
|
||||
const octokit = new CustomOctokit()
|
||||
|
||||
let labels = ''
|
||||
if (props && props.labels) {
|
||||
labels = Array.isArray(props.labels) ? props.labels.join(',') : props.labels
|
||||
}
|
||||
let issues: object[] = []
|
||||
if (TESTING) {
|
||||
issues = (await import('../tests/__data__/input/issues.js')).default
|
||||
} else {
|
||||
issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
per_page: 100,
|
||||
labels,
|
||||
status: 'open',
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return new Collection(issues).map(parseIssue)
|
||||
}
|
||||
|
||||
function parseIssue(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
|
||||
const FIELDS = new Dictionary({
|
||||
'Stream ID': 'stream_id',
|
||||
'Channel ID': 'channel_id',
|
||||
'Feed ID': 'feed_id',
|
||||
'Stream URL': 'stream_url',
|
||||
Label: 'label',
|
||||
Quality: 'quality',
|
||||
'HTTP User-Agent': 'http_user_agent',
|
||||
'HTTP User Agent': 'http_user_agent',
|
||||
'HTTP Referrer': 'http_referrer',
|
||||
'What happened to the stream?': 'reason',
|
||||
Reason: 'reason',
|
||||
Notes: 'notes'
|
||||
})
|
||||
|
||||
const fields = typeof issue.body === 'string' ? issue.body.split('###') : []
|
||||
|
||||
const data = new Dictionary<string>()
|
||||
fields.forEach((field: string) => {
|
||||
const parsed = typeof field === 'string' ? field.split(/\r?\n/).filter(Boolean) : []
|
||||
let _label = parsed.shift()
|
||||
_label = _label ? _label.replace(/ \(optional\)| \(required\)/, '').trim() : ''
|
||||
let _value = parsed.join('\r\n')
|
||||
_value = _value ? _value.trim() : ''
|
||||
|
||||
if (!_label || !_value) return data
|
||||
|
||||
const id = FIELDS.get(_label)
|
||||
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
||||
|
||||
if (!id) return
|
||||
|
||||
data.set(id, value)
|
||||
})
|
||||
|
||||
const labels = issue.labels.map(label => label.name)
|
||||
|
||||
return new Issue({ number: issue.number, labels, data: new DataSet(data) })
|
||||
}
|
||||
|
||||
export async function loadDiscussions() {
|
||||
let discussions: object[] = []
|
||||
if (TESTING) {
|
||||
discussions = (await import('../tests/__data__/input/discussions.js')).default
|
||||
} else {
|
||||
const CustomOctokit = Octokit.plugin(paginateGraphQL)
|
||||
const octokit = new CustomOctokit({
|
||||
auth: process.env.GITHUB_TOKEN
|
||||
})
|
||||
|
||||
const query = `
|
||||
query ($owner: String!, $repo: String!, $cursor: String) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
discussions(first: 100, after: $cursor, states: OPEN) {
|
||||
nodes {
|
||||
number
|
||||
body
|
||||
category {
|
||||
name
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
endCursor
|
||||
hasNextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const result = await octokit.graphql.paginate(query, {
|
||||
owner: 'iptv-org',
|
||||
repo: 'iptv'
|
||||
})
|
||||
|
||||
discussions = result.repository.discussions.nodes
|
||||
}
|
||||
|
||||
return new Collection(discussions).map(parseDiscussion)
|
||||
}
|
||||
|
||||
function parseDiscussion(discussion: {
|
||||
number: number
|
||||
category: { name: string }
|
||||
body: string
|
||||
}): Discussion {
|
||||
const FIELDS = new Dictionary({
|
||||
'Stream ID': 'stream_id'
|
||||
})
|
||||
|
||||
const fields = typeof discussion.body === 'string' ? discussion.body.split('###') : []
|
||||
|
||||
const data = new Dictionary<string>()
|
||||
fields.forEach((field: string) => {
|
||||
const parsed = typeof field === 'string' ? field.split(/\r?\n/).filter(Boolean) : []
|
||||
let _label = parsed.shift()
|
||||
_label = _label ? _label.replace(/ \(optional\)| \(required\)/, '').trim() : ''
|
||||
let _value = parsed.join('\r\n')
|
||||
_value = _value ? _value.trim() : ''
|
||||
|
||||
if (!_label || !_value) return data
|
||||
|
||||
const id = FIELDS.get(_label)
|
||||
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
||||
|
||||
if (!id) return
|
||||
|
||||
data.set(id, value)
|
||||
})
|
||||
|
||||
return new Discussion({
|
||||
number: discussion.number,
|
||||
category: discussion.category.name,
|
||||
data: new DataSet(data)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user