Разделение по отдельным репозиториям

This commit is contained in:
2025-03-03 16:31:32 +08:00
commit a1f5154ce8
44 changed files with 4362 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
{# Файл для включения кастомных блоков #}
{# Переименуйте файл в custom.twig и впишите сюда свой html/js #}

133
views/details.twig Normal file
View File

@@ -0,0 +1,133 @@
{% extends "template.twig" %}
{% block title %}[{{ id }}] {{ name }} - {{ config('app.title') }}{% endblock %}
{% block head %}
<style>
img.tvg-logo{max-width:80px;max-height:80px;padding:2px;border-radius:5px}
tr.chrow td{padding:3px}
td.chindex{width:1%}
td.chlogo{width:100px}
div.chlist-table{max-height:550px}
</style>
{% endblock %}
{% block header %}
<h2>О плейлисте: {{ name }}</h2>
{% if (content.encoding.alert) %}
<div class="alert alert-warning small" role="alert">
Кодировка исходного плейлиста отличается от UTF-8.
Он был автоматически с конвертирован из {{ content.encoding.name }}, чтобы отобразить здесь список каналов.
Однако названия каналов могут отображаться некорректно, причём не только здесь, но и в плеере.
</div>
{% endif %}
{% if (status.errCode > 0) %}
<div class="alert alert-danger small" role="alert">
Ошибка плейлиста: [{{ status.errCode }}] {{ status.errText }}
</div>
{% endif %}
{% endblock %}
{% block footer %}
<script src="{{ base_url('js/list.min.js') }}"></script>
<script>
var list = new List('chlist',{valueNames:['chname','chindex']});
</script>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-7">
<table class="table table-dark table-hover small mb-lg-5">
<tbody>
<tr>
<th class="w-25" scope="row">ID</th>
<td class="text-break">
<code>{{ id }}</code>&nbsp;{% if status.possibleStatus == 'online' %}
<span class="badge small text-dark bg-success">online</span>
{% elseif status.possibleStatus == 'offline' %}
<span class="badge small text-dark bg-danger">offline</span>
{% elseif status.possibleStatus == 'timeout' %}
<span class="badge small text-dark bg-warning">timeout</span>
{% elseif status.possibleStatus == 'error' %}
<span class="badge small text-dark bg-danger">error</span>
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Описание</th>
<td class="text-break"><p>{{ desc }}</p></td>
</tr>
<tr>
<th scope="row">Ccылка для ТВ</th>
<td><b onclick="prompt('Скопируй адрес плейлиста', '{{ url }}')"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Нажми на ссылку, чтобы скопировать её в буфер обмена"
class="font-monospace cursor-pointer text-break">{{ url }}</b></td>
</tr>
<tr>
<th scope="row">M3U</th>
<td class="text-break">{{ pls }}</td>
</tr>
<tr>
<th scope="row">Источник</th>
<td class="text-break">{{ src }}</td>
</tr>
</tbody>
</table>
{% if (content.attributes) %}
<h4>Дополнительные атрибуты</h4>
<table class="table table-dark table-hover small">
<tbody>
{% for attribute,value in content.attributes %}
<tr>
<th class="w-25" scope="row">{{ attribute }}</th>
<td class="text-break">{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
<div class="col-lg-5">
<h4>Список каналов ({{ content.channelCount ?? 0 }})</h4>
{% if (content.channelCount > 0) %}
<div id="chlist">
<input type="text"
class="form-control form-control-sm bg-dark text-light mb-2 fuzzy-search"
placeholder="Поиск..."
/>
<div class="chlist-table overflow-auto">
<table class="table table-dark table-hover small">
<tbody class="list">
{% for channel in content.channels %}
<tr class="chrow">
<td class="chindex">{{ loop.index }}</td>
<td class="chlogo text-center">
<img class="tvg-logo"
{% if (channel.logo.base64) %}
src="{{ channel.logo.base64 }}"
{% elseif (channel.attributes['tvg-logo']) %}
src="{{ base_url('logo?url=' ~ channel.attributes['tvg-logo']) }}"
loading="lazy"
{% else %}
src="{{ base_url('no-tvg-logo.png') }}"
{% endif %}
alt="Логотип канала '{{ channel.name }}'"
title="Логотип канала '{{ channel.name }}'"
/>
</td>
<td class="chname text-break">{{ channel.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}

297
views/faq.twig Normal file
View File

@@ -0,0 +1,297 @@
{% extends "template.twig" %}
{% block header %}
<h2>FAQ</h2>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12">
<p>
В этом сервисе собраны ссылки на IPTV-плейлисты, которые находятся в открытом доступе.
Они отбираются вручную и постоянно проверяются здесь автоматически.
</p>
<p>
Сервис "{{ config('app.title') }}" ({{ base_url() }}) не предназначен для хранения или трансляции
видео/аудио потоков, программ телепередач, плейлистов и их поддержки. Этим занимаются администраторы
ресурсов, указанные как источник, и те, с чьих ресурсов ведётся трансляция.
</p>
<p>
За содержимое плейлистов и их качество отвечают авторы плейлистов. На стороне сервиса управляются сами
плейлисты.
</p>
<p class="mb-5">
Сервис "{{ config('app.title') }}" ({{ base_url() }}) предоставляет только информацию об активности
плейлистов, найденных в открытом доступе, и короткие ссылки на них для удобства использования в ПО.
Вопросы по содержанию и работоспособности плейлистов, а также вопросы юридического характера, адресуйте
тем, кто несёт за них ответственность (см. источники плейлистов).
</p>
<div class="accordion" id="faq-accordion">
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header" id="h-purpose">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#purpose" aria-expanded="false" aria-controls="purpose">
Для чего нужен сервис?
</button>
</h2>
<div id="purpose" class="accordion-collapse collapse" aria-labelledby="h-purpose" data-bs-parent="#faq-accordion">
<div class="accordion-body">
<p>Изначально сервис создавался "для себя", чтобы:</p>
<ul>
<li>сократить ссылки на сторонние плейлисты и их было проще вводить с пульта;</li>
<li>собрать в одном месте наиболее годные плейлисты.</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header" id="h-howtouse">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#howtouse" aria-expanded="false" aria-controls="howtouse">
Как пользоваться сервисом?
</button>
</h2>
<div id="howtouse" class="accordion-collapse collapse" aria-labelledby="h-howtouse" data-bs-parent="#faq-accordion">
<p class="accordion-body">
На главной странице отображается список доступных в плейлистов, их идентификаторы, статусы,
количество каналов и короткие ссылки.
Для просмотра списка каналов следует нажать на ссылку <b>"Подробнее..."</b> под интересующим плейлистом.
Для добавления плейлиста в свой медиаплеер удобно использовать <b>"Ссылку для ТВ"</b>.
Это делается для удобства ввода, например, на телевизоре с пульта.
На странице детальной информации также есть прямая ссылка на сам плейлист от источника.
Можно использовать и её.
</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-howtoconnect">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#howtoconnect" aria-expanded="false" aria-controls="howtoconnect">
Как подключить плейлист?
</button>
</h2>
<div id="howtoconnect" class="accordion-collapse collapse" aria-labelledby="h-howtoconnect" data-bs-parent="#faq-accordion">
<p class="accordion-body">
<a href="https://www.google.com/search?q=%D0%BA%D0%B0%D0%BA%20%D0%BF%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D1%8C%20iptv%20%D0%BF%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%20%D0%BF%D0%BE%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B5">
Добавь в свой медиаплеер</a> "Ссылку для ТВ".
</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-isitfree">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#isitfree" aria-expanded="false" aria-controls="isitfree">
Эти плейлисты и каналы в них -- бесплатны?
</button>
</h2>
<div id="isitfree" class="accordion-collapse collapse" aria-labelledby="h-isitfree" data-bs-parent="#faq-accordion">
<p class="accordion-body">
Возможно. По крайней мере, так утверждают источники. Но гарантий никаких никто не даёт.
</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header" id="h-logos">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#logos" aria-expanded="false" aria-controls="logos">
Откуда берутся логотипы каналов и программы передач?
</button>
</h2>
<div id="logos" class="accordion-collapse collapse" aria-labelledby="h-logos" data-bs-parent="#faq-accordion">
<p class="accordion-body">
Всё это (не) указывается внутри плейлиста его авторами.
Но в некоторых плеерах можно вручную указывать программу передач (см. ниже).
</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-which">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#which" aria-expanded="false" aria-controls="which">
Какие плейлисты попадают сюда?
</button>
</h2>
<div id="which" class="accordion-collapse collapse" aria-labelledby="h-which" data-bs-parent="#faq-accordion">
<div class="accordion-body">
<p>Есть некоторые критерии, по которым плейлисты отбираются в этот список:</p>
<ul>
<li>Прежде всего -- каналы РФ и бывшего СНГ, но не только</li>
<li>Открытый источник</li>
<li>Прямая ссылка на плейлист</li>
<li>Автообновление плейлиста</li>
</ul>
<p>
В основном, в плейлистах именно трансляции телеканалов, но могут быть просто список каких-то
(мульт)фильмов и передач, находящихся на чужих дисках (как если бы вы сами составили плейлист с музыкой,
например).
</p>
</div>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-statuses">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#statuses" aria-expanded="false" aria-controls="statuses">
Что означают статусы плейлистов?
</button>
</h2>
<div id="statuses" class="accordion-collapse collapse" aria-labelledby="h-statuses" data-bs-parent="#faq-accordion">
<div class="accordion-body">
<ul>
<li>
<span class="badge small text-dark bg-secondary">loading</span>
Загрузка данных, нужно немного подождать.
</li>
<li>
<span class="badge small text-dark bg-success">online</span>
Плейлист, возможно, активен. <i>Если каналов 0, значит, вероятно, источник поставил
редирект с плейлиста на куда ему вздумалось. То есть плейлист, наверное, отсутствует
и, возможно, больше никогда не появится по текущему адресу.</i>
</li>
<li>
<span class="badge small text-dark bg-warning">timeout</span>
Не удалось вовремя проверить плейлист, сервер с плейлистом слишком долго запрягает.
</li>
<li>
<span class="badge small text-dark bg-danger">offline</span>
Плейлист недоступен, вообще.
</li>
<li>
<span class="badge small text-dark bg-danger">error</span>
Ошибка при проверке плейлиста. Пора удалять плейлист отсюда.
</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-donttrust">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#donttrust" aria-expanded="false" aria-controls="donttrust">
Почему нельзя доверять результатам проверки?
</button>
</h2>
<div id="donttrust" class="accordion-collapse collapse" aria-labelledby="h-donttrust" data-bs-parent="#faq-accordion">
<div class="accordion-body">
<p>
Я не гарантирую корректность и актуальность информации, которую ты увидишь здесь.
Хотя я и стараюсь улучшать качество проверок, но всё же рекомендую проверять желаемые
плейлисты самостоятельно вручную, ибо нет никаких гарантий:
</p>
<ul>
<li>что это вообще плейлисты, а не чьи-то архивы с мокрыми кисками;</li>
<li>что плейлисты по разным ссылкам не дублируют друг друга и отличаются каналами хотя бы на четверть;</li>
<li>что плейлист работоспособен (каналы работают, корректно названы, имеют аудио, etc.);</li>
<li>что подгрузится корректное количество каналов и их список (хотя на это я ещё могу влиять и стараюсь как-то улучшить).</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-guarantee">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#guarantee" aria-expanded="false" aria-controls="guarantee">
Какова гарантия, что я добавлю себе плейлист отсюда и он работать хоть сколько-нибудь долго?
</button>
</h2>
<div id="guarantee" class="accordion-collapse collapse" aria-labelledby="h-guarantee" data-bs-parent="#faq-accordion">
<p class="accordion-body">
Никакова.
Мёртвые плейлисты я периодически вычищаю, реже -- добавляю новые.
ID плейлистов могут меняться, поэтому вполне может произойти внезапная подмена одного другим, однако
намеренно я так не делаю.
Если один плейлист переезжает на новый адрес, то я ставлю временное перенаправление со старого ID на
новый.
Плюс читай выше про доверие результатам проверки (проблема может быть не стороне сервиса).
</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header" id="h-panic">
<button class="accordion-button text-warning bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#panic" aria-expanded="false" aria-controls="panic">
У меня перестал работать/исчез любимый канал/плейлист! Нет лого канала/программы передач!
</button>
</h2>
<div id="panic" class="accordion-collapse collapse" aria-labelledby="h-panic" data-bs-parent="#faq-accordion">
<p class="accordion-body">Ну штош ¯\_(ツ)_/¯</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-epg">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#epg" aria-expanded="false" aria-controls="epg">
Где взять программу передач (EPG)?
</button>
</h2>
<div id="epg" class="accordion-collapse collapse" aria-labelledby="h-epg" data-bs-parent="#faq-accordion">
<div class="accordion-body">
<ul>
<li><b>https://iptvx.one/viewtopic.php?f=12&t=4</b></li>
<li>https://iptvmaster.ru/epg-for-iptv</li>
<li>https://google.com</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-howoftenlist">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#howoftenlist" aria-expanded="false" aria-controls="howoftenlist">
Как часто обновляется список плейлистов?
</button>
</h2>
<div id="howoftenlist" class="accordion-collapse collapse" aria-labelledby="h-howoftenlist" data-bs-parent="#faq-accordion">
<p class="accordion-body">
Время от времени.
Иногда я захожу сюда и проверяю всё ли на месте, иногда занимаюсь какими-то доработками.
Если есть кандидаты на добавление, то читай ниже.
</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-howoftench">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#howoftench" aria-expanded="false" aria-controls="howoftench">
Как часто обновляется содержимое плейлистов?
</button>
</h2>
<div id="howoftench" class="accordion-collapse collapse" aria-labelledby="h-howoftench" data-bs-parent="#faq-accordion">
<p class="accordion-body">
Зависит от источника. Я этим не занимаюсь.
</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-api">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#api" aria-expanded="false" aria-controls="api">
Есть ли API? Как им пользоваться?
</button>
</h2>
<div id="api" class="accordion-collapse collapse" aria-labelledby="h-api" data-bs-parent="#faq-accordion">
<p class="accordion-body">
Есть, подробности <a href="https://github.com/anthonyaxenov/iptv2#api">здесь</a>.
</p>
</div>
</div>
<div class="accordion-item bg-dark text-light">
<h2 class="accordion-header bg-dark" id="h-howtoadd">
<button class="accordion-button text-light bg-dark" type="button" data-bs-toggle="collapse" data-bs-target="#howtoadd" aria-expanded="false" aria-controls="howtoadd">
Как пополнить этот список?
</button>
</h2>
<div id="howtoadd" class="accordion-collapse collapse" aria-labelledby="h-howtoadd" data-bs-parent="#faq-accordion">
<p class="accordion-body">
Сделать pull-request в <a href="https://github.com/anthonyaxenov/iptv">репозиторий</a>.
Я проверю плейлист и добавлю его в общий список, если всё ок.
</p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

66
views/list.twig Normal file
View File

@@ -0,0 +1,66 @@
{% extends "template.twig" %}
{% block header %}
<p class="text-muted small">
Обновлено:&nbsp;{{ updated_at }}&nbsp;МСК<br/>
Плейлистов в списке:&nbsp;<strong>{{ count }}</strong>
</p>
<hr/>
{% endblock %}
{% block content %}
<div class="table-responsive">
<table class="table table-responsive table-dark table-hover small">
<thead>
<tr>
<th class="text-center">ID</th>
<th>Информация о плейлисте</th>
<th class="d-none d-sm-table-cell">Ссылка для ТВ</th>
</tr>
</thead>
<tbody>
{% for id, playlist in playlists %}
<tr class="pls" data-playlist-id="{{ id }}">
<td class="text-center font-monospace id">{{ id }}</td>
<td class="info">
<a href="{{ base_url(id ~ '/details') }}" class="text-light fw-bold text-decoration-none">{{ playlist.name }}</a>
<div class="small mt-2">
{% if playlist.desc|length > 0 %}
<p class="my-1 d-none d-lg-block">{{ playlist.desc }}</p>
{% endif %}
<a href="{{ base_url(id ~ '/details') }}" class="text-light">Подробнее...</a>
</div>
</td>
<td class="col-3 d-none d-sm-table-cell">
<span onclick="prompt('Скопируй адрес плейлиста', '{{ playlist.url }}')"
title="Нажми на ссылку, чтобы скопировать её в буфер обмена"
class="font-monospace cursor-pointer">
{{ playlist.url }}
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if pageCount > 0 %}
<div aria-label="pages">
<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 border-secondary text-light" href="{{ base_url('page/' ~ page) }}">{{ page }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
</div>
{% endblock %}
{% block footer %}
{% endblock %}

18
views/notfound.twig Normal file
View File

@@ -0,0 +1,18 @@
{% extends "template.twig" %}
{% block header %}
<h2>Плейлист не найден</h2>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-12">
<p>
Плейлист {{ id }} не найден
</p>
<a class="btn btn-outline-light" href="{{ base_url() }}" title="На главную">
Перейти к списку
</a>
</div>
</div>
{% endblock %}

81
views/template.twig Normal file
View File

@@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<title>{% block title %}{{ config('app.title') }}{% endblock %}</title>
<meta charset="utf-8">
<meta name="description" content="Самообновляемые бесплатные IPTV-плейлисты для домашнего просмотра по коротким ссылкам, списки каналов, проверка доступности">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<style>.cursor-pointer{cursor:pointer}</style>
<link href="{{ base_url('css/bootstrap.min.css') }}" rel="stylesheet">
<link rel="apple-touch-icon" sizes="180x180" href="{{ base_url('/favicon/apple-touch-icon.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ base_url('/favicon/favicon-32x32.png') }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ base_url('/favicon/favicon-16x16.png') }}">
<link rel="manifest" href="{{ base_url('/favicon/site.webmanifest') }}">
<link rel="mask-icon" href="{{ base_url('/favicon/safari-pinned-tab.svg') }}" color="#5bbad5">
<meta name="msapplication-TileColor" content="#00aba9">
<meta name="msapplication-TileImage" content="{{ base_url('/favicon/mstile-144x144.png') }}">
<meta name="theme-color" content="#212529">
<style>.boosty{vertical-align:baseline;float:left;display:inline;width:20px}</style>
{% block head %}{% endblock %}
</head>
<body class="bg-dark text-light">
<div class="container col-lg-8 mx-auto">
<header>
<nav class="navbar navbar-expand-lg navbar-dark">
<img src="{{ base_url('/favicon/favicon-32x32.png') }}" class="d-inline-block align-text-top px-lg-1" alt=""/>
<a class="navbar-brand" href="{{ base_url() }}" title="На главную">
{{ config('app.title') }}
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ base_url() }}">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ base_url('faq') }}">FAQ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://git.axenov.dev/anthony/iptv">Gitea</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/anthonyaxenov/iptv">GitHub</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://axenov.dev">axenov.dev</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://t.me/iptv_aggregator">Telegram</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://boosty.to/anthonyaxenov">
<img class="boosty" src="{{ base_url('boosty.svg') }}" alt="Boosty">
Boosty
</a>
</li>
</ul>
</div>
</nav>
</header>
<section class="container h-100 pt-lg-3 px-0 pb-0">
{% block header %}{% endblock %}
{% block content %}{% endblock %}
</section>
<footer class="py-4 text-center">
<script src="{{ base_url('js/bootstrap.bundle.min.js') }}"></script>
{% block footer %}{% endblock %}
<a href="{{ base_url('faq') }}">FAQ</a>&nbsp;|&nbsp;<a
href="https://github.com/anthonyaxenov/iptv">GitHub</a>&nbsp;|&nbsp;<a
href="https://git.axenov.dev/anthony/iptv">Gitea</a>&nbsp;|&nbsp;<a
href="https://axenov.dev">axenov.dev</a>&nbsp;|&nbsp;<a
href="https://t.me/iptv_aggregator">Telegram</a><br>
</footer>
</div>
{% include("custom.twig") ignore missing %}
</body>
</html>