mirror of
https://github.com/iptv-org/iptv-org.github.io.git
synced 2025-05-11 17:40:05 -04:00
274 lines
7.3 KiB
JavaScript
274 lines
7.3 KiB
JavaScript
import { writable, get } from 'svelte/store'
|
|
import { Playlist, Link } from 'iptv-playlist-generator'
|
|
import sj from '@freearhey/search-js'
|
|
import _ from 'lodash'
|
|
import { browser } from '$app/environment'
|
|
import { Channel } from './models'
|
|
import { pushState } from '$app/navigation'
|
|
|
|
export const query = writable('')
|
|
export const hasQuery = writable(false)
|
|
export const channels = writable([])
|
|
export const countries = writable({})
|
|
export const filteredChannels = writable([])
|
|
export const selected = writable([])
|
|
export const downloadMode = writable(false)
|
|
|
|
let searchIndex = {}
|
|
export function search(q) {
|
|
if (!q) {
|
|
filteredChannels.set(get(channels))
|
|
hasQuery.set(false)
|
|
return
|
|
}
|
|
|
|
if (searchIndex.search) {
|
|
let results = searchIndex.search(q)
|
|
filteredChannels.set(results)
|
|
hasQuery.set(true)
|
|
}
|
|
}
|
|
|
|
export async function fetchChannels() {
|
|
const api = await loadAPI()
|
|
|
|
countries.set(api.countries)
|
|
|
|
let _channels = api.channels.map(c => createChannel(c, api))
|
|
|
|
channels.set(_channels)
|
|
filteredChannels.set(_channels)
|
|
searchIndex = sj.createIndex(_channels, {
|
|
searchable: [
|
|
'id',
|
|
'name',
|
|
'alt_names',
|
|
'alt_name',
|
|
'network',
|
|
'owner',
|
|
'owners',
|
|
'country',
|
|
'subdivision',
|
|
'city',
|
|
'broadcast_area',
|
|
'language',
|
|
'languages',
|
|
'category',
|
|
'categories',
|
|
'launched',
|
|
'closed',
|
|
'replaced_by',
|
|
'website',
|
|
'streams',
|
|
'guides',
|
|
'is_nsfw',
|
|
'is_closed',
|
|
'is_blocked'
|
|
]
|
|
})
|
|
}
|
|
|
|
export function setSearchParam(key, value) {
|
|
let query = key && value ? `?${key}=${value}` : ''
|
|
query = query.replace(/\+/g, '%2B')
|
|
const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}${query}`
|
|
const state = {}
|
|
state[key] = value
|
|
pushState(url, state)
|
|
setPageTitle(value)
|
|
}
|
|
|
|
export function setPageTitle(value) {
|
|
if (browser) {
|
|
const title = value ? `${value} · iptv-org` : 'iptv-org'
|
|
window.document.title = title
|
|
}
|
|
}
|
|
|
|
export function createChannel(data, api) {
|
|
let broadcastArea = []
|
|
let regionCountries = []
|
|
|
|
data.broadcast_area.forEach(areaCode => {
|
|
const [type, code] = areaCode.split('/')
|
|
switch (type) {
|
|
case 'c':
|
|
const country = api.countries[code]
|
|
if (country) broadcastArea.push({ type, code: country.code, name: country.name })
|
|
break
|
|
case 'r':
|
|
const region = api.regions[code]
|
|
if (region) {
|
|
broadcastArea.push({ type, code: region.code, name: region.name })
|
|
regionCountries = [
|
|
...regionCountries,
|
|
...region.countries.map(code => api.countries[code]).filter(Boolean)
|
|
]
|
|
}
|
|
break
|
|
case 's':
|
|
const subdivision = api.subdivisions[code]
|
|
if (subdivision)
|
|
broadcastArea.push({ type, code: subdivision.code, name: subdivision.name })
|
|
break
|
|
}
|
|
})
|
|
|
|
return new Channel({
|
|
id: data.id,
|
|
name: data.name,
|
|
altNames: data.alt_names,
|
|
network: data.network,
|
|
owners: data.owners,
|
|
city: data.city,
|
|
country: api.countries[data.country],
|
|
subdivision: api.subdivisions[data.subdivision],
|
|
languages: data.languages.map(code => api.languages[code]).filter(Boolean),
|
|
categories: data.categories.map(id => api.categories[id]).filter(Boolean),
|
|
isNSFW: data.is_nsfw,
|
|
launched: data.launched,
|
|
closed: data.closed,
|
|
replacedBy: data.replaced_by,
|
|
website: data.website,
|
|
logo: data.logo,
|
|
streams: api.streams[data.id],
|
|
guides: api.guides[data.id],
|
|
blocklistRecords: api.blocklist[data.id],
|
|
hasUniqueName: api.nameIndex[data.name.toLowerCase()].length === 1,
|
|
broadcastArea,
|
|
regionCountries
|
|
})
|
|
}
|
|
|
|
async function loadAPI() {
|
|
const api = {}
|
|
|
|
const [
|
|
countries,
|
|
regions,
|
|
subdivisions,
|
|
languages,
|
|
categories,
|
|
streams,
|
|
blocklist,
|
|
channels,
|
|
guides
|
|
] = await Promise.all([
|
|
fetch('https://iptv-org.github.io/api/countries.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : []))
|
|
.then(data =>
|
|
data.map(i => {
|
|
i.expanded = false
|
|
return i
|
|
})
|
|
)
|
|
.then(data => _.keyBy(data, 'code')),
|
|
fetch('https://iptv-org.github.io/api/regions.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : []))
|
|
.then(data => _.keyBy(data, 'code')),
|
|
fetch('https://iptv-org.github.io/api/subdivisions.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : []))
|
|
.then(data => _.keyBy(data, 'code')),
|
|
fetch('https://iptv-org.github.io/api/languages.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : []))
|
|
.then(data => _.keyBy(data, 'code')),
|
|
fetch('https://iptv-org.github.io/api/categories.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : []))
|
|
.then(data => _.keyBy(data, 'id')),
|
|
fetch('https://iptv-org.github.io/api/streams.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : []))
|
|
.then(data => _.groupBy(data, 'channel')),
|
|
fetch('https://iptv-org.github.io/api/blocklist.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : []))
|
|
.then(data => _.groupBy(data, 'channel')),
|
|
fetch('https://iptv-org.github.io/api/channels.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : [])),
|
|
fetch('https://iptv-org.github.io/api/guides.json')
|
|
.then(r => r.json())
|
|
.then(data => (data.length ? data : []))
|
|
.then(data => data.filter(guide => guide.channel))
|
|
.then(data => _.sortBy(data, 'lang'))
|
|
.then(data => _.groupBy(data, 'channel'))
|
|
])
|
|
|
|
api.countries = countries
|
|
api.regions = regions
|
|
api.subdivisions = subdivisions
|
|
api.languages = languages
|
|
api.categories = categories
|
|
api.streams = streams
|
|
api.blocklist = blocklist
|
|
api.channels = channels
|
|
api.guides = guides
|
|
|
|
api.nameIndex = _.groupBy(api.channels, channel => channel.name.toLowerCase())
|
|
|
|
return api
|
|
}
|
|
|
|
function getStreams() {
|
|
let streams = []
|
|
get(selected).forEach(channel => {
|
|
channel._streams.forEach(stream => {
|
|
if (stream.status === 'error') return
|
|
|
|
stream.channel = channel
|
|
streams.push(stream)
|
|
})
|
|
})
|
|
|
|
const levels = { online: 1, blocked: 2, timeout: 3, error: 4, default: 5 }
|
|
streams = _.orderBy(
|
|
streams,
|
|
[
|
|
s => s.channel.id.toLowerCase(),
|
|
s => levels[s.status] || levels['default'],
|
|
'height',
|
|
'frame_rate',
|
|
'url'
|
|
],
|
|
['asc', 'asc', 'desc', 'desc', 'asc']
|
|
)
|
|
streams = _.uniqBy(streams, stream => stream.channel.id || _.uniqueId())
|
|
|
|
return streams
|
|
}
|
|
|
|
export function createPlaylist() {
|
|
const playlist = new Playlist()
|
|
|
|
let streams = getStreams()
|
|
streams.forEach(stream => {
|
|
const link = new Link(stream.url)
|
|
link.title = stream.channel.name
|
|
link.attrs = {
|
|
'tvg-id': stream.channel.id,
|
|
'tvg-logo': stream.channel.logo,
|
|
'group-title': stream.channel._categories
|
|
.map(channel => channel.name)
|
|
.sort()
|
|
.join(';')
|
|
}
|
|
|
|
if (stream.user_agent) {
|
|
link.attrs['user-agent'] = stream.user_agent
|
|
link.vlcOpts['http-user-agent'] = stream.user_agent
|
|
}
|
|
|
|
if (stream.http_referrer) {
|
|
link.vlcOpts['http-referrer'] = stream.http_referrer
|
|
}
|
|
|
|
playlist.links.push(link)
|
|
})
|
|
|
|
return playlist
|
|
}
|