152 lines
8.0 KiB
HTML
152 lines
8.0 KiB
HTML
{{define "title"}}{{.Title}}{{end}}
|
|
|
|
{{define "metadescription"}}Самообновляемые бесплатные IPTV-плейлисты для домашнего просмотра по коротким ссылкам, списки каналов, проверка доступности{{end}}
|
|
|
|
{{define "metakeywords"}}самообновляемые,бесплатные,iptv-плейлисты,iptv,плейлисты{{end}}
|
|
|
|
{{define "head"}}
|
|
<style>
|
|
.card {transition: box-shadow .2s, transform .2s}
|
|
.card.hover-success:hover {transform: translateY(-7px); box-shadow: rgba(var(--bs-success-rgb), 1) 0 5px 20px -5px}
|
|
.card.hover-danger:hover {transform: translateY(-7px); box-shadow: rgba(var(--bs-danger-rgb), 1) 0 5px 20px -5px}
|
|
.card.hover-secondary:hover {transform: translateY(-7px); box-shadow: rgba(var(--bs-secondary-rgb), 1) 0 5px 20px -5px}
|
|
</style>
|
|
<script>
|
|
const DEFAULT_LOGO = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect width="100" height="100" fill="#333"/><text x="50" y="55" text-anchor="middle" fill="#999" font-size="14">no logo</text></svg>')
|
|
|
|
function setDefaultLogo(imgtag) {
|
|
imgtag.onerror = null
|
|
imgtag.src = DEFAULT_LOGO
|
|
}
|
|
</script>
|
|
{{end}}
|
|
|
|
{{define "header"}}
|
|
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4">
|
|
<div class="mb-2">
|
|
<h2 class="mb-0">Список плейлистов ({{.Count}})</h2>
|
|
<div class="text-muted small">Изменён {{.UpdatedAt}} МСК</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{define "content"}}
|
|
{{if .Error}}
|
|
<div class="alert alert-danger" role="alert">
|
|
<ion-icon name="alert-circle-outline" class="me-1"></ion-icon>{{.Error}}
|
|
</div>
|
|
{{else if eq (len .Playlists) 0}}
|
|
<div class="alert alert-warning" role="alert">
|
|
<ion-icon name="information-circle-outline" class="me-1"></ion-icon>Список плейлистов пуст.
|
|
Вернитесь на эту страницу позже — возможно, плейлисты ещё не добавлены или не проверены.
|
|
</div>
|
|
{{end}}
|
|
<div class="row g-4">
|
|
{{range .Playlists}}
|
|
{{$sc := statusClass .}}
|
|
<div class="col-md-6 col-lg-4">
|
|
<div class="card bg-dark text-light h-100 border border-{{$sc}} hover-{{$sc}} position-relative">
|
|
<a href="/{{.Code}}/details" class="text-decoration-none">
|
|
<div class="card-header d-flex align-items-center gap-2">
|
|
<span class="font-monospace text-{{$sc}}">{{.Code}}</span>
|
|
|
|
{{if not .IsKnown}}
|
|
<span class="cursor-help badge bg-{{$sc}} ms-auto"
|
|
title="Плейлист ещё не проверялся, придётся подождать"
|
|
>unknown</span>
|
|
{{else if .IsOnline}}
|
|
<span class="cursor-help badge bg-{{$sc}} ms-auto"
|
|
title="Возможно, этот плейлист рабочий"
|
|
>online</span>
|
|
{{else}}
|
|
<span class="cursor-help badge bg-{{$sc}} ms-auto"
|
|
title="Этот плейлист нерабочий или его не удалось проверить"
|
|
>offline</span>
|
|
{{end}}
|
|
|
|
{{if and .IsKnown .IsOnline}}
|
|
<span class="cursor-help badge border border-success"
|
|
title="Процент рабочих каналов"
|
|
>{{.OnlinePercent}}%</span>
|
|
{{end}}
|
|
|
|
{{if hasTag .Tags "adult"}}
|
|
<span class="cursor-help badge bg-warning text-dark"
|
|
title="Есть каналы для взрослых!"
|
|
>18+</span>
|
|
{{end}}
|
|
|
|
{{if .HasTokens}}
|
|
<span class="cursor-help badge bg-info text-dark"
|
|
title="В плейлисте есть каналы, которые могут быть нестабильны"
|
|
><ion-icon name="paw"></ion-icon></span>
|
|
{{end}}
|
|
</div>
|
|
</a>
|
|
|
|
<div class="card-body position-relative z-2">
|
|
<a href="/{{.Code}}/details" class="text-decoration-none">
|
|
<h5 class="card-title text-light">{{.Name}}</h5>
|
|
</a>
|
|
{{if .Description}}
|
|
<p class="card-text small text-secondary d-none d-md-block">{{.Description}}</p>
|
|
{{end}}
|
|
<div class="d-flex flex-wrap gap-2 mb-1">
|
|
{{if and .IsKnown .IsOnline}}
|
|
<span class="badge border border-secondary">
|
|
<ion-icon name="videocam-outline" class="me-1"></ion-icon> {{channelCount .}}<span class="d-none d-xl-inline-block"> каналов</span>
|
|
</span>
|
|
{{if gt (groupCount .) 0}}
|
|
<span class="badge border border-secondary">
|
|
<ion-icon name="folder-open-outline" class="me-1"></ion-icon> {{groupCount .}}<span class="d-none d-xl-inline-block"> групп</span>
|
|
</span>
|
|
{{end}}
|
|
{{if .HasTvg}}
|
|
<span class="badge border border-secondary">
|
|
<ion-icon name="newspaper-outline" class="me-1"></ion-icon><span class="d-none d-xl-inline-block"> ТВ-программа</span>
|
|
</span>
|
|
{{end}}
|
|
{{if .HasCatchup}}
|
|
<span class="badge border border-secondary">
|
|
<ion-icon name="play-back-outline" class="me-1"></ion-icon><span class="d-none d-xl-inline-block"> Архив</span>
|
|
</span>
|
|
{{end}}
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-footer cursor-pointer"
|
|
onclick="copyPlaylistUrl('{{.Code}}')"
|
|
title="Нажми на ссылку, чтобы скопировать её в буфер обмена"
|
|
>
|
|
<div class="d-flex justify-content-between align-items-center small">
|
|
<span class="font-monospace text-truncate">
|
|
{{baseUrl .Code}}
|
|
</span>
|
|
<ion-icon name="copy-outline"></ion-icon>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
{{if gt .PageCount 1}}
|
|
<nav class="mt-4">
|
|
<ul class="pagination justify-content-center">
|
|
{{range $page := seq 1 .PageCount}}
|
|
{{if eq $page $.PageCurrent}}
|
|
<li class="page-item active" aria-current="page">
|
|
<span class="page-link">{{$page}}</span>
|
|
</li>
|
|
{{else}}
|
|
<li class="page-item">
|
|
<a class="page-link bg-dark text-light" href="/page/{{$page}}">{{$page}}</a>
|
|
</li>
|
|
{{end}}
|
|
{{end}}
|
|
</ul>
|
|
</nav>
|
|
{{end}}
|
|
{{end}}
|