mirror of
https://github.com/iptv-org/iptv-org.github.io.git
synced 2025-05-12 18:10:06 -04:00
Init
This commit is contained in:
parent
98495b1e7a
commit
59b459dcf7
27 changed files with 3149 additions and 299 deletions
18
src/app.html
Normal file
18
src/app.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="description" content="" />
|
||||
<link rel="icon" href="%svelte.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css" />
|
||||
<script
|
||||
type="module"
|
||||
src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"
|
||||
></script>
|
||||
%svelte.head%
|
||||
</head>
|
||||
<body style="background-color: #f6f8fa; min-height: 100vh">
|
||||
<div>%svelte.body%</div>
|
||||
</body>
|
||||
</html>
|
30
src/components/ChannelItem.svelte
Normal file
30
src/components/ChannelItem.svelte
Normal file
|
@ -0,0 +1,30 @@
|
|||
<script>
|
||||
export let channel
|
||||
</script>
|
||||
|
||||
<tr>
|
||||
<td class="is-vcentered" style="min-width: 150px; text-align: center">
|
||||
{#if channel && channel.logo}
|
||||
<img
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
src="{channel.logo}"
|
||||
alt="{channel.name}"
|
||||
style="max-width: 100px; max-height: 50px; vertical-align: middle"
|
||||
/>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="is-vcentered" nowrap>
|
||||
<p>{channel.name}</p>
|
||||
</td>
|
||||
<td class="is-vcentered" nowrap>
|
||||
<code style="user-select: all">{channel.id}</code>
|
||||
</td>
|
||||
<td class="is-vcentered">
|
||||
{#each channel.guides as guide}
|
||||
<p>
|
||||
<code style="white-space: nowrap; user-select: all">{guide.url}</code>
|
||||
</p>
|
||||
{/each}
|
||||
</td>
|
||||
</tr>
|
65
src/components/CountryItem.svelte
Normal file
65
src/components/CountryItem.svelte
Normal file
|
@ -0,0 +1,65 @@
|
|||
<script>
|
||||
import ChannelItem from './ChannelItem.svelte'
|
||||
|
||||
export let country
|
||||
export let channels = []
|
||||
export let normQuery
|
||||
|
||||
function onExpand() {
|
||||
country.expanded = !country.expanded
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if channels && channels.length > 0}
|
||||
<div class="card mb-3 is-shadowless" style="border: 1px solid #dbdbdb">
|
||||
<div class="card-header is-shadowless is-clickable" on:click="{onExpand}">
|
||||
<span class="card-header-title">{country.flag} {country.name}</span>
|
||||
<button class="card-header-icon" aria-label="more options">
|
||||
<span class="icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512">
|
||||
{#if !country.expanded}
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="48"
|
||||
d="M112 184l144 144 144-144"
|
||||
/>
|
||||
{/if} {#if country.expanded}
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="48"
|
||||
d="M112 328l144-144 144 144"
|
||||
/>
|
||||
{/if}
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
{#if country.expanded || (channels && channels.length > 0 && normQuery.length)}
|
||||
<div class="card-content">
|
||||
<div class="table-container">
|
||||
<table class="table" style="min-width: 100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>TVG-ID</th>
|
||||
<th>EPG</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each channels as channel}
|
||||
<ChannelItem bind:channel="{channel}"></ChannelItem>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
59
src/routes/__layout.svelte
Normal file
59
src/routes/__layout.svelte
Normal file
|
@ -0,0 +1,59 @@
|
|||
<script>
|
||||
let scrollTop = 0
|
||||
|
||||
function scrollToTop(argument) {
|
||||
document.body.scrollTop = 0
|
||||
document.documentElement.scrollTop = 0
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY="{scrollTop}" />
|
||||
|
||||
<div class="navbar" style="background-color: transparent">
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<a href="https://github.com/iptv-org/database">
|
||||
<span class="icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<path
|
||||
d="M256 32C132.3 32 32 134.9 32 261.7c0 101.5 64.2 187.5 153.2 217.9a17.56 17.56 0 003.8.4c8.3 0 11.5-6.1 11.5-11.4 0-5.5-.2-19.9-.3-39.1a102.4 102.4 0 01-22.6 2.7c-43.1 0-52.9-33.5-52.9-33.5-10.2-26.5-24.9-33.6-24.9-33.6-19.5-13.7-.1-14.1 1.4-14.1h.1c22.5 2 34.3 23.8 34.3 23.8 11.2 19.6 26.2 25.1 39.6 25.1a63 63 0 0025.6-6c2-14.8 7.8-24.9 14.2-30.7-49.7-5.8-102-25.5-102-113.5 0-25.1 8.7-45.6 23-61.6-2.3-5.8-10-29.2 2.2-60.8a18.64 18.64 0 015-.5c8.1 0 26.4 3.1 56.6 24.1a208.21 208.21 0 01112.2 0c30.2-21 48.5-24.1 56.6-24.1a18.64 18.64 0 015 .5c12.2 31.6 4.5 55 2.2 60.8 14.3 16.1 23 36.6 23 61.6 0 88.2-52.4 107.6-102.3 113.3 8 7.1 15.2 21.1 15.2 42.5 0 30.7-.3 55.5-.3 63 0 5.4 3.1 11.5 11.4 11.5a19.35 19.35 0 004-.4C415.9 449.2 480 363.1 480 261.7 480 134.9 379.7 32 256 32z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<footer
|
||||
class="footer"
|
||||
style="
|
||||
background-color: transparent;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 3rem 1.5rem;
|
||||
pointer-events: none;
|
||||
"
|
||||
>
|
||||
<div class="content">
|
||||
<div class="level">
|
||||
<div class="level-left"></div>
|
||||
<div class="level-right">
|
||||
{#if scrollTop > 100}
|
||||
<button
|
||||
class="button level-item is-hidden-mobile"
|
||||
on:click="{scrollToTop}"
|
||||
style="pointer-events: auto"
|
||||
>
|
||||
<span class="icon is-small">
|
||||
<ion-icon name="arrow-up-outline"></ion-icon>
|
||||
</span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
122
src/routes/index.svelte
Normal file
122
src/routes/index.svelte
Normal file
|
@ -0,0 +1,122 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte'
|
||||
import CountryItem from '../components/CountryItem.svelte'
|
||||
import _ from 'lodash'
|
||||
|
||||
let query = ''
|
||||
let normQuery = ''
|
||||
let regQuery = ''
|
||||
let isLoading = true
|
||||
let countries = []
|
||||
let channels = []
|
||||
let filtered = []
|
||||
|
||||
$: grouped = _.groupBy(filtered, 'country')
|
||||
|
||||
function search() {
|
||||
normQuery = query.replace(/\s/g, '').toLowerCase()
|
||||
regQuery = new RegExp(query)
|
||||
|
||||
if (!normQuery) {
|
||||
filtered = channels
|
||||
}
|
||||
|
||||
filtered = channels.filter(c => {
|
||||
const normResult = c.key.includes(normQuery)
|
||||
const regResult = regQuery ? regQuery.test(c.name) || regQuery.test(c.id) : false
|
||||
|
||||
return normResult || regResult
|
||||
})
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
let guides = await fetch('https://iptv-org.github.io/api/guides.json')
|
||||
.then(response => response.json())
|
||||
.catch(console.log)
|
||||
guides = guides.length ? guides : []
|
||||
guides = _.groupBy(guides, 'channel')
|
||||
|
||||
channels = await fetch('https://iptv-org.github.io/api/channels.json')
|
||||
.then(response => response.json())
|
||||
.then(arr =>
|
||||
arr.map(c => {
|
||||
c.key = `${c.id}_${c.name}`.replace(/\s/g, '').toLowerCase()
|
||||
c.guides = guides[c.id] || []
|
||||
return c
|
||||
})
|
||||
)
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
return []
|
||||
})
|
||||
|
||||
const countriesJson = await fetch('https://iptv-org.github.io/api/countries.json')
|
||||
.then(response => response.json())
|
||||
.catch(console.log)
|
||||
|
||||
countries = countriesJson.map(i => {
|
||||
i.expanded = false
|
||||
return i
|
||||
})
|
||||
|
||||
filtered = channels
|
||||
|
||||
isLoading = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="section">
|
||||
<div class="container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-9">
|
||||
<form class="mb-5" on:submit|preventDefault="{search}">
|
||||
<div class="field-body">
|
||||
<div class="field is-expanded">
|
||||
<div class="field has-addons">
|
||||
<div class="control is-expanded">
|
||||
<input
|
||||
class="input"
|
||||
type="search"
|
||||
bind:value="{query}"
|
||||
placeholder="Search by channel name..."
|
||||
/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-info" type="submit">
|
||||
<span class="icon is-small is-right">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style="width: 1.25rem; height: 1.25rem"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="M456.69 421.39L362.6 327.3a173.81 173.81 0 0034.84-104.58C397.44 126.38 319.06 48 222.72 48S48 126.38 48 222.72s78.38 174.72 174.72 174.72A173.81 173.81 0 00327.3 362.6l94.09 94.09a25 25 0 0035.3-35.3zM97.92 222.72a124.8 124.8 0 11124.8 124.8 124.95 124.95 0 01-124.8-124.8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{#if !isLoading}
|
||||
<p class="help">Found { filtered.length.toLocaleString() } channels</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{#if isLoading}
|
||||
<div class="level">
|
||||
<div class="level-item">Loading...</div>
|
||||
</div>
|
||||
{/if} {#each countries as country}
|
||||
<CountryItem
|
||||
bind:country="{country}"
|
||||
bind:channels="{grouped[country.code]}"
|
||||
bind:normQuery="{normQuery}"
|
||||
></CountryItem>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue