mirror of
https://github.com/iptv-org/iptv
synced 2026-09-09 13:27:58 -04:00
Merge pull request #48553 from iptv-org/patch-2026.08.3
Patch 2026.08.3
This commit is contained in:
@@ -60,8 +60,6 @@ async function main() {
|
|||||||
.split(/\r?\n/)
|
.split(/\r?\n/)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.forEach((link: string) => {
|
.forEach((link: string) => {
|
||||||
errors = errors.concat(validateStreamUrl(link))
|
|
||||||
|
|
||||||
const found: Stream = streams.first((_stream: Stream) => _stream.url === link.trim())
|
const found: Stream = streams.first((_stream: Stream) => _stream.url === link.trim())
|
||||||
if (!found) {
|
if (!found) {
|
||||||
errors.push(`The stream with the URL "${link}" is missing from the playlists`)
|
errors.push(`The stream with the URL "${link}" is missing from the playlists`)
|
||||||
@@ -80,11 +78,13 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data.isDeleted('stream_id')) {
|
||||||
const streamId = data.getString('stream_id')
|
const streamId = data.getString('stream_id')
|
||||||
if (streamId) {
|
if (streamId) {
|
||||||
errors = errors.concat(validateStreamId(streamId))
|
errors = errors.concat(validateStreamId(streamId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { Logger, Collection } from '@freearhey/core'
|
|||||||
import { program, OptionValues } from 'commander'
|
import { program, OptionValues } from 'commander'
|
||||||
import { Storage } from '@freearhey/storage-js'
|
import { Storage } from '@freearhey/storage-js'
|
||||||
import { Playlist, Stream } from '../../models'
|
import { Playlist, Stream } from '../../models'
|
||||||
|
import { loadData, data } from '../../api'
|
||||||
import { truncate } from '../../utils'
|
import { truncate } from '../../utils'
|
||||||
import { loadData } from '../../api'
|
|
||||||
import { eachLimit } from 'async'
|
import { eachLimit } from 'async'
|
||||||
import dns from 'node:dns'
|
import dns from 'node:dns'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
@@ -70,6 +70,10 @@ async function main() {
|
|||||||
})
|
})
|
||||||
const files = program.args.length ? program.args : await rootStorage.list(`${STREAMS_DIR}/*.m3u`)
|
const files = program.args.length ? program.args : await rootStorage.list(`${STREAMS_DIR}/*.m3u`)
|
||||||
streams = await parser.parse(files)
|
streams = await parser.parse(files)
|
||||||
|
streams = streams.map((stream: Stream) => {
|
||||||
|
stream.setGuides(data.guidesGroupedByStreamId.get(stream.getId()))
|
||||||
|
return stream
|
||||||
|
})
|
||||||
|
|
||||||
logger.info(`found ${streams.count()} streams`)
|
logger.info(`found ${streams.count()} streams`)
|
||||||
if (streams.count() > LIVE_UPDATE_MAX_STREAMS) isLiveUpdateEnabled = false
|
if (streams.count() > LIVE_UPDATE_MAX_STREAMS) isLiveUpdateEnabled = false
|
||||||
|
|||||||
@@ -53,7 +53,13 @@ async function main() {
|
|||||||
|
|
||||||
const log = new Collection<LogItem>()
|
const log = new Collection<LogItem>()
|
||||||
streams.forEach((stream: Stream) => {
|
streams.forEach((stream: Stream) => {
|
||||||
if (stream.channel) {
|
if (!stream.channel) {
|
||||||
|
log.add({
|
||||||
|
type: 'warning',
|
||||||
|
line: stream.getLine(),
|
||||||
|
message: `"${stream.url}" is missing a channel ID`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
const channel = data.channelsKeyById.get(stream.channel)
|
const channel = data.channelsKeyById.get(stream.channel)
|
||||||
if (!channel) {
|
if (!channel) {
|
||||||
log.add({
|
log.add({
|
||||||
@@ -62,6 +68,23 @@ async function main() {
|
|||||||
message: `"${stream.tvgId}" is not in the database`
|
message: `"${stream.tvgId}" is not in the database`
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!stream.feed) {
|
||||||
|
log.add({
|
||||||
|
type: 'warning',
|
||||||
|
line: stream.getLine(),
|
||||||
|
message: `"${stream.url}" is missing a feed ID`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const feed = data.feedsKeyByStreamId.get(stream.getId())
|
||||||
|
if (!feed) {
|
||||||
|
log.add({
|
||||||
|
type: 'warning',
|
||||||
|
line: stream.getLine(),
|
||||||
|
message: `There is no feed with the ID "${stream.feed}" in the database for the "${stream.channel}" channel`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDuplicate = stream.url && buffer.has(stream.url)
|
const isDuplicate = stream.url && buffer.has(stream.url)
|
||||||
|
|||||||
@@ -140,35 +140,57 @@ async function main() {
|
|||||||
const channelSearchRequests = discussions.filter(
|
const channelSearchRequests = discussions.filter(
|
||||||
(discussion: Discussion) => discussion.category === 'Channel Search'
|
(discussion: Discussion) => discussion.category === 'Channel Search'
|
||||||
)
|
)
|
||||||
const channelSearchRequestsBuffer = new Dictionary()
|
|
||||||
|
const requestsWithFeed = new Set<string>()
|
||||||
|
|
||||||
channelSearchRequests.forEach((discussion: Discussion) => {
|
channelSearchRequests.forEach((discussion: Discussion) => {
|
||||||
const streamId =
|
const streamId =
|
||||||
discussion.data.getString('stream_id') || discussion.data.getString('channel_id') || ''
|
discussion.data.getString('stream_id') || discussion.data.getString('channel_id') || ''
|
||||||
const [channelId, feedId] = streamId.split('@')
|
const [channelId, feedId] = streamId.split('@')
|
||||||
|
|
||||||
const result = {
|
if (channelId && feedId) {
|
||||||
|
requestsWithFeed.add(channelId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const seenStreamIds = new Set<string>()
|
||||||
|
|
||||||
|
channelSearchRequests.forEach((discussion: Discussion) => {
|
||||||
|
const streamId =
|
||||||
|
discussion.data.getString('stream_id') || discussion.data.getString('channel_id') || ''
|
||||||
|
const [channelId, feedId] = streamId.split('@')
|
||||||
|
const channelData = channelId ? data.channelsKeyById.get(channelId) : null
|
||||||
|
|
||||||
|
const isExactDuplicate = streamId && seenStreamIds.has(streamId)
|
||||||
|
const overlappedByFeeds = !feedId && channelId && requestsWithFeed.has(channelId)
|
||||||
|
|
||||||
|
const rules = [
|
||||||
|
{ status: status.MISSING_CHANNEL_ID, when: !channelId },
|
||||||
|
{ status: status.INVALID_CHANNEL_ID, when: data.channelsKeyById.missing(channelId) },
|
||||||
|
{ status: status.DUPLICATE_REQUEST, when: isExactDuplicate || overlappedByFeeds },
|
||||||
|
{
|
||||||
|
status: status.CHANNEL_BLOCKED,
|
||||||
|
when: data.blocklistRecordsGroupedByChannel.has(channelId)
|
||||||
|
},
|
||||||
|
{ status: status.FULFILLED, when: streamsGroupedById.has(streamId) },
|
||||||
|
{ status: status.FULFILLED, when: !feedId && streamsGroupedByChannel.has(channelId) },
|
||||||
|
{ status: status.CHANNEL_CLOSED, when: channelData && channelData.isClosed() }
|
||||||
|
]
|
||||||
|
|
||||||
|
const matchedRule = rules.find(rule => rule.when)
|
||||||
|
const finalStatus = matchedRule ? matchedRule.status : status.PENDING
|
||||||
|
|
||||||
|
if (streamId) {
|
||||||
|
seenStreamIds.add(streamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
report.add({
|
||||||
issueNumber: discussion.number,
|
issueNumber: discussion.number,
|
||||||
type: 'channel search',
|
type: 'channel search',
|
||||||
streamId: streamId || undefined,
|
streamId: streamId || undefined,
|
||||||
streamUrl: undefined,
|
streamUrl: undefined,
|
||||||
status: status.PENDING
|
status: finalStatus
|
||||||
}
|
})
|
||||||
|
|
||||||
if (!channelId) result.status = status.MISSING_CHANNEL_ID
|
|
||||||
else if (data.channelsKeyById.missing(channelId)) result.status = status.INVALID_CHANNEL_ID
|
|
||||||
else if (channelSearchRequestsBuffer.has(streamId)) result.status = status.DUPLICATE_REQUEST
|
|
||||||
else if (data.blocklistRecordsGroupedByChannel.has(channelId))
|
|
||||||
result.status = status.CHANNEL_BLOCKED
|
|
||||||
else if (streamsGroupedById.has(streamId)) result.status = status.FULFILLED
|
|
||||||
else if (!feedId && streamsGroupedByChannel.has(channelId)) result.status = status.FULFILLED
|
|
||||||
else {
|
|
||||||
const channelData = data.channelsKeyById.get(channelId)
|
|
||||||
if (channelData && channelData.isClosed()) result.status = status.CHANNEL_CLOSED
|
|
||||||
}
|
|
||||||
|
|
||||||
channelSearchRequestsBuffer.set(streamId, true)
|
|
||||||
|
|
||||||
report.add(result)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
report = report.sortBy(item => item.issueNumber).filter(item => item.status !== status.PENDING)
|
report = report.sortBy(item => item.issueNumber).filter(item => item.status !== status.PENDING)
|
||||||
|
|||||||
@@ -14,23 +14,25 @@ export class DataSet {
|
|||||||
return this._data.missing(key) || this._data.get(key) === undefined
|
return this._data.missing(key) || this._data.get(key) === undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDeleted(key: string): boolean {
|
||||||
|
const deleteSymbol = '~'
|
||||||
|
|
||||||
|
return this._data.get(key) === deleteSymbol
|
||||||
|
}
|
||||||
|
|
||||||
getBoolean(key: string): boolean {
|
getBoolean(key: string): boolean {
|
||||||
return Boolean(this._data.get(key))
|
return Boolean(this._data.get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
getString(key: string): string | undefined {
|
getString(key: string): string | undefined {
|
||||||
const deleteSymbol = '~'
|
return this._data.get(key)
|
||||||
|
|
||||||
return this._data.get(key) === deleteSymbol ? '' : this._data.get(key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getArray(key: string): string[] | undefined {
|
getArray(key: string): string[] | undefined {
|
||||||
const deleteSymbol = '~'
|
|
||||||
|
|
||||||
if (this._data.missing(key)) return undefined
|
if (this._data.missing(key)) return undefined
|
||||||
|
|
||||||
const value = this._data.get(key)
|
const value = this._data.get(key)
|
||||||
|
|
||||||
return !value || value === deleteSymbol ? [] : value.split('\r\n')
|
return !value || this.isDeleted(key) ? [] : value.split('\r\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ export class Stream extends sdk.Models.Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateWithIssue(dataSet: DataSet): this {
|
updateWithIssue(dataSet: DataSet): this {
|
||||||
|
if (dataSet.isDeleted('stream_id')) {
|
||||||
|
this.channel = ''
|
||||||
|
this.feed = ''
|
||||||
|
this.updateTvgId().updateTitle().updateFilepath()
|
||||||
|
} else if (dataSet.has('stream_id')) {
|
||||||
const streamId = dataSet.getString('stream_id') || ''
|
const streamId = dataSet.getString('stream_id') || ''
|
||||||
const [channelId, feedId] = streamId.split('@')
|
const [channelId, feedId] = streamId.split('@')
|
||||||
|
|
||||||
@@ -41,12 +46,15 @@ export class Stream extends sdk.Models.Stream {
|
|||||||
this.feed = feedId
|
this.feed = feedId
|
||||||
this.updateTvgId().updateTitle().updateFilepath()
|
this.updateTvgId().updateTitle().updateFilepath()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
label: dataSet.getString('label'),
|
label: dataSet.isDeleted('label') ? '' : dataSet.getString('label'),
|
||||||
quality: dataSet.getString('quality'),
|
quality: dataSet.isDeleted('quality') ? '' : dataSet.getString('quality'),
|
||||||
httpUserAgent: dataSet.getString('http_user_agent'),
|
httpUserAgent: dataSet.isDeleted('http_user_agent')
|
||||||
httpReferrer: dataSet.getString('http_referrer')
|
? ''
|
||||||
|
: dataSet.getString('http_user_agent'),
|
||||||
|
httpReferrer: dataSet.isDeleted('http_referrer') ? '' : dataSet.getString('http_referrer')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.label !== undefined) this.label = data.label
|
if (data.label !== undefined) this.label = data.label
|
||||||
@@ -318,8 +326,9 @@ export class Stream extends sdk.Models.Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTvgId(): this {
|
updateTvgId(): this {
|
||||||
if (!this.channel) return this
|
if (!this.channel) {
|
||||||
if (this.feed) {
|
this.tvgId = ''
|
||||||
|
} else if (this.feed) {
|
||||||
this.tvgId = `${this.channel}@${this.feed}`
|
this.tvgId = `${this.channel}@${this.feed}`
|
||||||
} else {
|
} else {
|
||||||
this.tvgId = this.channel
|
this.tvgId = this.channel
|
||||||
|
|||||||
+7
-3
@@ -244,9 +244,9 @@ export async function loadDiscussions() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const query = `
|
const query = `
|
||||||
query ($owner: String!, $repo: String!, $cursor: String) {
|
query ($owner: String!, $repo: String!, $cursor: String, $orderBy: DiscussionOrder) {
|
||||||
repository(owner: $owner, name: $repo) {
|
repository(owner: $owner, name: $repo) {
|
||||||
discussions(first: 100, after: $cursor, states: OPEN) {
|
discussions(first: 100, after: $cursor, states: OPEN, orderBy: $orderBy) {
|
||||||
nodes {
|
nodes {
|
||||||
number
|
number
|
||||||
body
|
body
|
||||||
@@ -265,7 +265,11 @@ export async function loadDiscussions() {
|
|||||||
|
|
||||||
const result = await octokit.graphql.paginate(query, {
|
const result = await octokit.graphql.paginate(query, {
|
||||||
owner: 'iptv-org',
|
owner: 'iptv-org',
|
||||||
repo: 'iptv'
|
repo: 'iptv',
|
||||||
|
orderBy: {
|
||||||
|
field: 'CREATED_AT',
|
||||||
|
direction: 'ASC'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
discussions = result.repository.discussions.nodes
|
discussions = result.repository.discussions.nodes
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
The request contains error(s):
|
The request contains error(s):
|
||||||
- The stream with the URL "https://livestream.telvue.com/templeuni1/f7b44cfafd5c52223d5498196c8a2e7b.sdp/playlist.m3u8" is missing from the playlists
|
- The stream with the URL "https://jmp2.uk/plu-5a74b8e1e22a61737979c6bf.m3u8" is missing from the playlists
|
||||||
- There is no channel with the ID "boo.us" in the database
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
#EXTM3U
|
#EXTM3U x-tvg-url="https://example.com/guide.xml.gz"
|
||||||
#EXTINF:-1 tvg-id="ABSTV.ag",ABS TV
|
#EXTINF:-1 tvg-id="ABSTV.ag@SD",ABS TV
|
||||||
https://tego-cdn2a.sibercdn.com/Live_TV-ABSTV-10/tracks-v3a1/rewind-7200.m3u8?token=e5f61e7be8363eb781b4bdfe591bf917dd529c1a-SjY3NzRTbDZQNnFQVkZaNkZja2RxV3JKc1VBa05zQkdMNStJakRGV0VTTzNrOEVGVUlIQmxta1NLV0o3bzdVdQ-1736094545-1736008145
|
https://tego-cdn2a.sibercdn.com/Live_TV-ABSTV-10/tracks-v3a1/rewind-7200.m3u8?token=e5f61e7be8363eb781b4bdfe591bf917dd529c1a-SjY3NzRTbDZQNnFQVkZaNkZja2RxV3JKc1VBa05zQkdMNStJakRGV0VTTzNrOEVGVUlIQmxta1NLV0o3bzdVdQ-1736094545-1736008145
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
closes #14175, closes #14105, closes #14104, closes #14057, closes #14034, closes #13964, closes #13893, closes #13881, closes #13793, closes #13751, closes #13715, closes #14110, closes #14120, closes #14151, closes #14150
|
closes #14175, closes #14105, closes #14104, closes #14057, closes #14034, closes #13964, closes #13893, closes #13881, closes #13793, closes #13751, closes #13715, closes #14110, closes #14121, closes #14151, closes #14150, closes #39097
|
||||||
@@ -3,5 +3,7 @@
|
|||||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||||
#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (480p) [Geo-blocked]
|
#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (480p) [Geo-blocked]
|
||||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8
|
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8
|
||||||
|
#EXTINF:-1 tvg-id="",Fox Sports (720p)
|
||||||
|
https://jmp2.uk/plu-5a74b8e1e22a61737979c6bf.m3u8
|
||||||
#EXTINF:-1 tvg-id="BeanoTV.uk@SD",Beano TV (1080p)
|
#EXTINF:-1 tvg-id="BeanoTV.uk@SD",Beano TV (1080p)
|
||||||
https://a5b4bacecd47433dad06d3189fc7422e.mediatailor.us-east-1.amazonaws.com/v1/manifest/04fd913bb278d8775298c26fdca9d9841f37601f/RakutenTV-eu_BeanoTV/b1f233d5-847c-437d-aa4f-f73e67a85323/2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz"
|
https://a5b4bacecd47433dad06d3189fc7422e.mediatailor.us-east-1.amazonaws.com/v1/manifest/04fd913bb278d8775298c26fdca9d9841f37601f/RakutenTV-eu_BeanoTV/b1f233d5-847c-437d-aa4f-f73e67a85323/2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz"
|
||||||
|
|||||||
@@ -873,5 +873,21 @@
|
|||||||
"America/Port_of_Spain"
|
"America/Port_of_Spain"
|
||||||
],
|
],
|
||||||
"video_format": "576i"
|
"video_format": "576i"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"channel": "Channel7.bz",
|
||||||
|
"id": "SD",
|
||||||
|
"name": "SD",
|
||||||
|
"is_main": true,
|
||||||
|
"broadcast_area": [
|
||||||
|
"c/BZ"
|
||||||
|
],
|
||||||
|
"languages": [
|
||||||
|
"eng"
|
||||||
|
],
|
||||||
|
"timezones": [
|
||||||
|
"America/New_York"
|
||||||
|
],
|
||||||
|
"video_format": "576i"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -113,5 +113,25 @@
|
|||||||
"format": "XML"
|
"format": "XML"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"channel": "ABSTV.ag",
|
||||||
|
"feed": "SD",
|
||||||
|
"site": "example.com",
|
||||||
|
"site_id": "#",
|
||||||
|
"site_name": "ABS TV",
|
||||||
|
"lang": "es",
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"host": "example2.com",
|
||||||
|
"url": "https://example2.com/guide.xml",
|
||||||
|
"format": "XML"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host": "example.com",
|
||||||
|
"url": "https://example.com/guide.xml.gz",
|
||||||
|
"format": "GZIP"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1779,15 +1779,15 @@ module.exports = [
|
|||||||
state_reason: null
|
state_reason: null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14120',
|
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14121',
|
||||||
repository_url: 'https://api.github.com/repos/iptv-org/iptv',
|
repository_url: 'https://api.github.com/repos/iptv-org/iptv',
|
||||||
labels_url: 'https://api.github.com/repos/iptv-org/iptv/issues/14120/labels{/name}',
|
labels_url: 'https://api.github.com/repos/iptv-org/iptv/issues/14121/labels{/name}',
|
||||||
comments_url: 'https://api.github.com/repos/iptv-org/iptv/issues/14120/comments',
|
comments_url: 'https://api.github.com/repos/iptv-org/iptv/issues/14121/comments',
|
||||||
events_url: 'https://api.github.com/repos/iptv-org/iptv/issues/14120/events',
|
events_url: 'https://api.github.com/repos/iptv-org/iptv/issues/14121/events',
|
||||||
html_url: 'https://github.com/iptv-org/iptv/issues/14120',
|
html_url: 'https://github.com/iptv-org/iptv/issues/14121',
|
||||||
id: 1884922249,
|
id: 1884922249,
|
||||||
node_id: 'I_kwDOCWUK8M5wWaGJ',
|
node_id: 'I_kwDOCWUK8M5wWaGJ',
|
||||||
number: 14120,
|
number: 14121,
|
||||||
title: 'Edit: Tele2000',
|
title: 'Edit: Tele2000',
|
||||||
user: {
|
user: {
|
||||||
login: 'freearhey',
|
login: 'freearhey',
|
||||||
@@ -1842,7 +1842,7 @@ module.exports = [
|
|||||||
active_lock_reason: null,
|
active_lock_reason: null,
|
||||||
body: '### Stream URL\n\nhttps://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8\n\n### Stream ID\n\n_No response_\n\n### Quality\n\nNone\n\n### Label\n\nNone\n\n### HTTP User-Agent\n\n_No response_\n\n### HTTP Referrer\n\n_No response_\n\n### Notes\n\n_No response_\n\n### Contributing Guide\n\n- [X] I have read [Contributing Guide](https://github.com/iptv-org/iptv/blob/master/CONTRIBUTING.md)',
|
body: '### Stream URL\n\nhttps://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8\n\n### Stream ID\n\n_No response_\n\n### Quality\n\nNone\n\n### Label\n\nNone\n\n### HTTP User-Agent\n\n_No response_\n\n### HTTP Referrer\n\n_No response_\n\n### Notes\n\n_No response_\n\n### Contributing Guide\n\n- [X] I have read [Contributing Guide](https://github.com/iptv-org/iptv/blob/master/CONTRIBUTING.md)',
|
||||||
reactions: {
|
reactions: {
|
||||||
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14120/reactions',
|
url: 'https://api.github.com/repos/iptv-org/iptv/issues/14121/reactions',
|
||||||
total_count: 0,
|
total_count: 0,
|
||||||
'+1': 0,
|
'+1': 0,
|
||||||
'-1': 0,
|
'-1': 0,
|
||||||
@@ -1853,7 +1853,7 @@ module.exports = [
|
|||||||
rocket: 0,
|
rocket: 0,
|
||||||
eyes: 0
|
eyes: 0
|
||||||
},
|
},
|
||||||
timeline_url: 'https://api.github.com/repos/iptv-org/iptv/issues/14120/timeline',
|
timeline_url: 'https://api.github.com/repos/iptv-org/iptv/issues/14121/timeline',
|
||||||
performed_via_github_app: null,
|
performed_via_github_app: null,
|
||||||
state_reason: null
|
state_reason: null
|
||||||
},
|
},
|
||||||
@@ -2472,5 +2472,84 @@ module.exports = [
|
|||||||
timeline_url: 'https://api.github.com/repos/iptv-org/iptv/issues/15175/timeline',
|
timeline_url: 'https://api.github.com/repos/iptv-org/iptv/issues/15175/timeline',
|
||||||
performed_via_github_app: null,
|
performed_via_github_app: null,
|
||||||
state_reason: null
|
state_reason: null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: 'https://api.github.com/repos/iptv-org/iptv/issues/39097',
|
||||||
|
repository_url: 'https://api.github.com/repos/iptv-org/iptv',
|
||||||
|
labels_url: 'https://api.github.com/repos/iptv-org/iptv/issues/39097/labels{/name}',
|
||||||
|
comments_url: 'https://api.github.com/repos/iptv-org/iptv/issues/39097/comments',
|
||||||
|
events_url: 'https://api.github.com/repos/iptv-org/iptv/issues/39097/events',
|
||||||
|
html_url: 'https://github.com/iptv-org/iptv/issues/39097',
|
||||||
|
id: 1884922249,
|
||||||
|
node_id: 'I_kwDOCWUK8M5wWaGJ',
|
||||||
|
number: 39097,
|
||||||
|
title: 'Edit: Fox Sports',
|
||||||
|
user: {
|
||||||
|
login: 'freearhey',
|
||||||
|
id: 7253922,
|
||||||
|
node_id: 'MDQ6VXNlcjcyNTM5MjI=',
|
||||||
|
avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4',
|
||||||
|
gravatar_id: '',
|
||||||
|
url: 'https://api.github.com/users/freearhey',
|
||||||
|
html_url: 'https://github.com/freearhey',
|
||||||
|
followers_url: 'https://api.github.com/users/freearhey/followers',
|
||||||
|
following_url: 'https://api.github.com/users/freearhey/following{/other_user}',
|
||||||
|
gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}',
|
||||||
|
starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}',
|
||||||
|
subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions',
|
||||||
|
organizations_url: 'https://api.github.com/users/freearhey/orgs',
|
||||||
|
repos_url: 'https://api.github.com/users/freearhey/repos',
|
||||||
|
events_url: 'https://api.github.com/users/freearhey/events{/privacy}',
|
||||||
|
received_events_url: 'https://api.github.com/users/freearhey/received_events',
|
||||||
|
type: 'User',
|
||||||
|
site_admin: false
|
||||||
|
},
|
||||||
|
labels: [
|
||||||
|
{
|
||||||
|
id: 5923498886,
|
||||||
|
node_id: 'LA_kwDOCWUK8M8AAAABYRFrhg',
|
||||||
|
url: 'https://api.github.com/repos/iptv-org/iptv/labels/approved',
|
||||||
|
name: 'approved',
|
||||||
|
color: '85ddde',
|
||||||
|
default: false,
|
||||||
|
description: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5923508587,
|
||||||
|
node_id: 'LA_kwDOCWUK8M8AAAABYRGRaw',
|
||||||
|
url: 'https://api.github.com/repos/iptv-org/iptv/labels/streams:edit',
|
||||||
|
name: 'streams:edit',
|
||||||
|
color: '017ff9',
|
||||||
|
default: false,
|
||||||
|
description: 'Request to add a new link to a playlist'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
state: 'open',
|
||||||
|
locked: false,
|
||||||
|
assignee: null,
|
||||||
|
assignees: [],
|
||||||
|
milestone: null,
|
||||||
|
comments: 1,
|
||||||
|
created_at: '2023-09-07T00:30:51Z',
|
||||||
|
updated_at: '2023-09-07T00:48:23Z',
|
||||||
|
closed_at: null,
|
||||||
|
author_association: 'COLLABORATOR',
|
||||||
|
active_lock_reason: null,
|
||||||
|
body: '### Stream URL\n\nhttps://jmp2.uk/plu-5a74b8e1e22a61737979c6bf.m3u8\n\n### Stream ID\n\n~\n\n### Quality\n\n720p\n\n### Label\n\nNone\n\n### HTTP User-Agent\n\nNone\n\n### HTTP Referrer\n\n_No response_\n\n### Notes\n\n_No response_\n\n### Contributing Guide\n\n- [X] I have read [Contributing Guide](https://github.com/iptv-org/iptv/blob/master/CONTRIBUTING.md)',
|
||||||
|
reactions: {
|
||||||
|
url: 'https://api.github.com/repos/iptv-org/iptv/issues/39097/reactions',
|
||||||
|
total_count: 0,
|
||||||
|
'+1': 0,
|
||||||
|
'-1': 0,
|
||||||
|
laugh: 0,
|
||||||
|
hooray: 0,
|
||||||
|
confused: 0,
|
||||||
|
heart: 0,
|
||||||
|
rocket: 0,
|
||||||
|
eyes: 0
|
||||||
|
},
|
||||||
|
timeline_url: 'https://api.github.com/repos/iptv-org/iptv/issues/39097/timeline',
|
||||||
|
performed_via_github_app: null,
|
||||||
|
state_reason: null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#EXTM3U
|
#EXTM3U x-tvg-url="https://example.com/guide.xml.gz"
|
||||||
#EXTINF:-1 tvg-id="ABSTV.ag",ABS TV
|
#EXTINF:-1 tvg-id="ABSTV.ag@SD",ABS TV
|
||||||
https://tego-cdn2a.sibercdn.com/Live_TV-ABSTV-10/tracks-v3a1/rewind-7200.m3u8?token=e5f61e7be8363eb781b4bdfe591bf917dd529c1a-SjY3NzRTbDZQNnFQVkZaNkZja2RxV3JKc1VBa05zQkdMNStJakRGV0VTTzNrOEVGVUlIQmxta1NLV0o3bzdVdQ-1736094545-1736008145
|
https://tego-cdn2a.sibercdn.com/Live_TV-ABSTV-10/tracks-v3a1/rewind-7200.m3u8?token=e5f61e7be8363eb781b4bdfe591bf917dd529c1a-SjY3NzRTbDZQNnFQVkZaNkZja2RxV3JKc1VBa05zQkdMNStJakRGV0VTTzNrOEVGVUlIQmxta1NLV0o3bzdVdQ-1736094545-1736008145
|
||||||
#EXTINF:-1 tvg-id="ABSTV.ag@HD",ABS TV (1080p)
|
#EXTINF:-1 tvg-id="ABSTV.ag@HD",ABS TV (1080p)
|
||||||
https://query-streamlink.herokuapp.com/iptv-query?streaming-ip=https://www.twitch.tv/absliveantigua3
|
https://query-streamlink.herokuapp.com/iptv-query?streaming-ip=https://www.twitch.tv/absliveantigua3
|
||||||
|
|||||||
@@ -3,3 +3,5 @@
|
|||||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||||
#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (480p) [Geo-blocked]
|
#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (480p) [Geo-blocked]
|
||||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8
|
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8
|
||||||
|
#EXTINF:-1 tvg-id="FoxSports.uk",Fox Sports
|
||||||
|
https://jmp2.uk/plu-5a74b8e1e22a61737979c6bf.m3u8
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXTINF:-1 tvg-id="",Channel 7
|
||||||
|
https://example.com/playlist2.m3u8
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXTINF:-1 tvg-id="Channel7.bz",Channel 7
|
||||||
|
https://example.com/playlist2.m3u8
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#EXTM3U
|
#EXTM3U
|
||||||
#EXTINF:-1 tvg-id="FoxSports2.us@Asia",Fox Sports 2 Asia (Thai) (720p)
|
#EXTINF:-1 tvg-id="FoxSports2.us@Asia",Fox Sports 2 Asia (Thai) (720p)
|
||||||
https://example.com/playlist.m3u8
|
https://example.com/playlist.m3u8
|
||||||
#EXTINF:-1 tvg-id="TVN.pl",TVN
|
#EXTINF:-1 tvg-id="TVN.pl@SD",TVN
|
||||||
https://example.com/playlist2.m3u8
|
https://example.com/playlist2.m3u8
|
||||||
#EXTINF:-1 tvg-id="EverydayHeroes.us",Everyday Heroes (720p)
|
#EXTINF:-1 tvg-id="EverydayHeroes.us@SD",Everyday Heroes (720p)
|
||||||
https://a.jsrdn.com/broadcast/7b1451fa52/+0000/c.m3u8
|
https://a.jsrdn.com/broadcast/7b1451fa52/+0000/c.m3u8
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
#EXTM3U
|
#EXTM3U
|
||||||
#EXTINF:-1 tvg-id="qib22lAq1L.us",ABC (720p)
|
#EXTINF:-1 tvg-id="qib22lAq1L.us@SD",ABC (720p)
|
||||||
#EXTVLCOPT:http-referrer=http://imn.iq
|
#EXTVLCOPT:http-referrer=http://imn.iq
|
||||||
#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
|
#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
|
||||||
#KODIPROP:inputstream=inputstream.adaptive
|
#KODIPROP:inputstream=inputstream.adaptive
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXTINF:-1 tvg-id="Channel7.bz@HD",Channel 7
|
||||||
|
https://example.com/playlist2.m3u8
|
||||||
@@ -43,7 +43,7 @@ describe('issue:validate', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('can handle streams:edit request', () => {
|
it('can handle streams:edit request', () => {
|
||||||
const body = issues.find(issue => issue.number === 14120)?.body
|
const body = issues.find(issue => issue.number === 39097)?.body
|
||||||
const cmd = `${ENV_VAR} npm run issue:validate --- --body="${body}" --labels="approved,streams:edit"`
|
const cmd = `${ENV_VAR} npm run issue:validate --- --body="${body}" --labels="approved,streams:edit"`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ describe('playlist:validate', () => {
|
|||||||
it('show an error if channel id in the blocklist', () => {
|
it('show an error if channel id in the blocklist', () => {
|
||||||
const cmd = `${ENV_VAR} npm run playlist:validate -- us_blocked.m3u`
|
const cmd = `${ENV_VAR} npm run playlist:validate -- us_blocked.m3u`
|
||||||
try {
|
try {
|
||||||
execSync(cmd, { encoding: 'utf8' })
|
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||||
|
process.exit(0)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
||||||
expect((error as ExecError).stdout).toContain('us_blocked.m3u')
|
expect((error as ExecError).stdout).toContain('us_blocked.m3u')
|
||||||
@@ -26,22 +28,64 @@ describe('playlist:validate', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('show a warning if channel has wrong id', () => {
|
it('show a warning if stream is missing a channel id', () => {
|
||||||
const cmd = `${ENV_VAR} npm run playlist:validate -- wrong_id.m3u`
|
const cmd = `${ENV_VAR} npm run playlist:validate -- missing_channel_id.m3u`
|
||||||
try {
|
try {
|
||||||
execSync(cmd, { encoding: 'utf8' })
|
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||||
|
expect(stdout).toContain(
|
||||||
|
'missing_channel_id.m3u\n 2 warning "https://example.com/playlist2.m3u8" is missing a channel ID\n\n1 problems (0 errors, 1 warnings)\n'
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
||||||
expect((error as ExecError).stdout).toContain(
|
}
|
||||||
'wrong_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n'
|
})
|
||||||
|
|
||||||
|
it('show a warning if stream has wrong channel id', () => {
|
||||||
|
const cmd = `${ENV_VAR} npm run playlist:validate -- wrong_channel_id.m3u`
|
||||||
|
try {
|
||||||
|
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||||
|
expect(stdout).toContain(
|
||||||
|
'wrong_channel_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n'
|
||||||
)
|
)
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('show a warning if stream is missing a feed id', () => {
|
||||||
|
const cmd = `${ENV_VAR} npm run playlist:validate -- missing_feed_id.m3u`
|
||||||
|
try {
|
||||||
|
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||||
|
expect(stdout).toContain(
|
||||||
|
'missing_feed_id.m3u\n 2 warning "https://example.com/playlist2.m3u8" is missing a feed ID\n\n1 problems (0 errors, 1 warnings)\n'
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('show a warning if stream has a wrong feed id', () => {
|
||||||
|
const cmd = `${ENV_VAR} npm run playlist:validate -- wrong_feed_id.m3u`
|
||||||
|
try {
|
||||||
|
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||||
|
expect(stdout).toContain(
|
||||||
|
'wrong_feed_id.m3u\n 2 warning There is no feed with the ID "HD" in the database for the "Channel7.bz" channel\n\n1 problems (0 errors, 1 warnings)\n'
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('show a error if stream has an invalid url', () => {
|
it('show a error if stream has an invalid url', () => {
|
||||||
const cmd = `${ENV_VAR} npm run playlist:validate -- invalid_url.m3u`
|
const cmd = `${ENV_VAR} npm run playlist:validate -- invalid_url.m3u`
|
||||||
try {
|
try {
|
||||||
execSync(cmd, { encoding: 'utf8' })
|
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||||
|
process.exit(0)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
||||||
expect((error as ExecError).stdout).toContain('invalid_url.m3u')
|
expect((error as ExecError).stdout).toContain('invalid_url.m3u')
|
||||||
@@ -54,6 +98,7 @@ describe('playlist:validate', () => {
|
|||||||
|
|
||||||
it('skip the file if it does not exist', () => {
|
it('skip the file if it does not exist', () => {
|
||||||
const cmd = `${ENV_VAR} npm run playlist:validate -- missing.m3u`
|
const cmd = `${ENV_VAR} npm run playlist:validate -- missing.m3u`
|
||||||
execSync(cmd, { encoding: 'utf8' })
|
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||||
|
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ describe('report:create', () => {
|
|||||||
│ 8 │ 19957 │ 'channel search' │ '13thStreet.au' │ undefined │ 'channel_closed' │
|
│ 8 │ 19957 │ 'channel search' │ '13thStreet.au' │ undefined │ 'channel_closed' │
|
||||||
│ 9 │ 20956 │ 'channel search' │ 'IONTV.us' │ undefined │ 'fulfilled' │
|
│ 9 │ 20956 │ 'channel search' │ 'IONTV.us' │ undefined │ 'fulfilled' │
|
||||||
│ 10 │ 25157 │ 'streams:add' │ 'OnTimeSports.eg@SD' │ 'OnTime Sports SD.mu38' │ 'invalid_stream_url' │
|
│ 10 │ 25157 │ 'streams:add' │ 'OnTimeSports.eg@SD' │ 'OnTime Sports SD.mu38' │ 'invalid_stream_url' │
|
||||||
|
│ 11 │ 39097 │ 'streams:edit' │ '~' │ 'https://jmp2.uk/plu-5a74b8e1e22a61737979c6bf.m3u8' │ 'nonexistent_link' │
|
||||||
└─────────┴─────────────┴──────────────────┴─────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────┘`)
|
└─────────┴─────────────┴──────────────────┴─────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────┘`)
|
||||||
).toBe(true)
|
).toBe(true)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user