mirror of
https://github.com/iptv-org/iptv-org.github.io.git
synced 2025-05-11 17:40:05 -04:00
Create Channel model
This commit is contained in:
parent
4bfb062731
commit
9aa1b9fe50
11 changed files with 123 additions and 56 deletions
|
@ -9,7 +9,7 @@
|
||||||
nsfw: 'The channel has been added to our blocklist due to NSFW content'
|
nsfw: 'The channel has been added to our blocklist due to NSFW content'
|
||||||
}
|
}
|
||||||
|
|
||||||
const blocklistRefs = channel.blocklist_records
|
const blocklistRefs = channel._blocklistRecords
|
||||||
.map(record => {
|
.map(record => {
|
||||||
let refName
|
let refName
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
const guides = channel._guides
|
const guides = channel._guides
|
||||||
const streams = channel._streams
|
const streams = channel._streams
|
||||||
|
const displayName = channel._displayName
|
||||||
|
|
||||||
const [name, country] = channel.id.split('.')
|
const [name, country] = channel.id.split('.')
|
||||||
|
|
||||||
|
@ -20,11 +21,7 @@
|
||||||
let prevUrl = '/'
|
let prevUrl = '/'
|
||||||
const onOpened = () => {
|
const onOpened = () => {
|
||||||
prevUrl = window.location.href
|
prevUrl = window.location.href
|
||||||
window.history.pushState(
|
window.history.pushState({}, `${displayName} • iptv-org`, `/channels/${country}/${name}`)
|
||||||
{},
|
|
||||||
`${channel.displayName} • iptv-org`,
|
|
||||||
`/channels/${country}/${name}`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
window.history.pushState({}, `iptv-org`, prevUrl)
|
window.history.pushState({}, `iptv-org`, prevUrl)
|
||||||
|
@ -32,13 +29,13 @@
|
||||||
const showGuides = () =>
|
const showGuides = () =>
|
||||||
open(
|
open(
|
||||||
GuidesPopup,
|
GuidesPopup,
|
||||||
{ guides, title: channel.displayName },
|
{ guides, title: displayName },
|
||||||
{ transitionBgProps: { duration: 0 }, transitionWindowProps: { duration: 0 } }
|
{ transitionBgProps: { duration: 0 }, transitionWindowProps: { duration: 0 } }
|
||||||
)
|
)
|
||||||
const showStreams = () =>
|
const showStreams = () =>
|
||||||
open(
|
open(
|
||||||
StreamsPopup,
|
StreamsPopup,
|
||||||
{ streams, title: channel.displayName },
|
{ streams, title: displayName },
|
||||||
{ transitionBgProps: { duration: 0 }, transitionWindowProps: { duration: 0 } }
|
{ transitionBgProps: { duration: 0 }, transitionWindowProps: { duration: 0 } }
|
||||||
)
|
)
|
||||||
const showChannelData = () => {
|
const showChannelData = () => {
|
||||||
|
@ -89,7 +86,7 @@
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
src={channel.logo}
|
src={channel.logo}
|
||||||
alt={channel.displayName}
|
alt={displayName}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -103,9 +100,9 @@
|
||||||
href="/channels/{country}/{name}"
|
href="/channels/{country}/{name}"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="font-normal text-gray-600 dark:text-white hover:underline hover:text-blue-500 truncate whitespace-nowrap"
|
class="font-normal text-gray-600 dark:text-white hover:underline hover:text-blue-500 truncate whitespace-nowrap"
|
||||||
title={channel.displayName}
|
title={displayName}
|
||||||
>
|
>
|
||||||
{channel.displayName}
|
{displayName}
|
||||||
</a>
|
</a>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
{#if channel.is_closed}
|
{#if channel.is_closed}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
>
|
>
|
||||||
<div class="w-2/3 overflow-hidden">
|
<div class="w-2/3 overflow-hidden">
|
||||||
<div class="flex items-center space-x-3">
|
<div class="flex items-center space-x-3">
|
||||||
<h3 class="text-l font-medium text-gray-900 dark:text-white">{channel.displayName}</h3>
|
<h3 class="text-l font-medium text-gray-900 dark:text-white">{channel._displayName}</h3>
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
{#if channel.is_closed}
|
{#if channel.is_closed}
|
||||||
<ClosedBadge {channel} />
|
<ClosedBadge {channel} />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
export let channel
|
export let channel
|
||||||
|
|
||||||
const endpoint = 'https://github.com/iptv-org/database/issues/new'
|
const endpoint = 'https://github.com/iptv-org/database/issues/new'
|
||||||
const title = `Edit: ${channel.displayName}`
|
const title = `Edit: ${channel._displayName}`
|
||||||
const labels = 'channels:edit'
|
const labels = 'channels:edit'
|
||||||
const template = '__channels_edit.yml'
|
const template = '__channels_edit.yml'
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { goto } from '$app/navigation'
|
|
||||||
import { query, hasQuery, setSearchParam } from '~/store'
|
|
||||||
|
|
||||||
export let data
|
export let data
|
||||||
export let close = () => {}
|
export let close = () => {}
|
||||||
|
@ -19,7 +17,7 @@
|
||||||
{
|
{
|
||||||
name: 'owners',
|
name: 'owners',
|
||||||
type: 'link[]',
|
type: 'link[]',
|
||||||
value: data.owners.map(value => ({ label: value, query: `owners:${norm(value)}` }))
|
value: data.owners.map(value => ({ label: value, query: `owner:${norm(value)}` }))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'country',
|
name: 'country',
|
||||||
|
@ -41,7 +39,7 @@
|
||||||
{
|
{
|
||||||
name: 'broadcast_area',
|
name: 'broadcast_area',
|
||||||
type: 'link[]',
|
type: 'link[]',
|
||||||
value: data._broadcast_area.map(v => ({
|
value: data._broadcastArea.map(v => ({
|
||||||
label: v.name,
|
label: v.name,
|
||||||
query: `broadcast_area:${v.type}/${v.code}`
|
query: `broadcast_area:${v.type}/${v.code}`
|
||||||
}))
|
}))
|
||||||
|
@ -49,12 +47,12 @@
|
||||||
{
|
{
|
||||||
name: 'languages',
|
name: 'languages',
|
||||||
type: 'link[]',
|
type: 'link[]',
|
||||||
value: data._languages.map(v => ({ label: v.name, query: `languages:${v.code}` }))
|
value: data._languages.map(v => ({ label: v.name, query: `language:${v.code}` }))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'categories',
|
name: 'categories',
|
||||||
type: 'link[]',
|
type: 'link[]',
|
||||||
value: data._categories.map(v => ({ label: v.name, query: `categories:${v.id}` }))
|
value: data._categories.map(v => ({ label: v.name, query: `category:${v.id}` }))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'is_nsfw',
|
name: 'is_nsfw',
|
||||||
|
|
|
@ -15,12 +15,12 @@
|
||||||
result: 'Find channels that have "Nat Geo" in the name.'
|
result: 'Find channels that have "Nat Geo" in the name.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
query: 'alt_names:חינוכית',
|
query: 'alt_name:חינוכית',
|
||||||
result: 'Finds channels whose alternative name contains "חינוכית".'
|
result: 'Finds channels whose alternative name contains "חינוכית".'
|
||||||
},
|
},
|
||||||
{ query: 'network:ABC', result: 'Finds all channels operated by the ABC Network.' },
|
{ query: 'network:ABC', result: 'Finds all channels operated by the ABC Network.' },
|
||||||
{
|
{
|
||||||
query: 'owners:^$',
|
query: 'owner:^$',
|
||||||
result: 'Finds channels that have no owner listed.'
|
result: 'Finds channels that have no owner listed.'
|
||||||
},
|
},
|
||||||
{ query: 'country:GY', result: 'Finds all channels that are broadcast from Guyana.' },
|
{ query: 'country:GY', result: 'Finds all channels that are broadcast from Guyana.' },
|
||||||
|
@ -30,8 +30,8 @@
|
||||||
},
|
},
|
||||||
{ query: 'city:"San Francisco"', result: 'Finds all channels broadcast from San Francisco.' },
|
{ query: 'city:"San Francisco"', result: 'Finds all channels broadcast from San Francisco.' },
|
||||||
{ query: 'broadcast_area:c/CV', result: 'Finds channels that are broadcast in Cape Verde.' },
|
{ query: 'broadcast_area:c/CV', result: 'Finds channels that are broadcast in Cape Verde.' },
|
||||||
{ query: 'languages:fra', result: 'Find channels that are broadcast in French.' },
|
{ query: 'language:fra', result: 'Find channels that are broadcast in French.' },
|
||||||
{ query: 'categories:news', result: 'Finds all the news channels.' },
|
{ query: 'category:news', result: 'Finds all the news channels.' },
|
||||||
{ query: 'website:.', result: 'Finds channels that have a link to the official website.' },
|
{ query: 'website:.', result: 'Finds channels that have a link to the official website.' },
|
||||||
{ query: 'is_nsfw:true', result: 'Finds channels marked as NSFW.' },
|
{ query: 'is_nsfw:true', result: 'Finds channels marked as NSFW.' },
|
||||||
{
|
{
|
||||||
|
|
44
src/models/channel.js
Normal file
44
src/models/channel.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
export class Channel {
|
||||||
|
constructor(data) {
|
||||||
|
this.id = data.id
|
||||||
|
this.name = data.name
|
||||||
|
this.alt_names = this.alt_name = data.altNames
|
||||||
|
this.network = data.network
|
||||||
|
this.owners = this.owner = data.owners
|
||||||
|
this.city = data.city
|
||||||
|
this.country = [data.country?.code, data.country?.name].filter(Boolean)
|
||||||
|
this.subdivision = data.subdivision?.code || null
|
||||||
|
this.languages = this.language = [
|
||||||
|
...data.languages.map(language => language.code),
|
||||||
|
...data.languages.map(language => language.name)
|
||||||
|
]
|
||||||
|
this.categories = this.category = data.categories.map(category => category.name)
|
||||||
|
this.broadcast_area = [
|
||||||
|
...data.broadcastArea.map(area => `${area.type}/${area.code}`).filter(Boolean),
|
||||||
|
...data.broadcastArea.map(area => area.name).filter(Boolean),
|
||||||
|
...data.regionCountries.map(country => country.code).filter(Boolean),
|
||||||
|
...data.regionCountries.map(country => country.name).filter(Boolean)
|
||||||
|
]
|
||||||
|
this.is_nsfw = data.isNSFW
|
||||||
|
this.launched = data.launched
|
||||||
|
this.closed = data.closed
|
||||||
|
this.is_closed = !!data.closed || !!data.replacedBy
|
||||||
|
this.replaced_by = data.replacedBy
|
||||||
|
this.website = data.website
|
||||||
|
this.logo = data.logo
|
||||||
|
this.streams = Array.isArray(data.streams) ? data.streams.length : 0
|
||||||
|
this.guides = Array.isArray(data.guides) ? data.guides.length : 0
|
||||||
|
this.is_blocked = Array.isArray(data.blocklistRecords) && data.blocklistRecords.length > 0
|
||||||
|
|
||||||
|
this._hasUniqueName = data._hasUniqueName
|
||||||
|
this._displayName = data._hasUniqueName ? data.name : `${data.name} (${data.country?.name})`
|
||||||
|
this._country = data.country
|
||||||
|
this._subdivision = data.subdivision || null
|
||||||
|
this._languages = data.languages
|
||||||
|
this._categories = data.categories
|
||||||
|
this._broadcastArea = data.broadcastArea
|
||||||
|
this._streams = data.streams || []
|
||||||
|
this._guides = data.guides || []
|
||||||
|
this._blocklistRecords = data.blocklistRecords || []
|
||||||
|
}
|
||||||
|
}
|
1
src/models/index.js
Normal file
1
src/models/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './channel'
|
|
@ -23,7 +23,7 @@
|
||||||
let _countries = []
|
let _countries = []
|
||||||
let isLoading = true
|
let isLoading = true
|
||||||
|
|
||||||
$: groupedByCountry = _.groupBy($filteredChannels, 'country')
|
$: groupedByCountry = _.groupBy($filteredChannels, channel => channel._country.code)
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!$channels.length) {
|
if (!$channels.length) {
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
export let data
|
export let data
|
||||||
|
|
||||||
let isLoading = false
|
let isLoading = false
|
||||||
let channel = data.channel
|
const channel = data.channel
|
||||||
let streams = channel ? channel._streams : []
|
const streams = channel ? channel._streams : []
|
||||||
let guides = channel ? channel._guides : []
|
const guides = channel ? channel._guides : []
|
||||||
|
const displayName = channel._displayName
|
||||||
|
|
||||||
const structuredData = {
|
const structuredData = {
|
||||||
'@context': 'https://schema.org/',
|
'@context': 'https://schema.org/',
|
||||||
|
@ -28,8 +29,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{channel && channel.displayName ? `${channel.displayName} • iptv-org` : 'iptv-org'}</title>
|
<title>{channel && displayName ? `${displayName} • iptv-org` : 'iptv-org'}</title>
|
||||||
<meta name="description" content="Detailed description of {channel.displayName}." />
|
<meta name="description" content="Detailed description of {displayName}." />
|
||||||
{@html schema()}
|
{@html schema()}
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@
|
||||||
<div class="w-2/3 overflow-hidden">
|
<div class="w-2/3 overflow-hidden">
|
||||||
<div class="flex space-x-3">
|
<div class="flex space-x-3">
|
||||||
<h1 class="text-l font-medium text-gray-900 dark:text-white">
|
<h1 class="text-l font-medium text-gray-900 dark:text-white">
|
||||||
{channel.displayName}
|
{displayName}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
{#if channel.is_closed}
|
{#if channel.is_closed}
|
||||||
|
|
78
src/store.js
78
src/store.js
|
@ -3,6 +3,7 @@ import { Playlist, Link } from 'iptv-playlist-generator'
|
||||||
import sj from '@freearhey/search-js'
|
import sj from '@freearhey/search-js'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { browser } from '$app/environment'
|
import { browser } from '$app/environment'
|
||||||
|
import { Channel } from './models'
|
||||||
|
|
||||||
export const query = writable('')
|
export const query = writable('')
|
||||||
export const hasQuery = writable(false)
|
export const hasQuery = writable(false)
|
||||||
|
@ -32,7 +33,7 @@ export async function fetchChannels() {
|
||||||
|
|
||||||
countries.set(api.countries)
|
countries.set(api.countries)
|
||||||
|
|
||||||
let _channels = api.channels.map(c => transformChannel(c, api))
|
let _channels = api.channels.map(c => createChannel(c, api))
|
||||||
|
|
||||||
channels.set(_channels)
|
channels.set(_channels)
|
||||||
filteredChannels.set(_channels)
|
filteredChannels.set(_channels)
|
||||||
|
@ -41,13 +42,17 @@ export async function fetchChannels() {
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
'alt_names',
|
'alt_names',
|
||||||
|
'alt_name',
|
||||||
'network',
|
'network',
|
||||||
|
'owner',
|
||||||
'owners',
|
'owners',
|
||||||
'country',
|
'country',
|
||||||
'subdivision',
|
'subdivision',
|
||||||
'city',
|
'city',
|
||||||
'broadcast_area',
|
'broadcast_area',
|
||||||
|
'language',
|
||||||
'languages',
|
'languages',
|
||||||
|
'category',
|
||||||
'categories',
|
'categories',
|
||||||
'launched',
|
'launched',
|
||||||
'closed',
|
'closed',
|
||||||
|
@ -155,38 +160,59 @@ async function loadAPI() {
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transformChannel(channel, data) {
|
function createChannel(data, api) {
|
||||||
channel._streams = data.streams[channel.id] || []
|
let broadcastArea = []
|
||||||
channel._guides = data.guides[channel.id] || []
|
let regionCountries = []
|
||||||
channel._country = data.countries[channel.country]
|
|
||||||
channel._subdivision = data.subdivisions[channel.subdivision]
|
data.broadcast_area.forEach(areaCode => {
|
||||||
channel._languages = channel.languages.map(code => data.languages[code]).filter(i => i)
|
const [type, code] = areaCode.split('/')
|
||||||
channel._categories = channel.categories.map(id => data.categories[id]).filter(i => i)
|
|
||||||
channel._broadcast_area = channel.broadcast_area.map(value => {
|
|
||||||
const [type, code] = value.split('/')
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'c':
|
case 'c':
|
||||||
return { type, ...data.countries[code] }
|
const country = api.countries[code]
|
||||||
|
if (country) broadcastArea.push({ type, code: country.code, name: country.name })
|
||||||
|
break
|
||||||
case 'r':
|
case 'r':
|
||||||
return { type, ...data.regions[code] }
|
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':
|
case 's':
|
||||||
return { type, ...data.subdivisions[code] }
|
const subdivision = api.subdivisions[code]
|
||||||
|
if (subdivision)
|
||||||
|
broadcastArea.push({ type, code: subdivision.code, name: subdivision.name })
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
channel.is_closed = !!channel.closed || !!channel.replaced_by
|
|
||||||
channel.is_blocked = !!data.blocklist[channel.id]
|
|
||||||
channel.streams = channel._streams.length
|
|
||||||
channel.guides = channel._guides.length
|
|
||||||
channel.blocklist_records = Array.isArray(data.blocklist[channel.id])
|
|
||||||
? data.blocklist[channel.id]
|
|
||||||
: []
|
|
||||||
|
|
||||||
const isChannelNameRepeated = data.nameIndex[channel.name.toLowerCase()].length > 1
|
return new Channel({
|
||||||
channel.displayName = isChannelNameRepeated
|
id: data.id,
|
||||||
? `${channel.name} (${channel._country.name})`
|
name: data.name,
|
||||||
: channel.name
|
altNames: data.alt_names,
|
||||||
|
network: data.network,
|
||||||
return channel
|
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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStreams() {
|
function getStreams() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue