Files
web/views/list.twig

142 lines
7.6 KiB
Twig
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{###########################################################################
# Copyright (c) 2025, Антон Аксенов
# This file is part of iptv.axenov.dev web interface
# MIT License: https://git.axenov.dev/IPTV/web/src/branch/master/LICENSE
###########################################################################}
{% extends "template.twig" %}
{% block metadescription %}Самообновляемые бесплатные IPTV-плейлисты для домашнего просмотра по коротким ссылкам, списки каналов, проверка доступности{% endblock %}
{% block metakeywords %}самообновляемые,бесплатные,iptv-плейлисты,iptv,плейлисты{% endblock %}
{% block 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>
function setDefaultLogo(imgtag) {
imgtag.onerror = null
imgtag.src = '/no-tvg-logo.png'
}
</script>
{% endblock %}
{% block 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 class="d-flex flex-wrap gap-2 mb-2">
<span class="badge bg-success">online: {{ onlineCount }}</span>
<span class="badge bg-danger">offline: {{ offlineCount }}</span>
<span class="badge bg-secondary">unknown: {{ uncheckedCount }}</span>
</div>
</div>
{% endblock %}
{% block content %}
<div class="row g-4">
{% for code, playlist in playlists %}
{% set statusClass = 'secondary' %}
{% if playlist.isOnline is same as(true) %}
{% set statusClass = 'success' %}
{% elseif playlist.isOnline is same as(false) %}
{% set statusClass = 'danger' %}
{% endif %}
<div class="col-md-6 col-lg-4">
<div class="card bg-dark text-light h-100 border border-{{ statusClass }} hover-{{ statusClass }} 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-{{ statusClass }}">{{ code }}</span>
<span class="badge bg-{{ statusClass }} ms-auto">
{% if playlist.isOnline is same as(true) %}online
{% elseif playlist.isOnline is same as(false) %}offline
{% elseif playlist.isOnline is same as(null) %}unknown
{% endif %}
</span>
{% if playlist.isOnline is same as(true) %}
<span class="badge border border-success" title="Процент рабочих каналов">{{ playlist.onlinePercent }}%</span>
{% endif %}
{% if "adult" in playlist.tags %}
<span class="badge bg-warning text-dark" title="Есть каналы для взрослых!">18+</span>
{% endif %}
</div>
</a>
<div class="card-body position-relative z-2">
<a href="/{{ code }}/details" class="text-decoration-none">
<h5 class="card-title text-light">{{ playlist.name }}</h5>
</a>
{% if playlist.description is not same as(null) %}
<p class="card-text small text-secondary d-none d-md-block">{{ playlist.description }}</p>
{% endif %}
<div class="d-flex flex-wrap gap-2 mb-1">
{% if playlist.isOnline is not same as(null) %}
<span class="badge border border-secondary">
<ion-icon name="videocam-outline" class="me-1"></ion-icon>&nbsp;{{ playlist.channels|length }}<span class="d-none d-xl-inline-block">&nbsp;каналов</span>
</span>
{% endif %}
{% if playlist.groups|length > 0 %}
<span class="badge border border-secondary">
<ion-icon name="folder-open-outline" class="me-1"></ion-icon>&nbsp;{{ playlist.groups|length }}<span class="d-none d-xl-inline-block">&nbsp;групп</span>
</span>
{% endif %}
{% if playlist.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>
{% endif %}
{% if playlist.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>
{% endif %}
</div>
</div>
<div class="card-footer cursor-pointer"
onclick="prompt('Скопируй адрес плейлиста. Если не работает, добавь \'.m3u\' в конец.', '{{ base_url(playlist.code) }}')"
title="Нажми чтобы скопировать"
>
<div class="d-flex justify-content-between align-items-center small">
<span class="font-monospace text-truncate">
{{ base_url(playlist.code) }}
</span>
<ion-icon name="copy-outline"></ion-icon>
</div>
</div>
<a href="/{{ code }}/details" class="text-decoration-none">
</a>
</div>
</div>
{% endfor %}
</div>
{% if pageCount > 1 %}
<nav class="mt-4">
<ul class="pagination justify-content-center">
{% for page in range(1, pageCount) %}
{% if 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>
{% endif %}
{% endfor %}
</ul>
</nav>
{% endif %}
{% endblock %}
{% block footer %}
{% endblock %}