Files
iptvc/app/web/views/list.html
T
2026-07-19 19:14:55 +08:00

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>&nbsp;{{channelCount .}}<span class="d-none d-xl-inline-block">&nbsp;каналов</span>
</span>
{{if gt (groupCount .) 0}}
<span class="badge border border-secondary">
<ion-icon name="folder-open-outline" class="me-1"></ion-icon>&nbsp;{{groupCount .}}<span class="d-none d-xl-inline-block">&nbsp;групп</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">&nbsp;ТВ-программа</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">&nbsp;Архив</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}}