mirror of
https://github.com/iptv-org/iptv-org.github.io.git
synced 2025-05-11 01:20:06 -04:00
Init
This commit is contained in:
parent
98495b1e7a
commit
59b459dcf7
27 changed files with 3149 additions and 299 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -1 +1,8 @@
|
||||||
/node_modules
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
#/build
|
||||||
|
/.svelte-kit
|
||||||
|
/package
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
1
.npmrc
Normal file
1
.npmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
engine-strict=true
|
40
README.md
Normal file
40
README.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# create-svelte
|
||||||
|
|
||||||
|
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
||||||
|
|
||||||
|
## Creating a project
|
||||||
|
|
||||||
|
If you're seeing this, you've probably already done this step. Congrats!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create a new project in the current directory
|
||||||
|
npm init svelte@next
|
||||||
|
|
||||||
|
# create a new project in my-app
|
||||||
|
npm init svelte@next my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: the `@next` is temporary
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# or start the server and open the app in a new browser tab
|
||||||
|
npm run dev -- --open
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To create a production version of your app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
You can preview the production build with `npm run preview`.
|
||||||
|
|
||||||
|
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
177
app.js
177
app.js
|
@ -1,177 +0,0 @@
|
||||||
const ChannelItem = {
|
|
||||||
props: ['channel'],
|
|
||||||
template: `
|
|
||||||
<tr>
|
|
||||||
<td class="is-vcentered" style="min-width: 150px; text-align: center">
|
|
||||||
<img
|
|
||||||
loading="lazy"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
v-show="channel.logo"
|
|
||||||
:src="channel.logo"
|
|
||||||
style="max-width: 100px; max-height: 50px; vertical-align: middle"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td class="is-vcentered" nowrap>
|
|
||||||
<p v-text="channel.name"></p>
|
|
||||||
</td>
|
|
||||||
<td class="is-vcentered" nowrap>
|
|
||||||
<code v-text="channel.id" style="user-select: all;"></code>
|
|
||||||
</td>
|
|
||||||
<td class="is-vcentered">
|
|
||||||
<p v-for="guide in channel.guides"><code style="white-space: nowrap; user-select: all;" v-text="guide.url"></code></p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
const CountryItem = {
|
|
||||||
components: {
|
|
||||||
ChannelItem
|
|
||||||
},
|
|
||||||
props: ['channels', 'normQuery', 'regQuery', 'country'],
|
|
||||||
template: `
|
|
||||||
<div
|
|
||||||
class="card mb-3 is-shadowless"
|
|
||||||
style="border: 1px solid #dbdbdb"
|
|
||||||
v-show="channels && channels.length > 0"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="card-header is-shadowless is-clickable"
|
|
||||||
@click="country.expanded = !country.expanded"
|
|
||||||
>
|
|
||||||
<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">
|
|
||||||
<path
|
|
||||||
v-show="!country.expanded"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="48"
|
|
||||||
d="M112 184l144 144 144-144"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
v-show="country.expanded"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="48"
|
|
||||||
d="M112 328l144-144 144 144"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="card-content" v-show="country.expanded || (channels && channels.length > 0 && normQuery.length)">
|
|
||||||
<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>
|
|
||||||
<channel-item v-for="channel in channels" :channel="channel"></channel-item>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
const App = {
|
|
||||||
compilerOptions: {
|
|
||||||
isCustomElement: tag => tag.startsWith('ion-')
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
CountryItem
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isLoading: true,
|
|
||||||
query: '',
|
|
||||||
normQuery: '',
|
|
||||||
regQuery: null,
|
|
||||||
countries: [],
|
|
||||||
channels: [],
|
|
||||||
scrollTop: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
filtered() {
|
|
||||||
if (!this.normQuery) return this.channels
|
|
||||||
|
|
||||||
return this.channels.filter(c => {
|
|
||||||
const normResult = c.key.includes(this.normQuery)
|
|
||||||
const regResult = this.regQuery
|
|
||||||
? this.regQuery.test(c.name) || this.regQuery.test(c.id)
|
|
||||||
: false
|
|
||||||
|
|
||||||
return normResult || regResult
|
|
||||||
})
|
|
||||||
},
|
|
||||||
grouped() {
|
|
||||||
return _.groupBy(this.filtered, 'country')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
search() {
|
|
||||||
this.normQuery = this.query.replace(/\s/g, '').toLowerCase()
|
|
||||||
this.regQuery = new RegExp(this.query)
|
|
||||||
},
|
|
||||||
scrollToTop() {
|
|
||||||
document.body.scrollTop = 0
|
|
||||||
document.documentElement.scrollTop = 0
|
|
||||||
},
|
|
||||||
onScroll(e) {
|
|
||||||
this.scrollTop = window.top.scrollY
|
|
||||||
},
|
|
||||||
async loadChannels() {
|
|
||||||
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')
|
|
||||||
|
|
||||||
this.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 countries = await fetch('https://iptv-org.github.io/api/countries.json')
|
|
||||||
.then(response => response.json())
|
|
||||||
.catch(console.log)
|
|
||||||
|
|
||||||
this.countries = countries.map(i => {
|
|
||||||
i.expanded = false
|
|
||||||
return i
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
window.removeEventListener('scroll', this.onScroll)
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
window.addEventListener('scroll', this.onScroll)
|
|
||||||
await this.loadChannels()
|
|
||||||
this.isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vue.createApp(App).mount('#app')
|
|
0
build/.nojekyll
Normal file
0
build/.nojekyll
Normal file
27
build/_app/chunks/vendor-dedc54d0.js
Normal file
27
build/_app/chunks/vendor-dedc54d0.js
Normal file
File diff suppressed because one or more lines are too long
1
build/_app/error.svelte-aa63ee3d.js
Normal file
1
build/_app/error.svelte-aa63ee3d.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import{S as h,i as w,s as y,e as E,t as v,c as d,a as b,h as P,d as o,g as u,I as R,j as N,k as S,l as C,m as j,K as H}from"./chunks/vendor-dedc54d0.js";function I(r){let l,t=r[1].frame+"",a;return{c(){l=E("pre"),a=v(t)},l(f){l=d(f,"PRE",{});var s=b(l);a=P(s,t),s.forEach(o)},m(f,s){u(f,l,s),R(l,a)},p(f,s){s&2&&t!==(t=f[1].frame+"")&&N(a,t)},d(f){f&&o(l)}}}function K(r){let l,t=r[1].stack+"",a;return{c(){l=E("pre"),a=v(t)},l(f){l=d(f,"PRE",{});var s=b(l);a=P(s,t),s.forEach(o)},m(f,s){u(f,l,s),R(l,a)},p(f,s){s&2&&t!==(t=f[1].stack+"")&&N(a,t)},d(f){f&&o(l)}}}function z(r){let l,t,a,f,s=r[1].message+"",c,k,n,p,i=r[1].frame&&I(r),_=r[1].stack&&K(r);return{c(){l=E("h1"),t=v(r[0]),a=S(),f=E("pre"),c=v(s),k=S(),i&&i.c(),n=S(),_&&_.c(),p=C()},l(e){l=d(e,"H1",{});var m=b(l);t=P(m,r[0]),m.forEach(o),a=j(e),f=d(e,"PRE",{});var q=b(f);c=P(q,s),q.forEach(o),k=j(e),i&&i.l(e),n=j(e),_&&_.l(e),p=C()},m(e,m){u(e,l,m),R(l,t),u(e,a,m),u(e,f,m),R(f,c),u(e,k,m),i&&i.m(e,m),u(e,n,m),_&&_.m(e,m),u(e,p,m)},p(e,[m]){m&1&&N(t,e[0]),m&2&&s!==(s=e[1].message+"")&&N(c,s),e[1].frame?i?i.p(e,m):(i=I(e),i.c(),i.m(n.parentNode,n)):i&&(i.d(1),i=null),e[1].stack?_?_.p(e,m):(_=K(e),_.c(),_.m(p.parentNode,p)):_&&(_.d(1),_=null)},i:H,o:H,d(e){e&&o(l),e&&o(a),e&&o(f),e&&o(k),i&&i.d(e),e&&o(n),_&&_.d(e),e&&o(p)}}}function D({error:r,status:l}){return{props:{error:r,status:l}}}function A(r,l,t){let{status:a}=l,{error:f}=l;return r.$$set=s=>{"status"in s&&t(0,a=s.status),"error"in s&&t(1,f=s.error)},[a,f]}class F extends h{constructor(l){super();w(this,l,A,z,y,{status:0,error:1})}}export{F as default,D as load};
|
45
build/_app/manifest.json
Normal file
45
build/_app/manifest.json
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
".svelte-kit/runtime/client/start.js": {
|
||||||
|
"file": "start-c960b34f.js",
|
||||||
|
"src": ".svelte-kit/runtime/client/start.js",
|
||||||
|
"isEntry": true,
|
||||||
|
"imports": [
|
||||||
|
"_vendor-dedc54d0.js"
|
||||||
|
],
|
||||||
|
"dynamicImports": [
|
||||||
|
"src/routes/__layout.svelte",
|
||||||
|
".svelte-kit/runtime/components/error.svelte",
|
||||||
|
"src/routes/index.svelte"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"src/routes/__layout.svelte": {
|
||||||
|
"file": "pages/__layout.svelte-b87d0c05.js",
|
||||||
|
"src": "src/routes/__layout.svelte",
|
||||||
|
"isEntry": true,
|
||||||
|
"isDynamicEntry": true,
|
||||||
|
"imports": [
|
||||||
|
"_vendor-dedc54d0.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
".svelte-kit/runtime/components/error.svelte": {
|
||||||
|
"file": "error.svelte-aa63ee3d.js",
|
||||||
|
"src": ".svelte-kit/runtime/components/error.svelte",
|
||||||
|
"isEntry": true,
|
||||||
|
"isDynamicEntry": true,
|
||||||
|
"imports": [
|
||||||
|
"_vendor-dedc54d0.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"src/routes/index.svelte": {
|
||||||
|
"file": "pages/index.svelte-1502efd3.js",
|
||||||
|
"src": "src/routes/index.svelte",
|
||||||
|
"isEntry": true,
|
||||||
|
"isDynamicEntry": true,
|
||||||
|
"imports": [
|
||||||
|
"_vendor-dedc54d0.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"_vendor-dedc54d0.js": {
|
||||||
|
"file": "chunks/vendor-dedc54d0.js"
|
||||||
|
}
|
||||||
|
}
|
1
build/_app/pages/__layout.svelte-b87d0c05.js
Normal file
1
build/_app/pages/__layout.svelte-b87d0c05.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import{S as Q,i as W,s as Z,F as x,G as ee,e as v,c as _,a as r,d as s,H as se,b as o,f as w,g as y,I as d,J as j,K as te,L as U,k as P,M as X,m as q,N as le,O as ae,P as oe,q as re,o as ne}from"../chunks/vendor-dedc54d0.js";function Y(b){let t,m,p,a,h;return{c(){t=v("button"),m=v("span"),p=v("ion-icon"),this.h()},l(n){t=_(n,"BUTTON",{class:!0,style:!0});var i=r(t);m=_(i,"SPAN",{class:!0});var g=r(m);p=_(g,"ION-ICON",{name:!0}),r(p).forEach(s),g.forEach(s),i.forEach(s),this.h()},h(){se(p,"name","arrow-up-outline"),o(m,"class","icon is-small"),o(t,"class","button level-item is-hidden-mobile"),w(t,"pointer-events","auto")},m(n,i){y(n,t,i),d(t,m),d(m,p),a||(h=j(t,"click",ce),a=!0)},p:te,d(n){n&&s(t),a=!1,h()}}}function ie(b){let t=!1,m=()=>{t=!1},p,a,h,n,i,g,T,D,N,$,c,O,E,V,S,I,k,A,F;x(b[3]);const B=b[2].default,u=ee(B,b,b[1],null);let l=b[0]>100&&Y();return{c(){a=v("div"),h=v("div"),n=v("div"),i=v("a"),g=v("span"),T=U("svg"),D=U("path"),N=P(),u&&u.c(),$=P(),c=v("footer"),O=v("div"),E=v("div"),V=v("div"),S=P(),I=v("div"),l&&l.c(),this.h()},l(e){a=_(e,"DIV",{class:!0,style:!0});var f=r(a);h=_(f,"DIV",{class:!0});var M=r(h);n=_(M,"DIV",{class:!0});var z=r(n);i=_(z,"A",{href:!0});var G=r(i);g=_(G,"SPAN",{class:!0});var H=r(g);T=X(H,"svg",{xmlns:!0,viewBox:!0});var J=r(T);D=X(J,"path",{d:!0}),r(D).forEach(s),J.forEach(s),H.forEach(s),G.forEach(s),z.forEach(s),M.forEach(s),f.forEach(s),N=q(e),u&&u.l(e),$=q(e),c=_(e,"FOOTER",{class:!0,style:!0});var K=r(c);O=_(K,"DIV",{class:!0});var L=r(O);E=_(L,"DIV",{class:!0});var C=r(E);V=_(C,"DIV",{class:!0}),r(V).forEach(s),S=q(C),I=_(C,"DIV",{class:!0});var R=r(I);l&&l.l(R),R.forEach(s),C.forEach(s),L.forEach(s),K.forEach(s),this.h()},h(){o(D,"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"),o(T,"xmlns","http://www.w3.org/2000/svg"),o(T,"viewBox","0 0 512 512"),o(g,"class","icon"),o(i,"href","https://github.com/iptv-org/database"),o(n,"class","navbar-item"),o(h,"class","navbar-end"),o(a,"class","navbar"),w(a,"background-color","transparent"),o(V,"class","level-left"),o(I,"class","level-right"),o(E,"class","level"),o(O,"class","content"),o(c,"class","footer"),w(c,"background-color","transparent"),w(c,"position","fixed"),w(c,"bottom","0"),w(c,"width","100%"),w(c,"padding","3rem 1.5rem"),w(c,"pointer-events","none")},m(e,f){y(e,a,f),d(a,h),d(h,n),d(n,i),d(i,g),d(g,T),d(T,D),y(e,N,f),u&&u.m(e,f),y(e,$,f),y(e,c,f),d(c,O),d(O,E),d(E,V),d(E,S),d(E,I),l&&l.m(I,null),k=!0,A||(F=j(window,"scroll",()=>{t=!0,clearTimeout(p),p=setTimeout(m,100),b[3]()}),A=!0)},p(e,[f]){f&1&&!t&&(t=!0,clearTimeout(p),scrollTo(window.pageXOffset,e[0]),p=setTimeout(m,100)),u&&u.p&&(!k||f&2)&&le(u,B,e,e[1],k?oe(B,e[1],f,null):ae(e[1]),null),e[0]>100?l?l.p(e,f):(l=Y(),l.c(),l.m(I,null)):l&&(l.d(1),l=null)},i(e){k||(re(u,e),k=!0)},o(e){ne(u,e),k=!1},d(e){e&&s(a),e&&s(N),u&&u.d(e),e&&s($),e&&s(c),l&&l.d(),A=!1,F()}}}function ce(b){document.body.scrollTop=0,document.documentElement.scrollTop=0}function ue(b,t,m){let{$$slots:p={},$$scope:a}=t,h=0;function n(){m(0,h=window.pageYOffset)}return b.$$set=i=>{"$$scope"in i&&m(1,a=i.$$scope)},[h,a,p,n]}class de extends Q{constructor(t){super();W(this,t,ue,ie,Z,{})}}export{de as default};
|
1
build/_app/pages/index.svelte-1502efd3.js
Normal file
1
build/_app/pages/index.svelte-1502efd3.js
Normal file
File diff suppressed because one or more lines are too long
1
build/_app/start-c960b34f.js
Normal file
1
build/_app/start-c960b34f.js
Normal file
File diff suppressed because one or more lines are too long
1
build/_app/version.json
Normal file
1
build/_app/version.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":"1645518660819"}
|
BIN
build/favicon.png
Normal file
BIN
build/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
58
build/index.html
Normal file
58
build/index.html
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="description" content="" />
|
||||||
|
<link rel="icon" href="./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>
|
||||||
|
<meta http-equiv="content-security-policy" content="">
|
||||||
|
<link rel="modulepreload" href="/_app/start-c960b34f.js">
|
||||||
|
<link rel="modulepreload" href="/_app/chunks/vendor-dedc54d0.js">
|
||||||
|
<link rel="modulepreload" href="/_app/pages/__layout.svelte-b87d0c05.js">
|
||||||
|
<link rel="modulepreload" href="/_app/pages/index.svelte-1502efd3.js">
|
||||||
|
</head>
|
||||||
|
<body style="background-color: #f6f8fa; min-height: 100vh">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<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"></path></svg></span></a></div></div></div>
|
||||||
|
|
||||||
|
<div class="section"><div class="container"><div class="columns is-centered"><div class="column is-9"><form class="mb-5"><div class="field-body"><div class="field is-expanded"><div class="field has-addons"><div class="control is-expanded"><input class="input" type="search" placeholder="Search by channel name..." value=""></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"></path></svg></span></button></div></div>
|
||||||
|
</div></div></form>
|
||||||
|
|
||||||
|
<div class="level"><div class="level-item">Loading...</div></div> </div></div></div></div>
|
||||||
|
|
||||||
|
<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"></div></div></div></footer>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module" data-hydrate="1y836ta">
|
||||||
|
import { start } from "/_app/start-c960b34f.js";
|
||||||
|
start({
|
||||||
|
target: document.querySelector('[data-hydrate="1y836ta"]').parentNode,
|
||||||
|
paths: {"base":"","assets":""},
|
||||||
|
session: {},
|
||||||
|
route: true,
|
||||||
|
spa: false,
|
||||||
|
trailing_slash: "never",
|
||||||
|
hydrate: {
|
||||||
|
status: 200,
|
||||||
|
error: null,
|
||||||
|
nodes: [
|
||||||
|
import("/_app/pages/__layout.svelte-b87d0c05.js"),
|
||||||
|
import("/_app/pages/index.svelte-1502efd3.js")
|
||||||
|
],
|
||||||
|
params: {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
gh-pages.js
Normal file
13
gh-pages.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
var { publish } = require('gh-pages')
|
||||||
|
|
||||||
|
publish(
|
||||||
|
'build',
|
||||||
|
{
|
||||||
|
branch: 'gh-pages',
|
||||||
|
repo: 'https://github.com/iptv-org/iptv-org.github.io.git',
|
||||||
|
dotfiles: true
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
console.log('Deploy Complete!')
|
||||||
|
}
|
||||||
|
)
|
121
index.html
121
index.html
|
@ -1,121 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>iptv-org</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css" />
|
|
||||||
<script src="https://unpkg.com/vue@next"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
|
|
||||||
<script
|
|
||||||
type="module"
|
|
||||||
src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"
|
|
||||||
></script>
|
|
||||||
</head>
|
|
||||||
<body style="background-color: #f6f8fa; min-height: 100vh">
|
|
||||||
<div id="app">
|
|
||||||
<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>
|
|
||||||
<div class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="columns is-centered">
|
|
||||||
<div class="column is-9">
|
|
||||||
<form class="mb-5" @submit.prevent="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"
|
|
||||||
v-model="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>
|
|
||||||
<p v-if="!isLoading" class="help">
|
|
||||||
Found {{ filtered.length.toLocaleString() }} channels
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div v-show="isLoading" class="level">
|
|
||||||
<div class="level-item">Loading...</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<country-item
|
|
||||||
v-for="country in countries"
|
|
||||||
:country="country"
|
|
||||||
:channels="grouped[country.code]"
|
|
||||||
:norm-query="normQuery"
|
|
||||||
:reg-query="regQuery"
|
|
||||||
></country-item>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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">
|
|
||||||
<button
|
|
||||||
v-show="scrollTop > 100"
|
|
||||||
class="button level-item is-hidden-mobile"
|
|
||||||
@click="scrollToTop()"
|
|
||||||
style="pointer-events: auto"
|
|
||||||
>
|
|
||||||
<span class="icon is-small">
|
|
||||||
<ion-icon name="arrow-up-outline"></ion-icon>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="app.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
10
jsconfig.json
Normal file
10
jsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"$lib": ["src/lib"],
|
||||||
|
"$lib/*": ["src/lib/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
|
||||||
|
}
|
2615
package-lock.json
generated
Normal file
2615
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
23
package.json
Normal file
23
package.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "site",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "svelte-kit dev",
|
||||||
|
"build": "svelte-kit build",
|
||||||
|
"package": "svelte-kit package",
|
||||||
|
"preview": "svelte-kit preview",
|
||||||
|
"deploy": "node ./gh-pages.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/adapter-auto": "next",
|
||||||
|
"@sveltejs/adapter-static": "^1.0.0-next.28",
|
||||||
|
"@sveltejs/kit": "next",
|
||||||
|
"gh-pages": "^3.2.3",
|
||||||
|
"prettier-plugin-svelte": "^2.6.0",
|
||||||
|
"svelte": "^3.44.0"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
}
|
||||||
|
}
|
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>
|
0
static/.nojekyll
Normal file
0
static/.nojekyll
Normal file
BIN
static/favicon.png
Normal file
BIN
static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
10
svelte.config.js
Normal file
10
svelte.config.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import adapter from '@sveltejs/adapter-static'
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
const config = {
|
||||||
|
kit: {
|
||||||
|
adapter: adapter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
Loading…
Add table
Add a link
Reference in a new issue