mirror of
https://github.com/iptv-org/iptv-org.github.io.git
synced 2025-05-10 09:00: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