Compare commits

...

35 Commits

Author SHA1 Message Date
anthony 0e80f6076e Актуализация страниц о деплое и разработке + мелочи по оформлению 2026-07-23 20:10:36 +08:00
anthony 9d817f65d8 Актуализация сведений об иконках, конфигу и пр. мелочи по оформлению 2026-07-22 11:07:38 +08:00
anthony f85c61d273 Исправления сборки и запуска документации 2026-07-22 11:06:02 +08:00
anthony 890674dea0 Актуализирован README 2026-07-22 01:15:45 +08:00
anthony 8d0f3ebcce Миграция на zensical, актуализация под iptvc после рефакторинга 2026-07-22 01:11:07 +08:00
anthony 7d61aadc5d Актуализация и мелочи
Build images / build (push) Failing after 13m56s
2026-01-02 23:46:04 +08:00
anthony c1af326438 Мелочь про актуальность
Build images / build (push) Successful in 1m56s
2025-12-26 23:24:00 +08:00
anthony ba6b948d24 Корректировки о статусной странице
Build images / build (push) Successful in 3m11s
2025-11-30 18:40:12 +08:00
anthony db7ac03265 Описание статусной страницы
Build images / build (push) Successful in 1m33s
2025-11-30 10:57:07 +08:00
anthony fd11792a24 Обновлены скриншоты о странице плейлиста + мелочи 2025-11-30 10:35:10 +08:00
anthony dcf1fc909c Фикс сломанных ссылок
Build images / build (push) Successful in 1m42s
2025-11-30 00:20:29 +08:00
anthony dc05ceb780 Мелочи по правообладателям 2025-11-30 00:18:26 +08:00
anthony 7cbb906258 Мелочи по FAQ 2025-11-30 00:18:08 +08:00
anthony e62e555a3f Уточнения о статусах плейлистов и каналов 2025-11-29 23:59:50 +08:00
anthony 7b556a48af Скорректирована страница о ТГ-чате 2025-11-29 23:38:51 +08:00
anthony 710759c22d Скорректирована страница поддержки 2025-11-29 23:38:24 +08:00
anthony d67059d1ec Обновлена страница про статусы плейлистов 2025-11-29 22:45:45 +08:00
anthony ba5efb5be8 Обновлена страница про отбор плейлистов 2025-11-29 22:44:59 +08:00
anthony 0d60dee8d0 Обновлены скриншоты главной страницы 2025-11-29 22:41:50 +08:00
anthony 0fee02a486 Добавлена страница для правообладателей 2025-11-23 21:53:05 +08:00
anthony 4976b9c85a Описаны новые аргументы iptvc check --repeat/--every
Build images / build (push) Successful in 1m37s
2025-11-23 01:36:58 +08:00
anthony 6d460f4263 Пайплайн сборки контейнера с готовой документацией
Build images / build (push) Successful in 2m2s
2025-11-22 22:34:15 +08:00
anthony 1d24b3fab5 Мелкие уточнения 2025-11-22 22:33:06 +08:00
anthony 88586d15a2 Мелкие доработки Makefile 2025-11-22 22:32:37 +08:00
anthony 49b85a6bee Упаковка в docker 2025-11-22 17:18:34 +08:00
anthony 0e626ac48b Исправление замечаний билдера 2025-11-22 16:55:01 +08:00
anthony 557e2ba5a0 Удалены и заигнорированы сгенерированные файлы сайта 2025-11-22 16:24:51 +08:00
anthony fa2ef2ca84 Merge branch 'master' of git.axenov.dev:IPTV/docs 2025-11-22 16:22:16 +08:00
anthony 2566b35d41 Уточнения по командам и аргументам iptvc 2025-11-22 16:17:25 +08:00
anthony 0eefda4294 Орфограция и синтаксис 2025-11-22 16:15:55 +08:00
anthony af8361bd5c Обновлён FAQ, добавлены новые фото заглушек 2025-11-22 16:07:08 +08:00
anthony 2b211b3cda Фиксация версии squidfunk/mkdocs-material:9.6.20
Исправляет live-reloading. Подробности: https://github.com/squidfunk/mkdocs-material/issues/8478
2025-11-22 14:43:05 +08:00
anthony 782a787f1c Исправление ссылок на сайт в конфиге 2025-11-22 14:41:51 +08:00
anthony 7b99270938 Мелочи по переменным окружения iptvc 2025-10-05 12:03:16 +08:00
anthony 27b77f2590 Удалён старый адрес 2025-09-21 23:55:57 +08:00
828 changed files with 6864 additions and 8423 deletions
+409
View File
@@ -0,0 +1,409 @@
---
name: koda-zensical
description: "Навык, который необходим для работы в этом репозитории и должен закружаться безусловно. Для этого проекта обязательно применяй эти инструкции, поскольку они позволят правильно писать и форматировать исходные файлы документации."
---
# Документирование Zensical
## Принципы написания документации
### Язык и стиль
- Пиши простым и понятным языком
- Избегай просторечий и сложных технических терминов без объяснения
- Используй активный залог
- Обращайся к пользователю на "вы"
- Поддерживай единый стиль во всех документах
- Не злоупотребляй emoji
### Примеры
- Приводи примеры конфигурации
- Показывай скриншоты для важных шагов
- Добавляй таблицы для сравнения опций
### Исправление и избегание ошибок
- Синтаксические и грамматические ошибки должны исправляться в соответствии с правиламии и нормами естественного языка
- При изменении структуры документации:
- в новый документ следует добавить ссылки на другие релевантные документы или якоря
- ссылки на перемещённый или удалённый документ/якорь следует обновить в каждом существующем документе
- следует проверять вывод команды `make site` на наличие ошибок и предупреждений компилятора
## Синтаксис файлов
В основе документации лежит расширенный markdown.
Ниже описаны правила оформления и синтаксиса, которые отличаются от стандартного markdown и github-flavoured markdown.
Расширения синтаксиса предоставляются связкой:
- `zensical` (документация: <https://zensical.org/docs/>)
- `pymdown-extensions` (документация: <https://facelessuser.github.io/pymdown-extensions>)
Ниже только необходимые и достаточные правила для:
- качественной документации;
- хорошего человеческого восприятия;
- корректного формирования документации без ошибок.
Применение этих подходов необязательно, но требования к каждому требуется соблюдать строго.
Если приведены ссылки на документацию, можешь использовать их для получения актуальной информации.
Там так же могут быть описаные дополнительные приёмы для работы с документами.
### Frontmatter
Каждый документ должен начинаться с этого блока метаданных.
После frontmatter должна быть пустая строка.
Внутри должен быть валидный yaml.
Часто используются следующие опциональные параметры (* — желательны):
- *`title` — укорочечнное название документа для отображения в навигации (умолчание — заголовок 1 уровня)
- *`description` — небольшое осмысленное описание документа (умолчание — пусто)
- *`icon` — код иконки для отображения в навигации рядом с названием (умолчание — пусто)
- *`tags` — массив ключевых слов (тегов), описывающих документ (умолчание — пусто)
- `hide` — массив кодов элементов, которые нужно скрыть на странице документа:
- `navigation` — главная навигация (слева)
- `toc` — содержание страницы (справа)
- `path` — хлебные крошки (сверху)
- `status` — статус страницы (добавляет к пункту навигации слева иконку с подсказкой)
- `new` — новая информация
- `deprecated` — устаревшая информация
- `beta` — информация о нестабильном функционале
Параметр `title` не должен быть равен заголовку первого уровня.
В таком случае `title` следует убрать или не добавлять.
### Заголовки
На странице должен быть только один заголовок 1 уровня — сразу после Frontmatter.
До и после каждого заголовка должна быть 1 пустая строка.
В конце строки заголовка должно быть объявление в формате:
`{ id="header-slug" }`
Так будет проще связывать секции разных страниц между собой.
### Абзацы
До и после каждого абзаца должна быть 1 пустая строка.
Каждое предложение внутри абзаца должно быть на новой строке.
### Списки
Вложенные уровни отступаются на 4 пробела слева.
До и после каждого списка должна быть 1 пустая строка.
Ненумерованные списки начинаются с `-`.
### Многострочные блоки кода
> Требует дополнительной настройки.
> Обратись к документации: <https://zensical.org/docs/authoring/code-blocks/#code-blocks>
До и после каждого блока кода должна быть 1 пустая строка.
Каждый блок кода в заголовке может иметь атрибуты:
- `title="..."` — заголовок блока (например, название файла)
- `hl_lines="..."` — подсветка срок: номера через пробел и/или диапазоны через `-`
- `linenums="N"` — включить нумерацию строк, отсчитывая с указанного числа `N`
В конце строк внутри блока может быть любое число в формате `#(X)!` — это кликальбельные аннотации, содержимое которых будет взято из ближайшего нумерованного списка.
Полный пример:
```yaml title="config.yaml" hl_lines="6 8-10 13 17-20" linenums="1"
context:
- provider: code
# - provider: docs # сломан
- provider: diff
- provider: terminal
- provider: problems
- provider: folder
- provider: codebase
params:
nFinal: 10
# - provider: file
# - provider: url
# - provider: search
```
### Врезки
Позволяют акцентировать внимание на ключевых моментах, выделяя блок цветом и иконкой.
Документация: <https://raw.githubusercontent.com/zensical/docs/master/docs/authoring/admonitions.md>
Синтаксис:
```
!!! <тип> "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
??? <тип> "Заголовок разворачиваемой врезки"
Содержимое, которое может быть многострочным
и свёрнуто по умолчанию, но разворачивается по клику на заголовке
???+ <тип> "Заголовок сворачиваемой врезки"
Содержимое, которое может быть многострочным
и развёрнуто по умолчанию, но сворачивается по клику на заголовке
```
Типы, их цвета и пиктограммы:
| Тип | Цвет | Пиктограмма |
| ---------- | ------- | ------------------ |
| `note` | #448aff | карандаш в круге |
| `abstract` | #00b0ff | планшет для бумаги |
| `info` | #00b8d4 | `i` в круге |
| `tip` | #00bfa5 | пламя |
| `success` | #00c853 | галочка |
| `question` | #64dd17 | `?` в круге |
| `warning` | #ff9100 | `!` в треугольнике |
| `failure` | #ff5252 | крестик |
| `danger` | #ff1744 | молния в круге |
| `bug` | #f50057 | жук на щите |
| `example` | #7c4dff | пробирка |
| `quote` | #9e9e9e | двойная кавычка |
Заголовок может быть пустым, в этом случае:
- после типа указываются пустые двойные кавычки (иначе подставится название типа с заглавной буквы на английском языке)
- содержимое внутри блока обрамлён цветом своего типа
Если текста внутри врезки нет, отображается только яркий заголовок с иконкой.
Содержимое врезки отступается минимум на 4 пробела.
Содержимое без отступа (в начале строки) находится вне врезки.
Для содержимого врезки распространяются все те же markdown-правила, включая указанные в этом документе.
До и после каждой врезки должна быть 1 пустая строка.
### Сниппеты
> Требует дополнительной настройки.
> Обратись к документации:
> - <https://zensical.org/docs/setup/extensions/python-markdown-extensions/?h=snippets#snippets>
> - <https://facelessuser.github.io/pymdown-extensions/extensions/snippets/#snippets-notation>
> - <https://raw.githubusercontent.com/facelessuser/pymdown-extensions/refs/heads/main/pymdownx/snippets.py>
Это переиспользуемые блоки markdown/html, хранящиеся в файлах.
Как использовать:
1. создать файл в директории проекта согласно конфигурации, например, `snippets/filename.md`
2. наполнить содержимым
3. во всех местах документации вставить:
- пустая строка
- `--8<-- "filename.md"`
- пустая строка
4. если файлов несколько, вставить следующим образом:
- пустая строка
- `--8<--`
- `"filename1.md"`
- пустая строка
- `"filename2.md"`
- `--8<--`
- пустая строка
### Иконки
Каждая иконка определяется своим идентификатором, который делится на две части: код набора и код иконки.
Внутри frontmatter (параметр `icon`) используется формат: `набор/иконка`
В тексте документа используется формат: `:набор-иконка:`
Если иконка в начале строки, пробел ставится только после неё.
Если иконка в середине строки, пробелы ставятся до и после неё.
Если иконка в конце строки, пробел ставятся только до неё.
Доступны 4 встроенных набора иконок:
| Название | Код набора | Ссылка | Путь в проекте |
| --------------- | ------------- | ---------------------------------------------- | --------------------------- |
| Lucide | `lucide` | <https://lucide.dev/icons/> | - |
| Material Design | `material` | <https://pictogrammers.com/library/mdi/> | - |
| FontAwesome | `fontawesome` | <https://fontawesome.com/search> | - |
| Octicons | `octicons` | <https://primer.style/octicons/> | - |
| Simple Icons | `simple` | <https://simpleicons.org/> | - |
| VSCode Codicons | `vscode` | <https://github.com/microsoft/vscode-codicons> | `./overrides/.icons/vscode` |
Полный список названий иконок здесь: <https://squidfunk.github.io/mkdocs-material/assets/javascripts/iconsearch_index.json>
В проекте могут использоваться собственные наборы иконок.
В конфиге проекта есть параметр `custom_dir` - там указана директория с наборами.
Внутри этой директории может быть следующая иерархия:
```
<custom_dir>/
.icons/
<код_набора1>/
<код_иконки1>.svg
<код_иконки2>.svg
...
<код_набора2>/
<код_иконки3>.svg
<код_иконки4>.svg
...
```
Получить полный список иконок в этих наборах можно прочитав содержимое указанных директорий в проекте.
### Гриды (карточки)
Грид позволяет разместить короткие предложения в формате динамических карточек.
Он выглядит как markdown-список, обрамлённый в `<div>`.
До открывающего и после закрывающего тегов должна быть 1 пустая строка.
Пример простого грида с компактными карточками:
```
<div class="grid cards" markdown>
- :fontawesome-brands-html5: Карточка №1
- :fontawesome-brands-js: Карточка №2
- :fontawesome-brands-css3: Карточка №3
- :fontawesome-brands-internet-explorer: Карточка №4
</div>
```
Пример грида с многострочными карточками:
```
<div class="grid cards" markdown>
- :fontawesome-brands-html5: **Заголовок карточки №1**
---
Многострочное содержимое карточки №1
- :fontawesome-brands-js: **Заголовок карточки №2**
---
Многострочное содержимое карточки №2
- :fontawesome-brands-css3: **Заголовок карточки №3**
---
Многострочное содержимое карточки №3
- :fontawesome-brands-internet-explorer: **Заголовок карточки №4**
---
Многострочное содержимое карточки №4
</div>
```
### Вкладки (табы)
Позволяют уместить информацию на одном уровне, не растягивая страницу по высоте.
Синтаксис:
```
=== "Заголовок вкладки 1"
Содержимое вкладки 1
=== "Заголовок вкладки 2"
Содержимое вкладки 2
```
Содержимое вкладки отступается минимум на 4 пробела.
Содержимое без отступа (в начале строки) находится вне вкладки.
Для содержимого вкладки распространяются все те же markdown-правила, включая указанные в этом документе.
До и после каждого заголовка вкладки должна быть 1 пустая строка.
После содержимого последней вкладки должна быть 1 пустая строка.
### Сноски
Сноска позволяет добавить надстрочный индекс к слову, чтобы вынести пояснения в конец страницы, быстро переместиться к нему по клику на индекс и вернуться обратно.
Синтаксис:
```
Lorem[^1] ipsum[^2] dolor sit amet, consectetur adipiscing elit.
[^1]: однострочная сноска
[^2]:
многострочная сноска
с отступом 4 пробела слева
на каждой строке
```
### Подсказки (тултипы) и аббревиатуры
Они появляются при наведении мыши на какой-либо элемент на странице документа.
Пример 1: иконка с подсказкой:
`:material-information-outline:{ title="текст подсказки" }`
Пример 2: ссылка с подсказкой:
`[Hover me](https://example.com "I'm a tooltip!")`
Пример 3: альтернативная ссылка с подсказкой:
```
[Hover me][example]
[example]: https://example.com "I'm a tooltip!"
```
### Горячие клавиши
В общем случае, для указания корячих клавиш следует использовать тег `<kbd>`.
Примеры: `<kbd>B</kbd>`, `<kbd>Esc</kbd>`
Для описания комбинаций клавиш следует вставлять между каждой клавишей знак `+`, обрамлённый пробелами.
Примеры: `<kbd>Shift</kbd> + <kbd>A</kbd>`, `<kbd>Ctrl</kbd> + <kbd>K</kbd> + <kbd>4</kbd>`
Для MacOS-специфичных тем вставлять `+` не нужно.
Примеры: `<kbd>⌘</kbd><kbd>C</kbd>`, `<kbd>⇧</kbd><kbd>⌘</kbd><kbd>P</kbd>`
Сопоставление пиктограмм с названиями клавиш (служебных и модификаторов) MacOS:
- Базовые модификаторы:
- `` - `Command` (`Cmd`)
- `` - `Option` (`Alt`)
- `` - `Control` (`Ctrl`)
- `` - `Shift`
- `` - `Caps Lock`
- Навигация и управление:
- `` - `Delete` (Backspace, удаление символа слева)
- `` - `Forward Delete` (удаление символа справа, `Fn` + D`elete)
- `⏎` - `Return` (`Enter`)
- `⌕` - `Enter` на цифровой клавиатуре (в некоторых шрифтах)
- `⎋` - `Escape` (`Esc`)
- `⇥` - `Tab` (Табуляция)
- `⇤` - `Backtab` (`Shift` + `Tab`)
- `␣` - `Space` (Пробел)
- Перемещение по тексту:
- `↖` - `Home` (Начало документа, `Fn` + `←`)
- `↘` - `End` (Конец документа, `Fn` + `→`)
- `⇞` - `Page Up` (Страница вверх, `Fn` + `↑`)
- `⇟` - `Page Down` (Страница вниз, `Fn` + `↓`)
- Специальные и системные:
- `🌐` / `fn` — Функция (`Fn` / Кнопка смены языка/вызова эмодзи)
- `⏏``Eject` (Извлечение диска)
### Кнопки
- `[Серая кнопка](https://example.com/){ .md-button }`
- `[Синяя кнопка](https://example.com/){ .md-button .md-button--primary }`
- `[:fontawesome-solid-paper-plane: Кнопка серая с иконкой](https://example.com/){ .md-button }`
- `[:fontawesome-solid-paper-plane: Кнопка синяя с иконкой](https://example.com/){ .md-button .md-button--primary }`
+6
View File
@@ -0,0 +1,6 @@
/.git
/.gitea
/.gitignore
/.cache
Makefile
.DS_Store
+18
View File
@@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yaml,yml}]
indent_size = 2
[Makefile]
indent_style = tab
+44
View File
@@ -0,0 +1,44 @@
# https://docs.gitea.com/usage/actions/overview
# https://docs.github.com/ru/actions/reference/workflows-and-actions/contexts
name: Build images
on:
push:
branches:
- 'master'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with: # https://github.com/actions/checkout
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with: # https://github.com/docker/setup-buildx-action
buildkitd-config-inline: |
# https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md
[ registry."docker.io" ]
mirrors = ["https://dockerhub.timeweb.cloud", "https://dh-mirror.gitverse.ru"]
http = true
insecure = true
- name: Log in to Gitea Container Registry
uses: docker/login-action@v3
with: # https://github.com/docker/login-action
registry: git.axenov.dev
username: ${{ secrets.USERNAME }}
password: ${{ secrets.RELEASE_TOKEN }}
- name: Build and push Docker images
uses: docker/build-push-action@v5
with: # https://github.com/docker/build-push-action
context: .
push: true
tags: |
git.axenov.dev/iptv/m3u-su-docs:${{ github.ref_name }}
git.axenov.dev/iptv/m3u-su-docs:latest
+4 -1
View File
@@ -1 +1,4 @@
/.cache /.cache/
/site/
/__pycache__/
.DS_Store
+25
View File
@@ -0,0 +1,25 @@
- Всегда используй навык koda-zensical
- Всегда перечитывай файлы перед каждым изменением - пользователь уже мог внести изменения
- При изменениях структуры проекта всегда актуализируй AGENTS.md
- |
В структуре навигации файла `zensical.toml` всегда должен присутствовать закомментированный раздел `Debug`.
Это отладочные страницы с примерами, которые не должны попасть в итоговую документацию.
Этот раздел раскомментируется вручную по необходимости.
- |
Для отладки используй команду `make site`.
В выводе команды могу быть предупреждения и ошибки в следующем формате:
```
Warning: page does not exist
╭─[ test.md:695:10 ]
695 │ - [Edit](../edit/how-to-use-it.md) — редактирование кода
│ ────────────┬───────────
│ ╰───────────── page does not exist
─────╯
```
что означает буквально следующее: "в файле content/test.md в строке 695 в позиции 10 есть ссылка на несуществующую страницу."
Эти предупреждения надо всегда исправлять.
- При изменениях структуры проекта и только при корректной сборке (без предупреждений и ошибок) всегда актуализируй `./AGENTS.md`
- При доработке или расширении функционала этого проекта или синтаксиса всегда актуализируй скилл `./.agents/skills/koda-zensical`
+12
View File
@@ -0,0 +1,12 @@
{
"recommendations": [
"editorconfig.editorconfig",
"mhutchie.git-graph",
"golang.go",
"koda.koda",
"0x10.mkdocs-material-preview",
"redhat.vscode-yaml",
"takumii.markdowntable",
"tamasfe.even-better-toml"
]
}
+5
View File
@@ -0,0 +1,5 @@
{
"files.associations": {
"*.md": "python-markdown"
}
}
+22
View File
@@ -0,0 +1,22 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Живой предпросмотр (localhost:8000)",
"type": "shell",
"command": "make live"
},
{
"label": "Собрать статический сайт (./site)",
"type": "shell",
"command": "make site"
},
{
"label": "Собрать docker-образ со стат. сайтом",
"type": "shell",
"command": "make image"
}
]
}
+21
View File
@@ -0,0 +1,21 @@
FROM zensical/zensical:latest AS builder
ENV PYTHONPATH=/docs
COPY . /docs
RUN zensical build --clean
FROM nginx:alpine AS iptv-docs
LABEL org.opencontainers.image.title="IPTV Checker"
LABEL org.opencontainers.image.description="Documentation for our services and products"
LABEL org.opencontainers.image.authors="Anthony Axenov <anthonyaxenov@gmail.com>"
LABEL org.opencontainers.image.url="https://m3u.su"
LABEL org.opencontainers.image.vendor="Anthony Axenov"
LABEL org.opencontainers.image.documentation="https://m3u.su/docs"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.source="https://git.axenov.dev/IPTV/docs"
COPY --from=builder /docs/site /usr/share/nginx/html
WORKDIR /usr/share/nginx/html
USER root
EXPOSE 80
CMD [ "nginx", "-g", "daemon off;" ]
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2025 Антон Аксенов (Anthony Axenov) Copyright (c) 2025-2026 Антон Аксенов (Anthony Axenov)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+56 -6
View File
@@ -1,8 +1,58 @@
live: .DEFAULT_GOAL := help
@echo "Wait until container starts and open http://localhost:3000 to see live preview" .PHONY: clear live site image push run help
@docker run --rm -it -p 3000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material
build: ## clear = Remove cache directories
@docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material build -v clear:
@echo "Open http://localhost:8080/docs to see compiled documentation" rm -rf __pycache__ .cache site
## live = Run zensical with live-reloading on http://localhost:8801
live: clear
@echo "*** Wait until container starts and open http://localhost:8801 to see live preview"
@docker stop iptv-docs-dev 2>/dev/null; \
docker run \
--rm \
--pull always \
--env PYTHONPATH=/docs \
--publish 8801:8000 \
--volume ${PWD}:/docs \
--name iptv-docs-dev \
zensical/zensical:latest
## site = Build a local static site
site: clear
@echo "*** Wait until zensical finish"
@docker stop iptv-docs-dev 2>/dev/null; \
docker run \
--rm \
--pull always \
--env PYTHONPATH=/docs \
--volume ${PWD}:/docs \
--name iptv-docs-dev \
zensical/zensical:latest \
build \
--clean \
--strict
## image = Build a docker image
image:
@docker build --tag git.axenov.dev/iptv/iptv-docs:latest .
## run = Run docker container from image built with `make image` on http://localhost:8802
run:
@echo "*** Wait until container starts and open http://localhost:8802 to see ready static website"
@docker stop iptv-docs 2>/dev/null; \
docker run \
--rm \
--publish 8802:80 \
--name iptv-docs \
git.axenov.dev/iptv/iptv-docs:latest
## push = Push docker image built with `make image` to git.axenov.dev
push:
@docker login git.axenov.dev; \
docker push git.axenov.dev/iptv/iptv-docs:latest
## help = Show this message and exit (default)
help: Makefile
@echo "Available recipes:"
@sed -n 's/^##/ /p' $< | column -t -s '='
+27 -3
View File
@@ -1,8 +1,32 @@
# Документация iptv.axenov.dev # Документация m3u.su
Как работать с документацией: [src/dev/docs.md](src/dev/docs.md) Документация построена на [Zensical](https://zensical.org) (MkDocs-совместимый движок).
## Быстрый старт
```bash
# Сборка статического сайта
make site
# Просмотр в режиме live
make live
```
## Структура
- `content/` — исходники документации (Markdown)
- `zensical.toml` — конфигурация сайта
- `overrides/` — кастомные темы и иконки
- `snippets/` — переиспользуемые фрагменты
- `site/` — скомпилированный статический сайт
## Правила оформления
См. [content/iptvc/dev/docs.md](content/iptvc/dev/docs.md).
## Лицензия ## Лицензия
Исходный код и готовая документация распространяется на условиях лицензии MIT. Исходный код и готовая документация распространяется на условиях лицензии MIT.
См. файл [LICENSE](LICENSE) для подробностей. См. файл [LICENSE](LICENSE) для подробностей.
Стороннее ПО и ресурсы используются в соответствии лицензиями, указанными в файле [content/legal/foss.md](content/legal/foss.md).
+192
View File
@@ -0,0 +1,192 @@
:root {
/* Image admonition */
--md-admonition-icon--image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M16 13.25A1.75 1.75 0 0 1 14.25 15H1.75A1.75 1.75 0 0 1 0 13.25V2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75ZM1.75 2.5a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h.94l.03-.03 6.077-6.078a1.75 1.75 0 0 1 2.412-.06L14.5 10.31V2.75a.25.25 0 0 0-.25-.25Zm12.5 11a.25.25 0 0 0 .25-.25v-.917l-4.298-3.889a.25.25 0 0 0-.344.009L4.81 13.5ZM7 6a2 2 0 1 1-3.999.001A2 2 0 0 1 7 6M5.5 6a.5.5 0 1 0-1 0 .5.5 0 0 0 1 0"/></svg>');
/* Yoomoney logo */
--md-admonition-icon--yoomoney: url('data:image/svg+xml;charset=utf-8,<svg width="169" height="120" viewBox="0 0 169 120" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M108.99 0C75.5725 0 48.9902 26.962 48.9902 60C48.9902 93.4177 75.9523 120 108.99 120C142.028 120 168.99 93.038 168.99 60C168.99 26.962 142.028 0 108.99 0ZM108.99 82.4051C96.8383 82.4051 86.5852 72.1519 86.5852 60C86.5852 47.8481 96.8383 37.5949 108.99 37.5949C121.142 37.5949 131.395 47.8481 131.395 60C131.016 72.1519 121.142 82.4051 108.99 82.4051Z" fill="white"/><path d="M48.6076 17.4684V104.81H27.3418L0 17.4684H48.6076V17.4684Z" fill="white"/></svg>');
/* Boosty logo */
--md-admonition-icon--boosty: url('data:image/svg+xml;charset=utf-8,<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M2.661 14.337 6.801 0h6.362L11.88 4.444l-0.038 0.077 -3.378 11.733h3.15c-1.321 3.289 -2.35 5.867 -3.086 7.733 -5.816 -0.063 -7.442 -4.228 -6.02 -9.155M8.554 24l7.67 -11.035h-3.25l2.83 -7.073c4.852 0.508 7.137 4.33 5.791 8.952C20.16 19.81 14.344 24 8.68 24h-0.127z" fill="white" stroke-width="1"></path></svg>');
}
/* Image admonition styling */
.md-typeset .admonition.image,
.md-typeset details.image {
border-color: var(--md-admonition-fg-color);
}
.md-typeset .image > .admonition-title::before,
.md-typeset .image > summary::before {
background-color: var(--md-admonition-fg-color);
-webkit-mask-image: var(--md-admonition-icon--image);
mask-image: var(--md-admonition-icon--image);
}
/* Yoomoney admonition styling */
.md-typeset .admonition.yoomoney,
.md-typeset details.yoomoney {
border-color: rgba(113, 47, 244, 1);
}
.md-typeset .yoomoney > .admonition-title::before,
.md-typeset .yoomoney > summary::before {
background-color: rgb(113, 47, 244);
-webkit-mask-image: var(--md-admonition-icon--yoomoney);
mask-image: var(--md-admonition-icon--yoomoney);
}
/* Boosty admonition styling */
.md-typeset .admonition.boosty,
.md-typeset details.boosty {
border-color: rgb(241, 95, 44);
}
.md-typeset .boosty > .admonition-title::before,
.md-typeset .boosty > summary::before {
background-color: rgb(241, 95, 44);
-webkit-mask-image: var(--md-admonition-icon--boosty);
mask-image: var(--md-admonition-icon--boosty);
}
.badge {
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
box-sizing: border-box;
color: rgb(33, 37, 41);
display: inline-block;
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial,
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
font-size: 10.5px;
font-weight: 700;
height: 17.8438px;
line-height: 10.5px;
margin-right: 4px;
padding-bottom: 3.675px;
padding-left: 6.825px;
padding-right: 6.825px;
padding-top: 3.675px;
text-align: center;
text-size-adjust: 100%;
text-wrap-mode: nowrap;
vertical-align: baseline;
white-space-collapse: collapse;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.badge.online {
background-color: rgb(25, 135, 84);
}
.badge.online-percent {
border: 1px solid rgb(25, 135, 84);
color: var(--md-typeset-color);
}
.badge.offline {
background-color: rgb(220, 53, 69);
}
.badge.unknown {
background-color: rgb(108, 117, 125);
}
.badge.adult {
background-color: rgb(255, 193, 7);
}
.badge.lapka {
background-color: rgb(13, 202, 240);
}
.icon.online {
color: rgb(25, 135, 84);
}
.icon.offline {
color: rgb(220, 53, 69);
}
/*******************************************************************/
/* Common utils */
.md-typeset .md-button--primary {
background: var(--md-accent-fg-color--transparent);
color: var(--md-accent-fg-color);
}
.md-typeset .wordwrap * {
text-wrap: auto;
}
.md-typeset kbd {
border-radius: .2rem;
font-size: .85em;
padding: 0 .5em;
margin: 0 .1em;
box-shadow:
0 0.1rem 0 0.05rem var(--md-typeset-kbd-border-color),
0 0.1rem 0 var(--md-typeset-kbd-border-color),
0 -0.05rem 0.1rem var(--md-typeset-kbd-accent-color) inset;
}
/*******************************************************************/
/* Landing page template (overrides/landing.html) */
.tx-hero {
margin: -2.4rem -1.6rem 2.4rem;
padding: 3.5rem 1.6rem;
}
.tx-hero h1 {
margin: 0 0 0.5rem;
font-size: 3rem;
font-weight: 700;
line-height: 1.1;
}
.tx-hero p {
max-width: 40rem;
font-size: 1.15rem;
line-height: 1.5;
}
.tx-hero__buttons {
display: flex;
gap: 1rem;
}
/*******************************************************************/
/* Inline badges (inline_badges.py) */
.md-typeset .mdx-badge {
font-size: 0.8rem;
display: inline-flex;
align-items: stretch;
overflow: hidden;
border: .1rem solid var(--md-accent-fg-color--transparent);
border-radius: .25rem;
vertical-align: baseline;
}
.md-typeset .mdx-badge__icon,
.md-typeset .mdx-badge__text {
display: inline-flex;
align-items: center;
}
.md-typeset .mdx-badge__icon {
justify-content: center;
background-color: var(--md-accent-fg-color--transparent);
padding: .15rem;
color: var(--md-primary-fg-color);
}
.md-typeset .mdx-badge__text {
font-size: .85em;
padding: 0 .5rem;
}
.md-typeset .mdx-badge__text code {
margin: 0;
padding: 0;
border: 0;
background: transparent;
}

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

+103
View File
@@ -0,0 +1,103 @@
---
icon: material/file-refresh-outline
tags: ["статусы", "плейлисты", "каналы", "iptvc"]
---
# :material-file-refresh-outline: Проверки и статусы
!!! danger "Я не гарантирую корректность и актуальность плейлистов, которые ты увидишь на сайте, как и корректность результатов их проверки."
После прочтения этой страницы ты поймёшь почему.
Плейлисты [проверяются автоматически](../iptvc/overview.md) с некоей периодичностью.
Хотя я и стараюсь улучшать качество проверок, но всё же рекомендую проверять желаемые плейлисты самостоятельно вручную, ибо нет никаких гарантий:
* что плейлист составлен корректно и обработается правильно;
* что плейлист (не) работоспособен:
* он может работать, но проверка заврешена из-за какой-то технической ошибки;
* он уже может не работать, но результаты последней проверки показывают обратное;
* что транслируемый контент соответствует заявленным названиям;
* что сейчас или через X времени там не окажется [заглушка](faq.md#заглушка).
## Статусы плейлистов { id="playlists" }
Каждый плейлист может быть в одном из трёх статусов:
* <span class="badge unknown">unknown</span> — **Плейлист в очереди на проверку**
Он сменит свой статус в ближайшие минуты.
* <span class="badge online">online</span> — **Плейлист проверен**
Это не значит, что он работает.
Это значит, что адрес плейлиста корректен и *вероятно* там *что-то* транслируется.
* <span class="badge offline">offline</span> — **Плейлист недоступен**
Либо плейлист удалён с сервера, где он располагался когда-то, либо это просто разовый сбой (например, таймаут проверки).
*Возможно*, (не) скоро он (не) станет доступен.
!!! info "Обрати внимание"
Независимо от статуса плейлиста на сайте, его можно добавить в свой плеер по "Ссылке для ТВ" и проверить самостоятельно.
Проверка плейлиста не влияет на его работоспособность.
### Дополнительные возможности { id="extra" }
Если плейлист <span class="badge online">online</span> (успешно проверен), то у него могут быть дополнительные значки:
* <span class="badge online-percent">95%</span> — количество рабочих каналов на момент проверки;
* <span class="badge adult">18+</span> — плейлист имеет каналы для взрослых;
* <span class="badge lapka"><ion-icon name="paw"></ion-icon></span> — плейлист может быть нестабилен
Это значит, что в нём есть каналы со специальными параметрами: токенами, логинами и паролями, которые рано или поздно истекут или будут заблокированы (если ещё не).
Также это признак бесплатного пробного периода.
* <ion-icon name="folder-open-outline"></ion-icon> — каналы плейлиста разбиты на группы (например, музыкальные каналы и региональные);
* <ion-icon name="newspaper-outline"></ion-icon> — плейлист предоставляет программу передач для каналов;
* <ion-icon name="play-back"></ion-icon> — плейлист предоставляет возможность перемотки передач.
Если плейлист <span class="badge unknown">unknown</span> или <span class="badge offline">offline</span>, этих иконок не будет.
!!! info "Обрати внимание"
1. Пропорции рабочих и нерабочих каналов в плейлистах могут меняться от проверки к проверке.
Это нормально, таковы технические особенности проверки.
2. Работа архива и программы передач зависит от выбранного [плеера](../common/players.md).
Некоторые это просто не поддерживают.
## Статусы каналов { id="channels" }
Каждый канал в любом плейлисте может быть в одном из трёх статусов:
* <span class="icon online"><ion-icon name="radio-button-on-outline"></ion-icon></span> — ***Возможно*, канал работает**
Но там может транслироваться какая-нибудь [заглушка](faq.md#заглушка) (например, от [Wink](faq.md#wink)) или другой канал.
* <span class="icon offline"><ion-icon name="radio-button-on-outline"></ion-icon></span> — ***Возможно*, канал не работает**
Чем больше таких каналов в плейлисте, тем сложнее будет листать плейлист в плеере или на ТВ.
Но *возможно когда-нибудь* плейлист обновят и канал будет работать исправно.
Также здесь может быть просто разовый сбой (например, таймаут проверки).
* <span class="badge lapka"><ion-icon name="paw"></ion-icon></span> — **Канал может быть нестабилен**
Это значит, что для него указаны специальные параметры: токен, логин или пароль, которые рано или поздно истекут или будут заблокированы.
Тогда канал перестанет работать.
Также это признак бесплатного пробного периода.
* <span class="badge adult">18+</span> — **Канал для взрослых**
Читай ниже.
## Контент для взрослых { id="adult" }
Это откровенно порнографический, эротический или другой контент, неприемлемый для детской психики (например, жанровые каналы с фильмами ужасов).
Если при проверке плейлиста обнаружен хотя бы один канал для взрослых, то этот канал и весь плейлист помечается значком <span class="badge adult">18+</span>.
Такие каналы определяются благодаря правилам, описанным в файле [channels.json](../common/formats/channels.md).
Они применяются к названиям каналов и их атрибутам (`tvg-id`, `tvg-name`), которые описывают канал в плейлисте.
Для каналов со взрослым контентом применяется тег `adult`.
!!! warning "Обрати внимание"
Далеко не все каналы могут быть помечены таким тегом.
Хотя набор правил для тегов очень богат, но невозможно угадать все каналы с приемлемой точностью.
Почему — читай [здесь](../common/formats/channels.md#warnings).
---
!!! info "Примечание"
Теперь должно стать понятно, почему я не даю и не могу дать никаких гарантий относительно результатов проверок и тегов.
Каждый раз это просто непредсказуемый процесс.
+67 -63
View File
@@ -11,83 +11,80 @@ tags: ["сайт", "каналы", "плейлисты", "epg", "плееры",
## Добавь каналы! ## Добавь каналы!
Нет. Пожалуйста, обратитесь к [этому разделу документации](https://go-friend-go.narod.ru).
## Удали каналы! ## Удали каналы!
Нет. Пожалуйста, обратитесь к [этому разделу документации](https://go-friend-go.narod.ru).
## Но мне нужны конкретные каналы! ## Но мне нужны конкретные каналы!
Ищи. Пожалуйста, обратитесь к [этому разделу документации](https://go-friend-go.narod.ru).
## Сделай мне плейлист! ## Сделай мне плейлист!
Нет. Пожалуйста, обратитесь к [этому разделу документации](https://go-friend-go.narod.ru).
## Исправь плейлист! ## Исправь плейлист!
Нет. Пожалуйста, обратитесь к [этому разделу документации](https://go-friend-go.narod.ru).
## А за деньги? ## А за деньги?
[Пожертвованиям](support.md) я только рад. Пожалуйста, обратитесь к [этому разделу документации](https://go-friend-go.narod.ru).
Но нет.
## Эти плейлисты бесплатны? ## Эти плейлисты бесплатны?
Возможно. Возможно.
По крайней мере, так утверждают источники, которые их распространяют. По крайней мере, так утверждают источники, которые их распространяют.
Но гарантий никаких никто не даёт. Но гарантий никаких никто не даёт и не может.
Любой плейлист и любой канал в любом плейлисте может сдохнуть навсегда в любой момент. Любой плейлист и любой канал в любом плейлисте может сдохнуть навсегда в любой момент.
Или показывать [заглушку](#заглушка). Или показать [заглушку](#заглушка).
И претензии на этот счёт я не принимаю. И претензии на этот счёт я не принимаю.
## Откуда берутся логотипы каналов и программы передач? ## Откуда берутся логотипы каналов и программы передач?
Всё это (не) [указывается](formats/m3u.md#tvg-logo) внутри плейлиста его авторами. Всё это (не) [указывается](../common/formats/m3u.md#tvg-logo) внутри плейлиста его авторами.
Но в некоторых [плеерах](common/players.md) можно вручную указывать программу передач (см. ниже). Но в некоторых [плеерах](../common/players.md) можно вручную указывать программу передач (см. ниже).
## У канала нет логотипа! ## У канала нет логотипа!
Вспоминай. Вспоминай.
## Нет программы передач (EPG) у *канала*, что делать? ## У **канала** нет программы передач (EPG), что делать?
Фига в том, что EPG может быть и [указан](formats/m3u.md#url-tvg) в плейлисте, но у конкретного канала могут быть указаны некорректные [`tvg-id`](formats/m3u.md#tvg-id) или [`tvg-name`](formats/m3u.md#tvg-name). Фига в том, что EPG может быть и [указан](../common/formats/m3u.md#url-tvg) в плейлисте, но у конкретного канала могут быть указаны некорректные [`tvg-id`](../common/formats/m3u.md#tvg-id) или [`tvg-name`](../common/formats/m3u.md#tvg-name).
Может, его дёрнули из другого листа и не подогнали под другую EPG. Может, его дёрнули из другого листа и не подогнали под другую EPG.
Так, что вариантов масса: Так что вариантов масса:
* смотреть как есть; * смотреть как есть;
* найти другой плейлист, где этот канал есть не только сам по себе, но и с телепрограммой; * найти другой плейлист, где этот канал есть с телепрограммой;
* скачать плейлист себе файлом, исправить атрибуты канала и добавить в плеер уже этот лист, но забыть о его автообновлении; * скачать плейлист себе файлом, исправить атрибуты канала и добавить в плеер уже этот лист, но забыть о его автообновлении;
* настроить другую программу передач (см. ниже). * настроить другую программу передач (см. ниже).
Также, помни, что не все плееры вообще поддерживают работу с телепрограммой. Также, помни, что не все плееры вообще поддерживают работу с телепрограммой.
## Нет программы передач (EPG) у *плейлиста*, что делать? ## У **плейлиста** нет программы передач (EPG), что делать?
Помни: Помни:
* не все плееры вообще поддерживают работу с телепрограммой; * не все плееры вообще поддерживают работу с телепрограммой;
* в плейлисте она просто может не быть указана. * в плейлисте она просто может не быть указана.
Но если [плеер](common/players.md) позволяет, можно указать ссылку на сторонную телепрограмму. Но если [плеер](../common/players.md) позволяет, можно указать ссылку на сторонную телепрограмму.
И это целое дело. И это целое дело.
Надо чтобы совпадали [`tvg-id`](formats/m3u.md#tvg-id) или [`tvg-name`](formats/m3u.md#tvg-name) каналов с теми, которые указываются в EPG. Надо чтобы совпадали [`tvg-id`](../common/formats/m3u.md#tvg-id) или [`tvg-name`](../common/formats/m3u.md#tvg-name) каналов с теми, которые указываются в EPG.
Так что не всякая телепрограмма подойдёт, не ко всякому плейлисту и не ко всем каналам. Так что не всякая телепрограмма подойдёт, не ко всякому плейлисту и не ко всем каналам.
Надо подбирать и то, и то. Надо подбирать и то, и то.
<a id="epg"></a> ## А где взять программу передач (EPG)? { id="epg" }
## А где взять программу передач (EPG)?
1. Из самого плейлиста. 1. Из самого плейлиста.
Часто в атрибут [`url-tvg`](formats/m3u.md#url-tvg) тега `#EXTM3U` указывают одну или две ссылки на EPG, разделяя их `;`. Часто в атрибут [`url-tvg`](../common/formats/m3u.md#url-tvg) тега `#EXTM3U` указывают одну или две ссылки на EPG, разделяя их `;`.
Их можно использовать отдельно, например, если плеер не может корректно обработать такое значение. Их можно использовать отдельно, например, если плеер не может корректно обработать такое значение.
2. Взять одну из этих ссылок: 2. Взять одну из этих ссылок:
@@ -117,13 +114,13 @@ tags: ["сайт", "каналы", "плейлисты", "epg", "плееры",
## Почему на сайте плейлист онлайн, но в нём 0 каналов? ## Почему на сайте плейлист онлайн, но в нём 0 каналов?
[Тебе сюда](common/checks.md#плейлисты). [Тебе сюда](checks.md#playlists).
## Почему на сайте плейлист онлайн, но у меня он не работает? ## Почему на сайте плейлист онлайн, но у меня он не работает?
Что значит "не работает"? Что значит "не работает"?
* Ты уверен, что ссылка в [правильном формате](common/details.md#ссылка-для-тв)? * Ты уверен, что ссылка в [правильном формате](../iptvc/site/details.md#shortlink)?
* Ты уверен, что у тебя нормальное интернет-соединение? * Ты уверен, что у тебя нормальное интернет-соединение?
* Плеер показывает какую-то ошибку при добавлении плейлиста? * Плеер показывает какую-то ошибку при добавлении плейлиста?
* Плейлист добавляется по ссылке, но каналы не загружаются или плеер зависает? * Плейлист добавляется по ссылке, но каналы не загружаются или плеер зависает?
@@ -131,12 +128,12 @@ tags: ["сайт", "каналы", "плейлисты", "epg", "плееры",
* А есть скриншоты? Логи? Тексты ошибок? * А есть скриншоты? Логи? Тексты ошибок?
Попробуй погуглить проблему с конкретным плеером, может ты не один с такой проблемой. Попробуй погуглить проблему с конкретным плеером, может ты не один с такой проблемой.
Или [узнать в чате](tg/chat.md). Или [узнать в чате](../tg/chat.md).
Но вообще, это зависит от: Но вообще, это зависит от:
* автора плейлиста (дохлые каналы почти всегда есть даже в свежайших листах, но если лист не обновлялся год, что ты вряд-ли узнаешь, то рабочих каналов там не ищи); * автора плейлиста (дохлые каналы почти всегда есть даже в свежайших листах, но если лист не обновлялся год, что ты вряд-ли узнаешь, то рабочих каналов там не ищи);
* твоего [плеера](common/players.md) (видеотрансляции могут использовать кодек, который просто не поддерживается плеером); * твоего [плеера](../common/players.md) (видеотрансляции могут использовать кодек, который просто не поддерживается плеером);
* ширины твоего интернет-канала (не надо пытаться врубать FHD/4K трансляции с мобильного интернета на самом дешёвом тарифе в лесу); * ширины твоего интернет-канала (не надо пытаться врубать FHD/4K трансляции с мобильного интернета на самом дешёвом тарифе в лесу);
* ретроградности Меркурия и магнитных бурь (а вдруг); * ретроградности Меркурия и магнитных бурь (а вдруг);
* настроек твоей сети, твоего интернет-провайдера, VPN (подумай хорошенько, хочешь ли ты смотреть российские каналы из РФ через Уганду или США); * настроек твоей сети, твоего интернет-провайдера, VPN (подумай хорошенько, хочешь ли ты смотреть российские каналы из РФ через Уганду или США);
@@ -152,17 +149,17 @@ tags: ["сайт", "каналы", "плейлисты", "epg", "плееры",
Наверное, даже долго. Наверное, даже долго.
Мёртвые плейлисты я периодически вычищаю, реже -- добавляю новые. Мёртвые плейлисты я периодически вычищаю, реже добавляю новые.
Короткие коды плейлистов могут меняться, поэтому вполне может произойти внезапная подмена одного другим, однако это происходит крайне редко. Короткие коды плейлистов могут меняться, поэтому вполне может произойти внезапная подмена одного другим, однако это происходит крайне редко.
Плюс читай про доверие [результатам проверки](common/checks.md). Плюс читай про доверие [результатам проверки](checks.md).
## В плейлистах порнуха! ## В плейлистах порнуха!
Да, в плейлистах порнуха. Да, в плейлистах порнуха.
Это [явно помечается](common/checks.md#для-взрослых) везде, где это технически возможно. Это [явно помечается](checks.md#adult) везде, где это технически возможно.
Смотри с удовольствием сколько хочешь, всё для тебя. Смотри с удовольствием сколько хочешь, всё для тебя.
Или без удовольствия. Или без удовольствия.
@@ -188,13 +185,12 @@ tags: ["сайт", "каналы", "плейлисты", "epg", "плееры",
Если есть кандидаты на добавление, то читай ниже. Если есть кандидаты на добавление, то читай ниже.
<a id="автообновляемый"></a> ## Что значит автообновляемый плейлист? { id="автообновляемый" }
## Что значит автообновляемый плейлист?
Например, ты на своём компе: Например, ты на своём компе:
* открываешь любой текстовый редактор; * открываешь любой текстовый редактор;
* оформляешь текст в формате [m3u](formats/m3u.md); * оформляешь текст в формате [m3u](../common/formats/m3u.md);
* сохраняешь в файл `pls.m3u`. * сохраняешь в файл `pls.m3u`.
Получился плейлист `pls.m3u`. Получился плейлист `pls.m3u`.
@@ -234,8 +230,8 @@ tags: ["сайт", "каналы", "плейлисты", "epg", "плееры",
В чём плюсы: В чём плюсы:
* ты сам не изменяешь этот *чей-то* файл и не паришься; * ты сам не изменяешь этот *чей-то* файл и не паришься;
* возможно, кто-то за ним следит и периодически обновляет самостоятельно (вручную или как-то автоматически -- это не твоя проблема); * возможно, кто-то за ним следит и периодически обновляет самостоятельно (вручную или как-то автоматически это не твоя проблема);
* возможно, твой плеер сам подтянет плейлист после запуска (чаще всего так и происходит) или по твоей команде (если такая кнопка в нём есть) -- **это и есть автообновление**. * возможно, твой плеер сам подтянет плейлист после запуска (чаще всего так и происходит) или по твоей команде (если такая кнопка в нём есть) **это и есть автообновление**.
В чём минус: этот *кто-то* по своим причинам может удалить твой любимый канал или вообще плейлист, и больше он не подгрузится. В чём минус: этот *кто-то* по своим причинам может удалить твой любимый канал или вообще плейлист, и больше он не подгрузится.
@@ -255,53 +251,64 @@ tags: ["сайт", "каналы", "плейлисты", "epg", "плееры",
Нет, и не планируется. Нет, и не планируется.
Ищи [плеер](common/players.md) и добавляй плейлист туда по ссылке. Ищи [плеер](../common/players.md) и добавляй плейлист туда по ссылке.
<a id="заглушка"></a> ## На канале отображается заглушка { id="заглушка" }
## На канале отображается заглушка
<a id="заглушка1"></a> ### Просят денег и/или подписку { id="заглушка1" }
### Просят денег и/или подписку
??? quote "[Скриншот] Уважаемый клиент! Для возобновления просмотра Вам необходимо использовать не более 2 устройств" ??? image "Уважаемый клиент! Для возобновления просмотра Вам необходимо использовать не более 2 устройств"
![](assets/img/paywalls/1.jpg) ![](_assets/paywalls/1.jpg)
> Уважаемый клиент! Для возобновления просмотра Вам необходимо использовать не более 2 устройств. > Уважаемый клиент! Для возобновления просмотра Вам необходимо использовать не более 2 устройств.
> Обратитесь к поставщику контента для уточнения. > Обратитесь к поставщику контента для уточнения.
??? quote "[Скриншот] Ваша подписка не активна" ??? image "Ваша подписка не активна"
![](assets/img/paywalls/2.jpg) ![](_assets/paywalls/2.jpg)
> Ваша подписка не активна > Ваша подписка не активна
> Your subscription is not active > Your subscription is not active
??? quote "[Скриншот] Мы обнаружили систематическое нарушение правил использования нашего сервиса" ??? image "Мы обнаружили систематическое нарушение правил использования нашего сервиса"
![](assets/img/paywalls/3.jpg) ![](_assets/paywalls/3.jpg)
> Мы обнаружили систематическое нарушение правил использования нашего сервиса и заблокировали возможность просмотра контента. > Мы обнаружили систематическое нарушение правил использования нашего сервиса и заблокировали возможность просмотра контента.
> Для возобновления просмотра необходимо сменить OTTID в личном кабинете, и обновить плейлисты на ваших устройствах. > Для возобновления просмотра необходимо сменить OTTID в личном кабинете, и обновить плейлисты на ваших устройствах.
> Если вы считаете, что произошла какая-то ошибка - пожалуйста, обратитесь в техподдержку. > Если вы считаете, что произошла какая-то ошибка - пожалуйста, обратитесь в техподдержку.
Кто-то взял платный (или временный демонстрационный) плейлист и распространил его как бесплатный. ??? image "Вы превысили разрешённое количество одновременных поджключений"
![](_assets/paywalls/4.jpg)
> Вы превысили разрешённое количество одновременных подключений
> Для возобновления просмотра ограничьте количество подключённых устройств
Это либо тестовый/платный плейлист платного провайдера, либо канал от платного провайдера в чьём-то сборном бесплатном плейлисте.
Такие бесплатно работать не будут, а если и работают, то недолго и не у всех. Такие бесплатно работать не будут, а если и работают, то недолго и не у всех.
Забудь про этот плейлист. **Забудь про этот плейлист.**
Ищи другой.
Без вариантов.
Такова цена халявы.
Этот плейлист -- главный кандидат на удаление с сайта. **Ищи другой.**
<a id="wink"></a> **Без вариантов.**
### Wink
??? quote "[Скриншот] Просмотр ТВ-каналов, фильмов и сериалов доступен только в официальных приложениях Wink и на территории России" **Такова цена халявы.**
![](assets/img/paywalls/wink.jpg)
Этот плейлист — главный кандидат на удаление с сайта.
### Wink { id="wink" }
??? image "Просмотр ТВ-каналов, фильмов и сериалов доступен только в официальных приложениях Wink и на территории России"
![](_assets/paywalls/wink.jpg)
> Просмотр ТВ-каналов, фильмов и сериалов доступен только в официальных приложениях Wink и на территории России > Просмотр ТВ-каналов, фильмов и сериалов доступен только в официальных приложениях Wink и на территории России
Кто-то воткнул платный канал в плейлист и распространил его как бесплатный. ??? image "Wink ещё не показывает видео на этой территории"
![](_assets/paywalls/wink2.jpg)
> Wink ещё не показывает видео на этой территории
Если ты не сидишь под российским IP, то можешь сразу искать другой плейлист. **Решение 1:** купить подписку Wink и использовать официальные приложения.
Если ты в РФ, то попробуй использовать [плеер](common/players.md), который позволяет указать **User-Agent**, и вставить туда какой-нибудь из этих: **Решение 2:** найти другой плейлист.
**Решение 3:** если IP не российский, то сделать его российским (любыми способами).
**Решение 4:** использовать [плеер](../common/players.md), который позволяет указать **User-Agent**, и вставить туда какой-нибудь из этих:
``` ```
Mozilla/5.0 WINK/1.28.2 (AndroidTV/9) HlsWinkPlayer Mozilla/5.0 WINK/1.28.2 (AndroidTV/9) HlsWinkPlayer
@@ -314,12 +321,9 @@ Mozilla/5.0 (Linux; Android 9; SWITRON-i12A Build/PSV1.210329.021; wv)
Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
``` ```
Гарантий никаких, но кому-то помогает.
Можно попробовать прописать без `Mozilla/5.0` в начале, но это не должно быть критично. Можно попробовать прописать без `Mozilla/5.0` в начале, но это не должно быть критично.
Или подключи подписку Wink. Гарантий никаких, но кому-то помогает.
Или забудь про этот плейлист и ищи другой.
## Где спортивные каналы? Почему они не работают? ## Где спортивные каналы? Почему они не работают?
@@ -337,7 +341,7 @@ Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Нет, я не буду добавлять каналы в плейлисты. Нет, я не буду добавлять каналы в плейлисты.
Если будет спортивный рабочий плейлист -- добавлю на сайт. Если будет спортивный рабочий плейлист добавлю на сайт.
## Как добавить плейлист в общий список? ## Как добавить плейлист в общий список?
+16 -16
View File
@@ -1,18 +1,18 @@
--- ---
icon: material/home icon: material/home
hide: [toc]
--- ---
# :material-home: Введение # :material-home: Об агрегаторе m3u.su
Сервис предназначен для централизованного сбора ссылок на публичные IPTV-плейлисты, которые находятся в открытом доступе. Этот сервис предназначен для централизованного сбора ссылок на публичные IPTV-плейлисты, которые находятся в открытом доступе.
Они отбираются вручную и периодически проверяются автоматически. Они отбираются вручную и периодически проверяются автоматически.
!!! info "Все необходимые адреса" !!! info "Все необходимые адреса"
**Веб-сайт:** [m3u.su](https://m3u.su) **Веб-сайт:** [m3u.su](https://m3u.su)
Документация: [m3u.su/docs](https://m3u.su/docs)
Исходный код: [git.axenov.dev/IPTV](https://git.axenov.dev/IPTV) Исходный код: [git.axenov.dev/IPTV](https://git.axenov.dev/IPTV)
Новостной канал: [@iptv_aggregator](https://t.me/iptv_aggregator) Telegram-канал: [@iptv_aggregator](https://t.me/iptv_aggregator)
Обсуждение: [@iptv_aggregator_chat](tg/chat.md)
Бот: [@iptv_aggregator_bot](tg/bot.md)
Далеко не все пользователи, желающие использовать цифровое ТВ, могут позволить себе подключение IPTV у своего провайдера или поставщиков контента. Далеко не все пользователи, желающие использовать цифровое ТВ, могут позволить себе подключение IPTV у своего провайдера или поставщиков контента.
@@ -30,19 +30,19 @@ icon: material/home
## Основные принципы проекта ## Основные принципы проекта
1. Проект должен быть публичен и бесплатен для всех 1. Проект должен быть публичен и бесплатен для всех.
2. Проект не должен нарушать права третьих лиц 2. Проект не должен нарушать права третьих лиц.
3. Весь исходный код проекта должен быть открыт и распространяться под MIT-лицензией 3. Весь исходный код проекта должен быть открыт и распространяться под MIT-лицензией.
## Для чего предназначен сервис ## Для чего предназначен сервис
1. Для хранения ссылок на сторонние плейлисты 1. Для хранения ссылок на сторонние плейлисты.
2. Для периодической проверки доступности плейлистов и их каналов 2. Для периодической проверки доступности плейлистов и их каналов.
3. Для кратковременного хранения результатов проверок и их публикации (в человекочитаемом и машиночитаемом виде) 3. Для кратковременного хранения результатов проверок и их публикации (в человекочитаемом и машиночитаемом виде).
4. Для категоризации плейлистов и их каналов с помощью тегов 4. Для категоризации плейлистов и их каналов с помощью тегов.
5. Для предоставления коротких ссылок на плейлисты (для удобства ввода с пульта) 5. Для предоставления коротких ссылок на плейлисты (для удобства ввода с пульта).
6. Для переадресации с короткого адреса плейлиста на исходный 6. Для переадресации с короткого адреса плейлиста на исходный.
7. Для публикации общей информации о каналах (название, логотип) 7. Для публикации общей информации о каналах (название, логотип).
## Для чего НЕ предназначен сервис ## Для чего НЕ предназначен сервис
@@ -61,7 +61,7 @@ icon: material/home
Автор не зарабатывает на проекте и не собирается. Автор не зарабатывает на проекте и не собирается.
Всё, что отображается на сайте, сделано бесплатно и на энтузиазме. Всё, что отображается на сайте, сделано бесплатно и на энтузиазме.
Но ты можешь сделать [добровольное пожертвование](support.md), которое поможет мне компенсировать затраты на поддержку и техническое развитие проекта. Но ты можешь сделать [добровольное пожертвование](../common/support.md), которое поможет мне компенсировать затраты на поддержку и техническое развитие проекта.
## Условия, гарантии, обязательства и последствия ## Условия, гарантии, обязательства и последствия
+18
View File
@@ -0,0 +1,18 @@
---
icon: material/file-eye-outline
tags: ["статусы", "плейлисты"]
---
# :material-file-eye-outline: Как отбираются плейлисты
Есть некоторые важные критерии, по которым плейлисты отбираются в проект:
- открытый источник и прямая ссылка;
- автообновление;
- бесплатный доступ, без необходимости регистрации, без пробного периода;
- без ограничений, в т. ч. по количеству устройств, плеерам, длительности просмотра;
- безусловная доступность с территории РФ.
В основном, в плейлистах именно трансляции телеканалов, но может быть и медиатека: просто список каких-то (мульт)фильмов и записи телепередач, находящихся на чужих дисках (как если бы вы сами составили плейлист, например, с музыкой).
!!! danger "Плейлисты, нарушающие законодательство, удаляются с сайта окончательно по факту обращения от правообладателя."
+29
View File
@@ -0,0 +1,29 @@
---
icon: material/pulse
tags: ["сайт"]
---
# :material-pulse: Статус сервиса
Так выглядит статусная страница сервиса.
Попасть на неё можно по ссылке "[Аптайм](https://status.m3u.su)" в шапке сайта.
![Скриншот с примером главной страницы на десктопе](_assets/status/main.jpg)
Здесь отображается состояние компонентов сервиса:
* веб-интерфейс (то, что открывается в браузере);
* чекер (фоновая проверка плейлистов с помощью [iptvc](../iptvc/overview.md));
* кэш (база данных с временными данными о результатах проверки плейлистов).
Шкалы движутся во времени справа налево.
Они должны быть зелёными, а статус компонента должен быть «Healthy».
Каждое деление на шкале приблизительно равно 10 минутам между проверками.
Можно нажать на название сервиса и посмотреть детальную информацию:
![Скриншот с примером страницы компонента на десктопе](_assets/status/details.jpg)
Если на шкале появляется красное деление, значит был кратковременный сбой.
Но если вместо зелёного преобладают красные цвета, значит на сервере что-то основательно сломалось.
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

+636
View File
@@ -0,0 +1,636 @@
---
title: config.yml
icon: material/file-cog
tags: ["iptvc", "конфигурация"]
---
# :material-file-cog: Конфигурация config.yml
Программа читает настройки из YAML-файла `config.yml` в корне проекта.
Путь к файлу можно задать через глобальный флаг `--config`.
## Приоритет настроек
От низшего к высшему:
1. **Значения по умолчанию** — встроены в код;
2. **`config.yml`** — YAML-файл;
3. **Переменные окружения** — переопределяют `config.yml` (если заданы);
4. **CLI-флаги** — переопределяют переменные окружения и `config.yml` (если заданы явно).
Файл `.env` загружается автоматически, переменные из него применяются как переменные окружения.
## Структура файла
```yaml
app:
timezone: GMT
debug: false
log_level: info
playlists: ./playlists.ini
tags: ./channels.json
server:
host: ""
port: 8800
site:
base-url: http://localhost:8800
title: IPTV Checker
meta:
description: Самообновляемые бесплатные IPTV-плейлисты для домашнего просмотра
keywords: iptv,плейлисты,m3u
repo-url: https://git.axenov.dev/IPTV
page-size: 0
favicon:
header:
menu:
- title: Документация
url: /docs
icon: document-text-outline
footer:
links:
- title: Исходники
url: https://git.axenov.dev/IPTV
icon: code-slash-outline
tabs:
raw:
visible: true
legal:
visible: true
text: |
<p>Юридический текст с <a href="/terms">условиями использования</a>.</p>
check:
start-on-serve: false
playlists:
user-agent:
- Mozilla/5.0 WINK/1.31.1 (AndroidTV/9) HlsWinkPlayer
timeout: 10 # секунды
all-cooldown: 1800 # секунды
one-cooldown: 2 # секунды
max-routines: 1
per-routine: 1
channels:
user-agent: Mozilla/5.0 WINK/1.31.1 (AndroidTV/9) HlsWinkPlayer
timeout: 10 # секунды
byte-range: 512
cooldown: 0 # секунды
max-routines: 50
per-routine: 10
cache:
enabled: false
host: localhost
port: 6379
username:
password:
db: 0
ttl: 30 # секунды
```
Каждый параметр ниже описан отдельной секцией с указанием значения по умолчанию, переменной окружения и соответствующего CLI-флага.
---
## Секция `app` { id=app }
### `app.timezone` { id=app-timezone }
<!-- md:default GMT -->
<!-- md:env APP_TIMEZONE -->
Часовой пояс, используемый в логах и при отображении времени проверок.
---
### `app.debug` { id=app-debug }
<!-- md:default false -->
<!-- md:env APP_DEBUG -->
<!-- md:arg --debug -->
Режим отладки.
Включает расширенное логирование и дополнительные проверки в логике приложения.
---
### `app.log_level` { id=app-log-level }
<!-- md:default info -->
<!-- md:env APP_LOG_LEVEL -->
<!-- md:arg --log-level -->
Уровень логирования.
Допустимые значения: `debug`, `info`, `warn`, `error`.
---
### `app.playlists` { id=app-playlists }
<!-- md:default ./playlists.ini -->
<!-- md:env APP_PLAYLISTS -->
<!-- md:arg --ini -->
Путь к локальному [ini-файлу](../../common/formats/playlists.md) с описанием плейлистов.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
### `app.tags` { id=app-tags }
<!-- md:default ./channels.json -->
<!-- md:env APP_TAGS -->
<!-- md:arg --tags -->
Путь к локальному [json-файлу](../../common/formats/channels.md) с описанием тегов каналов.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
## Секция `server` { id=server }
### `server.host` { id=server-host }
<!-- md:default -->
<!-- md:env SERVER_HOST -->
<!-- md:arg --host -->
Хост для привязки веб-сервера.
Пустая строка — слушать на всех интерфейсах.
!!! info "Аргумент работает только для команды `serve`."
---
### `server.port` { id=server-port }
<!-- md:default 8800 -->
<!-- md:env SERVER_PORT -->
<!-- md:arg -p, --port -->
Порт веб-сервера.
!!! info "Аргумент работает только для команды `serve`."
---
## Секция `site` { id=site }
Настройки внешнего вида и ссылок сайта: заголовок, навигация, пагинация, иконка.
### `site.base-url` { id=site-base-url }
<!-- md:default http://localhost:8800 -->
<!-- md:env SITE_BASE_URL -->
Базовый URL сайта.
Используется при формировании абсолютных ссылок в шаблонах.
---
### `site.repo-url` { id=site-repo-url }
<!-- md:default https://git.axenov.dev/IPTV -->
<!-- md:env SITE_REPO_URL -->
Ссылка на исходный репозиторий (отображается в подвале).
---
### `site.page-size` { id=site-page-size }
<!-- md:default 0 -->
<!-- md:env SITE_PAGE_SIZE -->
Размер страницы пагинации.
При значении `0` пагинация отключена, на главной странице выводятся все плейлисты.
---
### `site.favicon` { id=site-favicon }
<!-- md:default -->
<!-- md:env SITE_FAVICON -->
Путь к файлу иконки сайта. Пустая строка — используется встроенная.
---
### `site.meta` { id=site-meta }
Мета-теги `<meta name="description">` и `<meta name="keywords">` для HTML-шаблонов.
| Поле | Переменная окружения | Описание |
| ------------- | ----------------------- | ---------------------------- |
| `description` | `SITE_META_DESCRIPTION` | Описание сайта в meta-тегах |
| `keywords` | `SITE_META_KEYWORDS` | Ключевые слова через запятую |
---
### `site.title` { id=site-title }
<!-- md:default IPTV Checker -->
<!-- md:env SITE_TITLE -->
Заголовок сайта, отображается в `<title>` и в navbar.
---
### `site.header.menu` { id=site-header-menu }
Массив элементов [`Link`](#link) в шапке сайта.
---
### `site.footer.links` { id=site-footer-links }
Массив элементов [`Link`](#link) в подвале сайта.
---
### `site.tabs.raw.visible` { id=site-tabs-raw-visible }
<!-- md:default true -->
Отображать вкладку «Исходный плейлист».
---
### `site.tabs.legal.visible` { id=site-tabs-legal-visible }
<!-- md:default true -->
Отображать вкладку «Юридическая информация».
---
### `site.tabs.legal.text` { id=site-tabs-legal-text }
HTML-контент вкладки «Юридическая информация».
Предназначена для вывода информации о правообладателях и контактах для связи с администратором сайта для решения правовых вопросов.
---
### Тип `Link` { id=link }
Элемент навигации или подвала.
Если задано `children`, рендерится как выпадающее меню.
| Поле | Тип | Описание |
| ---------- | ------ | ----------------------------------------------------------- |
| `title` | string | Текст ссылки |
| `url` | string | URL ссылки (можно опустить, если есть `children`) |
| `icon` | string | Имя иконки |
| `children` | Link[] | Дочерние ссылки (выпадающее меню, один уровень вложенности) |
--8<-- "icons.md"
```yaml title="Пример"
site:
header:
menu:
- title: Помощь
icon: help-circle-outline
children:
- title: Документация
url: https://m3u.su/docs
icon: document-text-outline
- title: Исходники
url: https://git.axenov.dev/IPTV
icon: code-slash-outline
- title: "@iptv_aggregator"
url: https://t.me/iptv_aggregator
icon: bullhorn-variant-outline
- title: Telegram
icon: bullhorn-variant-outline
children:
- title: Канал
url: https://t.me/iptv_aggregator
icon: megaphone-outline
- title: Чат
url: https://t.me/iptv_aggregator_chat
icon: chatbubbles-outline
footer:
links:
- title: Исходники
url: https://git.axenov.dev/IPTV
icon: code-slash-outline
- title: axenov.dev
url: https://axenov.dev
icon: person-outline
- title: "@iptv_aggregator"
url: https://t.me/iptv_aggregator
icon: megaphone-outline
```
---
## Секция `check` { id=check }
Параметры проверки плейлистов и каналов. Поддерживаются скаляры и массивы.
---
### `check.start-on-serve` { id=check-start-on-serve }
<!-- md:default false -->
<!-- md:env CHECK_START_ON_SERVE -->
Запустить фоновую проверку при `serve` без явного флага `--check`.
Независимый переключатель от CLI-флага `--check` — фоновая проверка стартует, если **хотя бы один** из них активен.
---
### `check.playlists` { id=check-playlists }
Параметры проверки плейлистов (загрузка m3u-файлов по URL или из ФС).
---
#### `check.playlists.user-agent` { id=check-playlists-user-agent }
<!-- md:default Mozilla/5.0 WINK/1.31.1 (AndroidTV/9) HlsWinkPlayer -->
<!-- md:env CHECK_PLAYLISTS_USER_AGENT_* -->
<!-- md:arg --playlists-user-agent -->
User-Agent для HTTP-запросов плейлистов.
!!! info "Необычный параметр"
Если значение параметра задано строкой, то в запросах к плейлистам будет использоваться только оно.
Если значение параметра задано массивом строк, то в запросах к плейлистам будет использоваться случайный из указанных.
!!! info "Необычная переменная"
В окружении может задаваться индексированными переменными:
- `CHECK_PLAYLISTS_USER_AGENT_1="value1"`
- `CHECK_PLAYLISTS_USER_AGENT_2="value2"`
- и т.д.; чтение останавливается на первой отсутствующей.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.playlists.timeout` { id=check-playlists-timeout }
<!-- md:default 10 -->
<!-- md:env CHECK_PLAYLISTS_TIMEOUT -->
<!-- md:arg --playlists-timeout -->
Таймаут HTTP-запроса плейлиста в секундах.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.playlists.all-cooldown` { id=check-playlists-all-cooldown }
<!-- md:default 1800 -->
<!-- md:env CHECK_PLAYLISTS_ALL_COOLDOWN -->
<!-- md:arg --playlists-all-cooldown -->
Задержка после проверки всех плейлистов в секундах.
!!! info "Необычная переменная"
Если значение переменной указано одним числом, то для задержки будет использоваться только оно.
Если значение переменной указано двумя числами через запятую, то будет использоваться случайная задержка в указанном диапазоне.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.playlists.one-cooldown` { id=check-playlists-one-cooldown }
<!-- md:default 2 -->
<!-- md:env CHECK_PLAYLISTS_ONE_COOLDOWN -->
<!-- md:arg --playlists-one-cooldown -->
Задержка после проверки каждого плейлиста в секундах.
!!! info "Необычная переменная"
Если значение переменной указано одним числом, то для задержки будет использоваться только оно.
Если значение переменной указано двумя числами через запятую, то будет использоваться случайная задержка в указанном диапазоне.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.playlists.max-routines` { id=check-playlists-max-routines }
<!-- md:default 1 -->
<!-- md:env CHECK_PLAYLISTS_MAX_ROUTINES -->
<!-- md:arg --playlists-max-routines -->
Максимальное количество параллельных потоков (рутин) проверки плейлистов.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.playlists.per-routine` { id=check-playlists-per-routine }
<!-- md:default 1 -->
<!-- md:env CHECK_PLAYLISTS_PER_ROUTINE -->
<!-- md:arg --playlists-per-routine -->
Максимальное количество плейлистов в каждом потоке (рутине) проверки.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
### `check.channels` { id=check-channels }
Параметры проверки каналов внутри плейлиста.
---
#### `check.channels.user-agent` { id=check-channels-user-agent }
<!-- md:default Mozilla/5.0 WINK/1.31.1 (AndroidTV/9) HlsWinkPlayer -->
<!-- md:env CHECK_CHANNELS_USER_AGENT_* -->
<!-- md:arg --channels-user-agent -->
User-Agent для HTTP-запроса каждого канала каждого плейлиста.
!!! info "Необычный параметр"
Если значение параметра задано строкой, то в запросах к каналам будет использоваться только оно.
Если значение параметра задано массивом строк, то в запросах к каналам будет использоваться случайный из указанных.
!!! info "Необычная переменная"
В окружении может задаваться индексированными переменными:
- `CHECK_CHANNELS_USER_AGENT_1="value1"`
- `CHECK_CHANNELS_USER_AGENT_2="value2"`
- и т.д.; чтение останавливается на первой отсутствующей.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.channels.timeout` { id=check-channels-timeout }
<!-- md:default 10 -->
<!-- md:env CHECK_CHANNELS_TIMEOUT -->
<!-- md:arg --channels-timeout -->
Таймаут HTTP-запроса каждого канала каждого плейлиста в секундах.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.channels.byte-range` { id=check-channels-byte-range }
<!-- md:default 512 -->
<!-- md:env CHECK_CHANNELS_BYTE_RANGE -->
<!-- md:arg --channels-byte-range -->
Объём данных в байтах, запрашиваемых у сервера при проверке каждого канала каждого плейлиста.
Меньшее значение повышает риск ошибок в определении типа контента (mime-type).
Большее значение может приводить к повышенной нагрузке и увеличению времени проверки.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.channels.cooldown` { id=check-channels-cooldown }
<!-- md:default 0 -->
<!-- md:env CHECK_CHANNELS_COOLDOWN -->
<!-- md:arg --channels-cooldown -->
Задержка после проверки каждого канала каждого плейлиста в секундах.
!!! info "Необычная переменная"
Если значение переменной указано одним числом, то для задержки будет использоваться только оно.
Если значение переменной указано двумя числами через запятую, то будет использоваться случайная задержка в указанном диапазоне.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.channels.max-routines` { id=check-channels-max-routines }
<!-- md:default 50 -->
<!-- md:env CHECK_CHANNELS_MAX_ROUTINES -->
<!-- md:arg --channels-max-routines -->
Максимальное количество параллельных потоков (рутин) проверки каналов каждого плейлиста.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
#### `check.channels.per-routine` { id=check-channels-per-routine }
<!-- md:default 10 -->
<!-- md:env CHECK_CHANNELS_PER_ROUTINE -->
<!-- md:arg --channels-per-routine -->
Максимальное количество каналов в каждом потоке (рутине) проверки каждого плейлиста.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
## Секция `cache` { id=cache }
Параметры подключения к KeyDB/Redis для хранения результатов проверок.
---
### `cache.enabled` { id=cache-enabled }
<!-- md:default false -->
<!-- md:env CACHE_ENABLED -->
<!-- md:arg --cache-enabled -->
Включить использование внешнего кеша.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
### `cache.host` { id=cache-host }
<!-- md:default localhost -->
<!-- md:env CACHE_HOST -->
<!-- md:arg --cache-host -->
Хост KeyDB/Redis.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
### `cache.port` { id=cache-port }
<!-- md:default 6379 -->
<!-- md:env CACHE_PORT -->
<!-- md:arg --cache-port -->
Порт KeyDB/Redis.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
### `cache.username` { id=cache-username }
<!-- md:default -->
<!-- md:env CACHE_USERNAME -->
<!-- md:arg --cache-username -->
Логин для подключения. Пустая строка — без аутентификации.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
### `cache.password` { id=cache-password }
<!-- md:default -->
<!-- md:env CACHE_PASSWORD -->
<!-- md:arg --cache-password -->
Пароль для подключения.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
### `cache.db` { id=cache-db }
<!-- md:default 0 -->
<!-- md:env CACHE_DB -->
<!-- md:arg --cache-db -->
Номер логической базы данных в KeyDB/Redis.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
---
### `cache.ttl` { id=cache-ttl }
<!-- md:default 30 -->
<!-- md:env CACHE_TTL -->
<!-- md:arg --cache-ttl -->
TTL записей кеша, секунды.
!!! info "Аргумент работает только для команд `check` и `serve --check`."
+33
View File
@@ -0,0 +1,33 @@
---
icon: material/book-open-page-variant-outline
---
# :material-book-open-page-variant-outline: Об этой документации
!!! warning "Актуальность документации может отставать от текущей версии сервиса, его исходных кодов и инфраструктуры"
Поддерживать содержимое в актуальном состоянии большой труд.
Прошу отнестись с пониманием, а лучше — [помочь делом](support.md#participate).
<!--
!!! danger "Тем не менее, прошу прочесть её!"
Потому что очень часто и мне, и в общий чат поступают одинаковые вопросы, на которые уже просто нет сил отвечать персонально.
-->
Если у тебя возникает вопрос, на который уже есть ответ на одной из этих страниц, то ты рискуешь
* либо быть посланным сюда;
* либо быть посланным далеко не сюда;
* либо остаться в игноре.
## Навигация
К твоим услугам:
1. в заголовке сайта — глобальный поиск;
2. над заголовком страницы — метки страницы (полный список ниже);
3. слева — общее содержание;
4. справа — содержание конкретной страницы.
??? info end "На мобильниках содержание страницы спрятано за этой кнопкой в боковом меню:"
![Скриншот бокового меню с мобильной версии](_assets/mobile-toc-btn.jpg)
@@ -1,4 +1,5 @@
--- ---
title: channels.json
icon: material/code-json icon: material/code-json
tags: ["iptvc", "теги", "каналы"] tags: ["iptvc", "теги", "каналы"]
--- ---
@@ -38,8 +39,7 @@ tags: ["iptvc", "теги", "каналы"]
Каналы сопоставляются в нижнем регистре. Каналы сопоставляются в нижнем регистре.
<a id="warnings"></a> ## Рекомендации и предостережения { id="warnings" }
## Рекомендации и предостережения
1. Если хочешь написать новое правило, будь осторожен с регулярками. 1. Если хочешь написать новое правило, будь осторожен с регулярками.
Старайся не охватывать несколько каналов сразу. Старайся не охватывать несколько каналов сразу.
@@ -52,14 +52,13 @@ tags: ["iptvc", "теги", "каналы"]
5. Один канал может быть назван по-разному. 5. Один канал может быть назван по-разному.
Например, `Ю` или `Ю!`, `Россия 1 +5` или `Россия-1`. Например, `Ю` или `Ю!`, `Россия 1 +5` или `Россия-1`.
6. Важно учитывать холдинги. 6. Важно учитывать холдинги.
Например, российские Матч, ТНТ и НТВ имеют множество разных каналов, и тупо искать `^нтв$` -- тупо. Например, российские Матч, ТНТ и НТВ имеют множество разных каналов, и тупо искать `^нтв$` тупо.
7. У всех каналов есть `title`. 7. У всех каналов есть `title`.
Не все каналы имеют `tvg-id`. Не все каналы имеют `tvg-id`.
Некоторые каналы имеют `tvg-name`. Некоторые каналы имеют `tvg-name`.
Все три параметра могут оказаться на кириллице. Все три параметра могут оказаться на кириллице.
<a id="доступные-теги"> ## Доступные теги { id="доступные-теги" }
## Доступные теги
| Ключевое слово | Описание | | Ключевое слово | Описание |
| -------------- | ----------------------------------------------------------- | | -------------- | ----------------------------------------------------------- |
@@ -1,4 +1,5 @@
--- ---
title: "*.m3u (*.m3u8)"
icon: material/playlist-play icon: material/playlist-play
tags: ["плейлисты", "каналы"] tags: ["плейлисты", "каналы"]
--- ---
@@ -15,6 +16,7 @@ tags: ["плейлисты", "каналы"]
После директив с новой строки указывается ссылка на канал (или путь к файлу). После директив с новой строки указывается ссылка на канал (или путь к файлу).
В свою чередь, по этой ссылке может быть: В свою чередь, по этой ссылке может быть:
* либо текстовое представление контента в формате m3u/m3u8/XMLTV/MPD с описанием непосредственно участки трансляции; * либо текстовое представление контента в формате m3u/m3u8/XMLTV/MPD с описанием непосредственно участки трансляции;
* либо непосредственно сама потоковая трансляция mp4 или т. п. * либо непосредственно сама потоковая трансляция mp4 или т. п.
@@ -39,78 +41,65 @@ http://example.com/play-bbc.m3u
http://example.com/play-am.m3u8 http://example.com/play-am.m3u8
``` ```
<a id="EXTM3U"></a> ### Директива `#EXTM3U` { id="EXTM3U" }
### Директива `#EXTM3U`
Заголовок файла (обязателен). Заголовок файла (обязателен).
<a id="url-tvg"></a> #### Атрибут `url-tvg` (он же `x-tvg-url`) { id="url-tvg" }
#### Атрибут `url-tvg` (он же `x-tvg-url`)
Ссылка программу передач в формате `*.xml` или `*.xml.gz`. Ссылка программу передач в формате `*.xml` или `*.xml.gz`.
<a id="catchup"></a> #### Атрибуты `catchup*` { id="catchup" }
#### Атрибуты `catchup*`
Читай здесь: [Архив телепрограмм (SS IPTV)](https://ss-iptv.com/ru/operators/catchup) Читай здесь: [Архив телепрограмм (SS IPTV)](https://ss-iptv.com/ru/operators/catchup)
<a id="EXTGRP"></a> ### Директива `#EXTGRP` { id="EXTGRP" }
### Директива `#EXTGRP`
Название группы, к которой относится контент (звуковая дорожка или канал). Название группы, к которой относится контент (звуковая дорожка или канал).
Указывается в формате `#EXTGRP:XXX`, где: `XXX` -- название группы. Указывается в формате `#EXTGRP:XXX`, где: `XXX` название группы.
<a id="EXTINF"></a> ### Директива `#EXTINF` { id="EXTINF" }
### Директива `#EXTINF`
Описывает контент (звуковую дорожку или канал). Описывает контент (звуковую дорожку или канал).
Указывается в формате `#EXTINF:XXX YYY,ZZZ`, где: Указывается в формате `#EXTINF:XXX YYY,ZZZ`, где:
* `XXX` -- длительность в секундах (обязательно, но может быть `-1` или `0`); * `XXX` длительность в секундах (обязательно, но может быть `-1` или `0`);
* `YYY` -- атрибуты (см. ниже); * `YYY` атрибуты (см. ниже);
* `ZZZ` -- название контента; * `ZZZ` название контента;
<a id="tvg-shift"></a> #### Атрибут `tvg-shift` { id="tvg-shift" }
#### Атрибут `tvg-shift`
Cмещение телепрограммы в часах относительно указанного в программе. Cмещение телепрограммы в часах относительно указанного в программе.
<a id="tvg-id"></a> #### Атрибуты `tvg-id` и `tvg-name` { id="tvg-id" }
<a id="tvg-name"></a> <a id="tvg-name"></a>
#### Атрибуты `tvg-id` и `tvg-name`
Идентификатор телепрограммы канала. Идентификатор телепрограммы канала.
По нему телепрограмма привязывается к трансляции с учётом смещения времени. По нему телепрограмма привязывается к трансляции с учётом смещения времени.
<a id="tvg-logo"></a> #### Атрибут `tvg-logo` { id="tvg-logo" }
#### Атрибут `tvg-logo`
Ссылка на логотип канала. Ссылка на логотип канала.
<a id="tvg-country"></a> #### Атрибут `tvg-country` { id="tvg-country" }
#### Атрибут `tvg-country`
Код Alpha-2 страны вещания согласно ISO 3166-1 или ОКСМ. Код Alpha-2 страны вещания согласно ISO 3166-1 или ОКСМ.
<a id="tvg-language"></a> #### Атрибут `tvg-language` { id="tvg-language" }
#### Атрибут `tvg-language`
Название языка телепередачи согласно ISO 639-2. Название языка телепередачи согласно ISO 639-2.
<a id="group-title"></a> #### Атрибут `group-title` { id="group-title" }
#### Атрибут `group-title`
Название группы. Название группы.
По функционалу идентичен директиве `#EXTGRP`. По функционалу идентичен директиве `#EXTGRP`.
<a id="user-agent"></a> #### Атрибут `user-agent` { id="user-agent" }
#### Атрибут `user-agent`
Значение заголовка `User-Agent` для обращения к контенту по http. Значение заголовка `User-Agent` для обращения к контенту по http.
<a id="audio-track"></a> #### Атрибут `audio-track` { id="audio-track" }
#### Атрибут `audio-track`
Языковой код (ISO 639-2) аудио дорожки канала, например: "eng,rus". Языковой код (ISO 639-2) аудио дорожки канала, например: "eng,rus".
@@ -118,22 +107,20 @@ Cмещение телепрограммы в часах относительн
Дорожкой по умолчанию устанавливается первая указанная в списке. Дорожкой по умолчанию устанавливается первая указанная в списке.
<a id="aspect-ratio"></a> #### Атрибут `aspect-ratio` { id="aspect-ratio" }
#### Атрибут `aspect-ratio`
Определяет пропорции экрана (может быть недоступно для некоторых моделей телевизоров). Определяет пропорции экрана (может быть недоступно для некоторых моделей телевизоров).
Допустимые значения: 16:9, 3:2, 4:3, 1,85:1, 2,39:1 (наиболее распространенное значение для фильмов) Допустимые значения: 16:9, 3:2, 4:3, 1,85:1, 2,39:1 (наиболее распространенное значение для фильмов)
<a id="EXTVLCOPT"></a> ### Директива `#EXTVLCOPT` { id="EXTVLCOPT" }
### Директива `#EXTVLCOPT`
Специфична для VLC Player. Специфична для VLC Player.
Директив может быть множество для одной дорожки (канала). Директив может быть множество для одной дорожки (канала).
Указывается в формате `#EXTVLCOPT:XXX` или `#EXTVLCOPT--XXX=YYY`, где: Указывается в формате `#EXTVLCOPT:XXX` или `#EXTVLCOPT--XXX=YYY`, где:
* `XXX` -- параметр командной строки VLC Player ([полный список](https://wiki.videolan.org/VLC_command-line_help/)); * `XXX` параметр командной строки VLC Player ([полный список](https://wiki.videolan.org/VLC_command-line_help/));
* `YYY` -- значения параметра. * `YYY` значения параметра.
## Дополнительные материалы ## Дополнительные материалы
@@ -1,7 +1,6 @@
--- ---
icon: material/file-code-outline icon: material/file-code-outline
hide: hide: [toc]
- toc
--- ---
# :material-file-code-outline: Форматы файлов # :material-file-code-outline: Форматы файлов
@@ -20,5 +19,5 @@ hide:
- [:material-playlist-play: Формат файлов `*.m3u` (`*.m3u8`)](m3u.md) - [:material-playlist-play: Формат файлов `*.m3u` (`*.m3u8`)](m3u.md)
--- ---
Плейлист -- это вообще что? Плейлист это вообще что?
</div> </div>
@@ -1,4 +1,5 @@
--- ---
title: playlists.ini
icon: material/code-brackets icon: material/code-brackets
tags: ["плейлисты"] tags: ["плейлисты"]
--- ---
@@ -23,7 +24,7 @@ src = 'https://example.com/super-duper-playlist'
Для значений можно (не) использовать 'одинарные' или "двойные" кавычки. Для значений можно (не) использовать 'одинарные' или "двойные" кавычки.
Ради единообразия рекомендуется использовать 'одинарные'. Ради единообразия рекомендуется использовать 'одинарные'.
## `code` ## `code` { id="code" }
Код плейлиста в рамках этого конфига (**обязательно**). Код плейлиста в рамках этого конфига (**обязательно**).
@@ -35,23 +36,23 @@ src = 'https://example.com/super-duper-playlist'
Для удобства ввода с пульта, код рекомендуется задавать числом или короткой строкой без пробелов и др. спецсимволов. Для удобства ввода с пульта, код рекомендуется задавать числом или короткой строкой без пробелов и др. спецсимволов.
Чем короче, тем лучше. Чем короче, тем лучше.
## `name` ## `name` { id="name" }
Название плейлиста (необязательно). Название плейлиста (необязательно).
По умолчанию: `Playlist #<code>`. По умолчанию: `Playlist #<code>`.
## `desc` ## `desc` { id="desc" }
Краткое описание из источника или от себя (необязательно). Краткое описание из источника или от себя (необязательно).
По умолчанию: пусто. По умолчанию: пусто.
## `pls` ## `pls` { id="pls" }
Прямая ссылка на m3u/m3u8 плейлист (**обязательно**). Прямая ссылка на m3u/m3u8 плейлист (**обязательно**).
## `src` ## `src` { id="src" }
Ссылка на источник (страницу сайта), откуда был взят плейлист (необязательно). Ссылка на источник (страницу сайта), откуда был взят плейлист (необязательно).
+16
View File
@@ -0,0 +1,16 @@
---
icon: material/file-document
hide: [toc]
---
# Общая информация
<div class="grid cards" markdown>
- [:material-cogs: Как работает сервис](../aggregator/overview.md)
- [:material-file-eye-outline: Как отбираются плейлисты](../aggregator/selection.md)
- [:material-file-refresh-outline: Проверки и статусы](../aggregator/checks.md)
- [:fontawesome-solid-list-check: Список плейлистов](../iptvc/site/list.md)
- [:material-table-eye: Страница плейлиста](../iptvc/site/details.md)
- [:material-television-play: Как подключить плейлист](../iptvc/site/connect.md)
- [:material-multimedia: IPTV плееры](players.md)
</div>
@@ -9,13 +9,13 @@ tags: ["плееры"]
В списке ниже только те плееры, которые широко известны и популярны у зрителей IPTV, а также рекомендуются специализированными сайтами. В списке ниже только те плееры, которые широко известны и популярны у зрителей IPTV, а также рекомендуются специализированными сайтами.
Некоторые из них помечены значком :thumbsup: -- значит, он уже зарекомендовал себя как стабильный и удобный, с ним меньше всего хлопот. Некоторые из них помечены значком :thumbsup: значит, он уже зарекомендовал себя как стабильный и удобный, с ним меньше всего хлопот.
Список для удобства разбит по платформам и ОС. Список для удобства разбит по платформам и ОС.
Обращайся к содержанию справа для быстрой навигации. Обращайся к содержанию справа для быстрой навигации.
!!! info "Здесь не хватает очень много подробностей" !!! info "Здесь не хватает очень много подробностей"
Если ты имел дело с каким-то плеером, знаешь как его настроить или какие-то другие детали, я прошу тебя помочь [актуализировать эту страницу](../support.md#participate), чтобы через это помочь другим пользователям с выбором и настройкой плеера под свои цели. Если ты имел дело с каким-то плеером, знаешь как его настроить или какие-то другие детали, я прошу тебя помочь [актуализировать эту страницу](support.md#participate), чтобы через это помочь другим пользователям с выбором и настройкой плеера под свои цели.
## Кроссплатформенные ## Кроссплатформенные
@@ -32,18 +32,18 @@ tags: ["плееры"]
Универсальный плеер практически для любого мультимедиа-контента. Универсальный плеер практически для любого мультимедиа-контента.
??? quote "[Скриншот] Главное окно" ??? image "Главное окно"
![](../assets/img/players/vlc/main.jpg) ![](_assets/players/vlc/main.jpg)
??? quote "[Скриншот] Добавление плейлиста на десктопе" ??? image "Добавление плейлиста на десктопе"
!!! warning "Указание протокола `https://` обязательно!" !!! warning "Указание протокола `https://` обязательно!"
![](../assets/img/players/vlc/add1.jpg) ![](_assets/players/vlc/add1.jpg)
![](../assets/img/players/vlc/add2.jpg) ![](_assets/players/vlc/add2.jpg)
??? quote "[Скриншот] Добавление плейлиста на андроиде" ??? image "Добавление плейлиста на андроиде"
!!! warning "Указание протокола `https://` обязательно!" !!! warning "Указание протокола `https://` обязательно!"
![](../assets/img/players/vlc/add1-mob.jpg) ![](_assets/players/vlc/add1-mob.jpg)
![](../assets/img/players/vlc/add2-mob.jpg) ![](_assets/players/vlc/add2-mob.jpg)
### :thumbsup: IPTVnator ### :thumbsup: IPTVnator
@@ -57,13 +57,13 @@ tags: ["плееры"]
Если использовать веб-версию, то настройки сохраняются в браузере. Если использовать веб-версию, то настройки сохраняются в браузере.
??? quote "[Скриншот] Главное окно" ??? image "Главное окно"
![](../assets/img/players/iptvnator/main.jpg) ![](_assets/players/iptvnator/main.jpg)
??? quote "[Скриншот] Добавление плейлиста" ??? image "Добавление плейлиста"
!!! warning "Указание протокола `https://` обязательно!" !!! warning "Указание протокола `https://` обязательно!"
![](../assets/img/players/iptvnator/add1.jpg) ![](_assets/players/iptvnator/add1.jpg)
![](../assets/img/players/iptvnator/add2.jpg) ![](_assets/players/iptvnator/add2.jpg)
### IPTV Web Player ### IPTV Web Player
@@ -71,15 +71,15 @@ tags: ["плееры"]
Простой и удобный веб-плеер. Простой и удобный веб-плеер.
Загрузка плейлиста по ссылкам или из файла, но только одного. Загрузка плейлиста по ссылкам или из файла, но только одного.
В качестве тестового плейлиста всем известный (m3u.su/sh)[https://m3u.su/sh/details].
Подгрузка и отображение телепрограммы (используется https://cdn.epg.one/epg2.xml). Подгрузка и отображение телепрограммы (используется https://cdn.epg.one/epg2.xml).
??? quote "[Скриншот] Главное окно" ??? image "Главное окно"
![](../assets/img/players/iptv-web-player/main.jpg) ![](_assets/players/iptv-web-player/main.jpg)
??? quote "[Скриншот] Добавление плейлиста" ??? image "Добавление плейлиста"
!!! success "Указание протокола `https://` необязательно!" !!! success "Указание протокола `https://` необязательно!"
![](../assets/img/players/iptv-web-player/add.jpg) ![](_assets/players/iptv-web-player/add.jpg)
### Kodi ### Kodi
@@ -135,12 +135,12 @@ tags: ["плееры"]
Поддерживает плейлисты по ссылкам, сторонние телепрограммы, группировку каналов, изменение плейлистов и многое другое. Поддерживает плейлисты по ссылкам, сторонние телепрограммы, группировку каналов, изменение плейлистов и многое другое.
??? quote "[Скриншот] Главное окно" ??? image "Главное окно"
![](../assets/img/players/yuki-iptv/main.jpg) ![](_assets/players/yuki-iptv/main.jpg)
??? quote "[Скриншот] Добавление плейлиста" ??? image "Добавление плейлиста"
!!! warning "Указание протокола `https://` обязательно!" !!! warning "Указание протокола `https://` обязательно!"
![](../assets/img/players/yuki-iptv/add.jpg) ![](_assets/players/yuki-iptv/add.jpg)
--- ---
@@ -173,25 +173,25 @@ tags: ["плееры"]
* Скачать: [play.google.com](https://play.google.com/store/apps/details?id=com.ottplay.ottplay) * Скачать: [play.google.com](https://play.google.com/store/apps/details?id=com.ottplay.ottplay)
??? quote "[Скриншот] Главный экран" ??? image "Главный экран"
![](../assets/img/players/televizo/main1.jpg) ![](_assets/players/televizo/main1.jpg)
![](../assets/img/players/televizo/main2.jpg) ![](_assets/players/televizo/main2.jpg)
??? quote "[Скриншот] Добавление плейлиста" ??? image "Добавление плейлиста"
!!! warning "Указание протокола `https://` обязательно!" !!! warning "Указание протокола `https://` обязательно!"
![](../assets/img/players/televizo/add1.jpg) ![](_assets/players/televizo/add1.jpg)
![](../assets/img/players/televizo/add2.jpg) ![](_assets/players/televizo/add2.jpg)
Из настроек: Из настроек:
![](../assets/img/players/televizo/add21.jpg) ![](_assets/players/televizo/add21.jpg)
![](../assets/img/players/televizo/add22.jpg) ![](_assets/players/televizo/add22.jpg)
И дальше те же шаги 3-5 на скриншотах выше. И дальше те же шаги 3-5 на скриншотах выше.
??? quote "Установка User-Agent" ??? quote "Установка User-Agent"
На экране добавления/редактирования плейлиста снять галочку "User-Agent по умолчанию" и ввести необходимый. На экране добавления/редактирования плейлиста снять галочку "User-Agent по умолчанию" и ввести необходимый.
Например, для [Wink](../faq.md#wink). Например, для [Wink](../aggregator/faq.md#wink).
#### :thumbsup: M3U #### :thumbsup: M3U
@@ -199,19 +199,19 @@ tags: ["плееры"]
Умеет показывать картинку-в-картинке, отображать группы и сортировать каналы. Умеет показывать картинку-в-картинке, отображать группы и сортировать каналы.
Программу передач нужно [подключать отдельной ссылкой](../faq.md#epg), из плейлиста не тянет. Программу передач нужно [подключать отдельной ссылкой](../aggregator/faq.md#epg), из плейлиста не тянет.
??? quote "[Скриншот] Главный экран" ??? image "Главный экран"
![](../assets/img/players/m3u/main.jpg) ![](_assets/players/m3u/main.jpg)
??? quote "[Скриншот] Добавление плейлиста" ??? image "Добавление плейлиста"
!!! warning "Указание протокола `https://` обязательно!" !!! warning "Указание протокола `https://` обязательно!"
![](../assets/img/players/m3u/add1.jpg) ![](_assets/players/m3u/add1.jpg)
![](../assets/img/players/m3u/add2.jpg) ![](_assets/players/m3u/add2.jpg)
??? quote "[Скриншот] Установка User-Agent" ??? image "Установка User-Agent"
![](../assets/img/players/m3u/ua1.jpg) ![](_assets/players/m3u/ua1.jpg)
![](../assets/img/players/m3u/ua2.jpg) ![](_assets/players/m3u/ua2.jpg)
#### IPTV (Александр Софронов) #### IPTV (Александр Софронов)
+59
View File
@@ -0,0 +1,59 @@
---
icon: material/hand-heart-outline
---
# :material-hand-heart-outline: Поддержка проекта
Проект держится только на сугубо техническом интересе одного разработчика в свободное от работы время.
Проект сознательно не монетизируется: это неправильно по отношению к пользователям и правообладателям.
Ниже перечислены минимально доступные вам способы — от самых простых к более сложным.
## :simple-telegram: Подписаться в Telegram
У проекта есть два публичных ресурса для прямой связи с пользователями.
Там можно ставить **платные реакции** к постам и/или **дарить голоса** (бусты):
* канал: [@iptv_aggregator](https://t.me/iptv_aggregator) ([boost](https://t.me/iptv_aggregator?boost)) — в нём новости о проекте (общие объявления и проведённые доработки);
* чат: [@iptv_aggregator_chat](../tg/chat.md) ([boost](https://t.me/iptv_aggregator_chat?boost)) — комментарии к каналу, общение по теме проекта и IPTV.
## :material-wallet: Внести пожертвование
Вы можете внести прямое денежное **пожертвование** с банковской карты на виртуальный кошелёк ЮMoney:
!!! yoomoney "[yoomoney.ru/to/41001685237530](https://yoomoney.ru/to/41001685237530)"
Разовый платёж, без подписок, на любую сумму.
Также вы можете оформить подписку на Boosty:
!!! boosty "[boosty.to/anthonyaxenov](https://boosty.to/anthonyaxenov)"
Разовый платёж или платная подписка.
Пожертвования добровольны.
Они не дают права на эксклюзивный доступ к чему-либо и не рассматриваются как способ обогащения.
Это лишь попытка компенсировать затраты на содержание проекта.
На пожертвования [был приобретён](https://t.me/iptv_aggregator/30) домен `m3u.su`, который сейчас используется в качестве основного адреса.
## :simple-git: Принять участие в разработке { id="participate" }
Весь исходный код проекта хранится в репозиториях организации: [git.axenov.dev/IPTV](https://git.axenov.dev/IPTV)
Чтобы принять участие в разработке, необходимо [зарегистрироваться на сайте git.axenov.dev](https://git.axenov.dev/user/sign_up) и **активировать** учётную запись по e-mail.
!!! info "Это бесплатно, но неактивированные учётки периодически удаляются."
### :octicons-issue-opened-16: Создать задачу
Любое ПО неидеально, как и документация к нему.
Если вы нашли ошибку, опечатку, неожиданное поведение ПО или есть предложение по улучшению — можете создать задачу в соответствующем репозитории организации.
### :octicons-git-pull-request-16: Прислать изменения
Вы можете внести исправления в код самостоятельно и прислать pull-request для принятия в основную ветку.
Это может быть новый функционал, исправления ошибок или опечаток.
Если есть идеи и желание для расширения функционала проекта, можем обсудить создание нового репозитория с выдачей необходимых прав.
+71
View File
@@ -0,0 +1,71 @@
---
title: Добро пожаловать!
template: landing.html
hide: [navigation, toc]
hero:
title: Агрегатор плейлистов
subtitle: Бесплатный сервис для сбора публичных IPTV-плейлистов с автоматической проверкой доступности каналов
buttons:
- text: О сервисе
url: aggregator/overview.html
primary: true
- text: Открыть сайт
url: https://m3u.su
primary: false
---
Ниже представлена информация о проекте по разделам.
Для навигации используйте
<div class="grid cards" markdown>
- :material-home:{ .lg .middle } **Агрегатор m3u.su**
---
Что это за сервис и как он работает?
Основные принципы и цель проекта.
[Подробнее :octicons-arrow-right-24:](aggregator/overview.md){ .md-button .md-button--primary }
- :material-console:{ .lg .middle } **iptvc**
---
CLI-утилита для запуска веб-интерфейса и проверки IPTV-плейлистов: установка, команды, конфигурация
[Подробнее :octicons-arrow-right-24:](iptvc/overview.md){ .md-button .md-button--primary }
- :material-web:{ .lg .middle } **Запуск сайта**
---
Создай собственный агрегатор плейлистов на базе iptvc, используй его в собственной домашней сети
[Подробнее :octicons-arrow-right-24:](iptvc/site/first-steps.md){ .md-button .md-button--primary }
- :material-file-cog:{ .lg .middle } **Конфигурация**
---
Полное описание `config.yml`, переменных окружения и CLI-флагов
[Подробнее :octicons-arrow-right-24:](common/config/config.md){ .md-button .md-button--primary }
- :material-book-open-page-variant-outline:{ .lg .middle } **Об этой документации**
---
Подборка плееров для всех платформ — найдите и настройте плеер для себя
[Подробнее :octicons-arrow-right-24:](common/players.md){ .md-button .md-button--primary }
- :material-hand-heart:{ .lg .middle } **Поддержка проекта**
---
Проекту важна поддержка, и вот как вы можете принять участие
[Подробнее :octicons-arrow-right-24:](common/support.md){ .md-button .md-button--primary }
</div>
+384
View File
@@ -0,0 +1,384 @@
---
title: check
tags: [iptvc]
---
# Команда `check`
Команда поддерживает множество аргументов для разных целей.
Они могут дополнять друг друга.
Порядок аргументов не имеет значения.
## `-i`, `--ini` { id=ini }
Указывает путь к локальному [ini-файлу](../../common/formats/playlists.md) с описанием плейлистов.
Можно указать только однажды.
Значение по умолчанию: `./playlists.ini`
Если файл не найден, проверка плейлистов будет доступна только по ссылкам ([`--url`](#url)) или из локальных файлов ([`--file`](#file)).
```shell title="Пример"
./iptvc check -i ~/my.ini
```
## `-t`, `--tags` { id=tags }
Указывает путь к локальному [json-файлу](../../common/formats/channels.md) с описанием тегов каналов.
Можно указать только однажды.
Значение по умолчанию: `./channels.json`
Если файл не найден, то будет выведено предупреждение о том, что каналы не будут помечены тегами.
```shell title="Пример"
./iptvc check -t ~/tags.json
```
## `-f`, `--file` { id=file }
Указывает путь к локальному файлу плейлиста `*.m3u`/`*.m3u8`.
Можно указать несколько разных.
```shell title="Пример"
./iptvc check -f playlist.m3u
./iptvc check -f playlist1.m3u --file playlist2.m3u8
./iptvc check --file /path/to/playlist.m3u
```
## `-u`, `--url` { id=url }
Указывает URL удалённого плейлиста (поддерживаются протоколы http/https).
Можно указать несколько разных.
```shell title="Пример"
./iptvc check -u http://example.com/playlist.m3u
./iptvc check -u https://site.com/playlist.m3u8 --url http://other.com/list.m3u
```
## `-c`, `--code` { id=code }
Указывает код плейлиста из файла [playlists.ini](../../common/formats/playlists.md).
Можно указать несколько разных.
!!! warning "Работает только вместе с [`--ini`](#ini)."
Если не указан ни разу, то будут проверены все плейлисты, которые указаны в ini-файле.
Если используется кеширование, то проверенные плейлисты (результаты проверки которых ещё находятся в кеше) проверяться не будут.
```shell title="Пример"
./iptvc check -i ~/my.ini -c RU_BASIC --code MOVIE_PREMIUM
```
## `--repeat` { id=repeat }
Указывает количество повторений (итераций) команды.
Значение по умолчанию: `1`
Если указано `0`, тогда:
* повторение будет бесконечным;
* если переданы [`--url`](#url), [`--file`](#file) или [`--code`](#code), то на каждой итерации будут проверяться только указанные плейлисты;
* если не переданы [`--url`](#url), [`--file`](#file) или [`--code`](#code), то на каждой итерации список плейлистов будет подготавливаться заново.
Если при этом используется кеширование, то проверенные плейлисты (результаты проверки которых ещё находятся в кеше) проверяться не будут.
```shell title="Пример"
# проверить 5 раз плейлисты с кодами xx и yy из my.ini
./iptvc check -i ~/my.ini -c xx --code yy --repeat 5
# бесконечно проверять все плейлисты из my.ini, без учёта проверенных
./iptvc check -i ~/my.ini --repeat 0
# бесконечно проверять плейлист из файла
./iptvc check -f test.m3u --repeat 0
```
## `--playlists-all-cooldown` { id=playlists-all-cooldown }
Указывает паузу между полными циклами проверки в секундах. Параметр переопределяет `check.playlists.all-cooldown` из конфигурации.
Пауза применяется после завершения полного цикла и перед началом следующего. Внутри цикла этот параметр не используется: для задержки между плейлистами применяется [`check.playlists.one-cooldown`](#playlists-one-cooldown).
Значение по умолчанию: значение `check.playlists.all-cooldown` из конфигурации, обычно `1800` (30 минут).
```shell title="Пример"
# проверить 5 раз с паузой 5 секунд между циклами
./iptvc check -i ~/my.ini -c xx --code yy --repeat 5 --playlists-all-cooldown 5
# бесконечно проверять все плейлисты из my.ini каждый час
./iptvc check -i ~/my.ini --repeat 0 --playlists-all-cooldown 3600
# бесконечно проверять плейлист из файла с паузой 10 секунд
./iptvc check -f test.m3u --repeat 0 --playlists-all-cooldown 10
```
## `-r`, `--random` { id=random }
Указывает максимальное количество случайных плейлистов из ini-файла для проверки.
!!! warning "Работает только вместе с [`--ini`](#ini)."
Если не указан ни разу, то будут проверены все плейлисты, которые указаны в ini-файле.
Если используется кеширование, то проверенные плейлисты (результаты проверки которых ещё находятся в кеше) проверяться не будут.
```shell title="Пример"
./iptvc check -i ~/my.ini -r 10
```
## `-j`, `--json` { id=json }
Если указано, то подробные результаты проверки будут выводиться в формате JSON.
```shell title="Пример"
./iptvc check -f playlist.m3u --json
```
## `-q`, `--quiet` { id=quiet }
Подавляет вывод всех логов.
!!! info "Не влияет на [`--json`](#json) (JSON-данные будут выведены в stdout), но перекрывает [`--verbose`](#verbose) (логов не будет вовсе, независимо от повышенной подробности)."
```shell title="Пример"
./iptvc check -i ~/my.ini --random 10 --quiet --json
```
## `-v`, `--verbose` { id=verbose }
Включает подробное логирование.
```shell title="Пример"
./iptvc check --random 10 --verbose
```
## Глобальные флаги { id=global }
Эти флаги доступны для всех команд и переопределяют значения из `config.yml`.
### `--debug` { id=debug }
Включает режим отладки. Переопределяет `app.debug` из `config.yml` и переменную `APP_DEBUG`.
```shell title="Пример"
./iptvc check -i ~/my.ini --debug
```
### `--log-level` { id=log-level }
Устанавливает уровень логирования. Переопределяет `app.log_level` из `config.yml`.
Доступные значения: `debug`, `info`, `warn`, `error`.
```shell title="Пример"
./iptvc check -i ~/my.ini --log-level debug
```
## Флаги проверки плейлистов { id=check-playlists }
Эти флаги переопределяют параметры секции `check.playlists` из `config.yml`. Доступны для команд `check` и `serve`.
### `--playlists-timeout` { id=playlists-timeout }
Таймаут HTTP-запроса плейлиста в секундах.
Переопределяет `check.playlists.timeout` (по умолчанию `10`).
```shell title="Пример"
./iptvc check -i ~/my.ini --playlists-timeout 5
```
### `--playlists-all-cooldown` { id=playlists-all-cooldown }
Задержка в секундах после проверки всех плейлистов.
Переопределяет `check.playlists.all-cooldown` (по умолчанию `1800`).
```shell title="Пример"
./iptvc check -i ~/my.ini --playlists-all-cooldown 10
```
### `--playlists-one-cooldown` { id=playlists-one-cooldown }
Задержка в секундах после проверки каждого плейлиста.
Переопределяет `check.playlists.one-cooldown` (по умолчанию `2`).
```shell title="Пример"
./iptvc check -i ~/my.ini --playlists-one-cooldown 2
```
### `--playlists-max-routines` { id=playlists-max-routines }
Максимум одновременно проверяемых плейлистов.
Переопределяет `check.playlists.max-routines` (по умолчанию `1`).
```shell title="Пример"
./iptvc check -i ~/my.ini --playlists-max-routines 10
```
### `--playlists-per-routine` { id=playlists-per-routine }
Количество плейлистов на одну процедуру проверки.
Переопределяет `check.playlists.per-routine` (по умолчанию `1`).
```shell title="Пример"
./iptvc check -i ~/my.ini --playlists-per-routine 3
```
### `--playlists-user-agent` { id=playlists-user-agent }
User-Agent для HTTP-запросов плейлистов. Можно указать несколько — будет выбран случайный при каждом запросе.
Переопределяет `check.playlists.user-agent`.
```shell title="Пример"
./iptvc check -i ~/my.ini --playlists-user-agent "Mozilla/5.0" "curl/8.0"
```
## Флаги проверки каналов { id=check-channels }
Эти флаги переопределяют параметры секции `check.channels` из `config.yml`. Доступны для команд `check` и `serve`.
### `--channels-timeout` { id=channels-timeout }
Таймаут HTTP-запроса канала в секундах.
Переопределяет `check.channels.timeout` (по умолчанию `10`).
```shell title="Пример"
./iptvc check -i ~/my.ini --channels-timeout 5
```
### `--channels-byte-range` { id=channels-byte-range }
Объём данных в байтах для загрузки от сервера при проверке канала.
Переопределяет `check.channels.byte-range` (по умолчанию `512`).
```shell title="Пример"
./iptvc check -i ~/my.ini --channels-byte-range 1024
```
### `--channels-cooldown` { id=channels-cooldown }
Задержка в секундах после проверки каждого канала.
Переопределяет `check.channels.cooldown` (по умолчанию `0`).
```shell title="Пример"
./iptvc check -i ~/my.ini --channels-cooldown 1
```
### `--channels-max-routines` { id=channels-max-routines }
Максимум одновременно проверяемых каналов.
Переопределяет `check.channels.max-routines` (по умолчанию `50`).
```shell title="Пример"
./iptvc check -i ~/my.ini --channels-max-routines 100
```
### `--channels-per-routine` { id=channels-per-routine }
Количество каналов на одну процедуру проверки.
Переопределяет `check.channels.per-routine` (по умолчанию `10`).
```shell title="Пример"
./iptvc check -i ~/my.ini --channels-per-routine 20
```
### `--channels-user-agent` { id=channels-user-agent }
User-Agent для HTTP-запросов каналов. Можно указать несколько — будет выбран случайный при каждом запросе.
Переопределяет `check.channels.user-agent`.
```shell title="Пример"
./iptvc check -i ~/my.ini --channels-user-agent "Mozilla/5.0" "VLC/3.0"
```
## Флаги кеша { id=cache-flags }
Эти флаги переопределяют параметры секции `cache` из `config.yml`. Доступны для команд `check` и `serve`.
### `--cache-enabled` { id=cache-enabled }
Включает кеширование результатов в KeyDB/Redis.
Переопределяет `cache.enabled` (по умолчанию `false`).
```shell title="Пример"
./iptvc check -i ~/my.ini --cache-enabled
```
### `--cache-host` { id=cache-host }
Хост KeyDB/Redis.
Переопределяет `cache.host` (по умолчанию `localhost`).
```shell title="Пример"
./iptvc check -i ~/my.ini --cache-enabled --cache-host 192.168.1.10
```
### `--cache-port` { id=cache-port }
Порт KeyDB/Redis.
Переопределяет `cache.port` (по умолчанию `6379`).
```shell title="Пример"
./iptvc check -i ~/my.ini --cache-enabled --cache-port 6380
```
### `--cache-username` { id=cache-username }
Логин для подключения к KeyDB/Redis.
Переопределяет `cache.username`.
```shell title="Пример"
./iptvc check -i ~/my.ini --cache-enabled --cache-username myuser
```
### `--cache-password` { id=cache-password }
Пароль для подключения к KeyDB/Redis.
Переопределяет `cache.password`.
```shell title="Пример"
./iptvc check -i ~/my.ini --cache-enabled --cache-password secret
```
### `--cache-db` { id=cache-db }
Номер базы данных KeyDB/Redis.
Переопределяет `cache.db` (по умолчанию `0`).
```shell title="Пример"
./iptvc check -i ~/my.ini --cache-enabled --cache-db 2
```
### `--cache-ttl` { id=cache-ttl }
TTL записей кеша в секундах.
Переопределяет `cache.ttl` (по умолчанию `30`).
```shell title="Пример"
./iptvc check -i ~/my.ini --cache-enabled --cache-ttl 3600
```
@@ -1,5 +1,6 @@
--- ---
tags: ["iptvc"] title: help
tags: [iptvc]
--- ---
# Команда `help` # Команда `help`
@@ -27,11 +28,15 @@ Available Commands:
check Check playlists check Check playlists
completion Generate the autocompletion script for the specified shell completion Generate the autocompletion script for the specified shell
help Help about any command help Help about any command
serve Start web interface
version Show version version Show version
Flags: Flags:
-h, --help help for iptvc --config string path to config file (default "config.yml")
-v, --verbose enable additional output --debug enable debug mode (overrides config.yml)
-h, --help help for iptvc
--log-level string log level: debug, info, warn, error (overrides config.yml)
-v, --verbose enable additional output
Use "iptvc [command] --help" for more information about a command. Use "iptvc [command] --help" for more information about a command.
``` ```
+23
View File
@@ -0,0 +1,23 @@
---
icon: octicons/terminal-24
hide: [toc]
---
# :octicons-terminal-24: Справочник команд
* [`check`](check.md) — проверка плейлистов
* [`serve`](serve.md) — запуск веб-интерфейса
* [`version`](version.md) — получение версии и выход
* [`help`](help.md) — получение справки о программе и выход
Каждая команда отвечает за конкретную операцию и имеет свои настройки (аргументы), которыми можно влиять на логику выполнения операции.
Также есть глобальные аргументы, которые доступны для всех команд:
| Флаг | Тип | Соответствует в `config.yml` | Описание |
| ----------------- | ------ | ---------------------------- | ----------------------------------------------------- |
| `--config` | string | — | Путь к файлу конфигурации (по умолчанию `config.yml`) |
| `--debug` | bool | `app.debug` | Включить режим отладки |
| `--log-level` | string | `app.log_level` | Уровень логирования: `debug`, `info`, `warn`, `error` |
| `-v`, `--verbose` | bool | — | Подробное логирование |
+384
View File
@@ -0,0 +1,384 @@
---
title: serve
tags: [iptvc]
---
# Команда `serve`
Запускает встроенный веб-сервер для просмотра плейлистов и результатов их проверки в браузере.
```bash
iptvc serve [flags]
```
## Веб-сервер
### `-p`, `--port` { id="port" }
Порт для веб-сервера.
Переопределяет `server.port` из `config.yml` и переменную `SERVER_PORT`.
Если не указан, используется значение из `config.yml` (по умолчанию `8800`).
```bash
iptvc serve -p 3000
```
### `--host` { id="host" }
Хост для привязки веб-сервера.
Переопределяет `server.host` из `config.yml` и переменную `SERVER_HOST`.
Если не указан, используется значение из `config.yml` (по умолчанию — все интерфейсы).
```bash
iptvc serve --host 127.0.0.1
```
## Фоновая проверка
### `--check` { id="check" }
Включает фоновую проверку плейлистов. По умолчанию выключена.
Без этого флага веб-сервер работает standalone — отображает данные из кеша (если включён) или статус `unknown` для всех плейлистов.
```bash
iptvc serve --check
```
При `--check` доступны следующие флаги:
### `-i`, `--ini` { id="ini" }
Путь к локальному [ini-файлу](../../common/formats/playlists.md) с описанием плейлистов.
Значение по умолчанию: `./playlists.ini`
```bash
iptvc serve --check -i ~/my.ini
```
### `-t`, `--tags` { id="tags" }
Путь к [json-файлу](../../common/formats/channels.md) с описанием тегов каналов.
Значение по умолчанию: `./channels.json`
```bash
iptvc serve --check -t ~/tags.json
```
### `--playlists-all-cooldown` { id=playlists-all-cooldown }
Пауза между полными циклами фоновой проверки в секундах. Параметр переопределяет `check.playlists.all-cooldown` из конфигурации.
Значение по умолчанию: значение `check.playlists.all-cooldown` из конфигурации, обычно `1800` (30 минут).
```bash
# пауза 2 минуты между циклами
iptvc serve --check --playlists-all-cooldown 120
```
### `--repeat` { id="repeat" }
Количество циклов фоновой проверки.
Значение по умолчанию: `0` (бесконечно)
```bash
# проверить один раз и остановить фоновую проверку
iptvc serve --check --repeat 1
```
### `-r`, `--random` { id="random" }
Максимальное количество случайных плейлистов из ini-файла для проверки.
```bash
iptvc serve --check -r 10
```
## Глобальные флаги
### `--config` { id="config" }
Путь к файлу конфигурации `config.yml`.
Значение по умолчанию: `config.yml`
```bash
iptvc serve --config /etc/iptvc/config.yml
```
### `--debug` { id="debug" }
Включает режим отладки. Переопределяет `app.debug` из `config.yml` и переменную `APP_DEBUG`.
```bash
iptvc serve --debug
```
### `--log-level` { id="log-level" }
Устанавливает уровень логирования. Переопределяет `app.log_level` из `config.yml`.
Доступные значения: `debug`, `info`, `warn`, `error`.
```bash
iptvc serve --log-level debug
```
### `-v`, `--verbose` { id="verbose" }
Включает подробное логирование.
## Флаги проверки плейлистов
Эти флаги переопределяют параметры секции `check.playlists` из `config.yml`. Доступны для команд `check` и `serve`. Имеют смысл только при включённой фоновой проверке (`--check` или `check.start-on-serve: true`).
### `--playlists-timeout` { id="playlists-timeout" }
Таймаут HTTP-запроса плейлиста в секундах.
Переопределяет `check.playlists.timeout` (по умолчанию `10`).
```bash
iptvc serve --check --playlists-timeout 5
```
### `--playlists-all-cooldown` { id="playlists-all-cooldown" }
Задержка в секундах после проверки всех плейлистов.
Переопределяет `check.playlists.all-cooldown` (по умолчанию `1800`).
```bash
iptvc serve --check --playlists-all-cooldown 10
```
### `--playlists-one-cooldown` { id="playlists-one-cooldown" }
Задержка в секундах после проверки каждого плейлиста.
Переопределяет `check.playlists.one-cooldown` (по умолчанию `2`).
```bash
iptvc serve --check --playlists-one-cooldown 2
```
### `--playlists-max-routines` { id="playlists-max-routines" }
Максимум одновременно проверяемых плейлистов.
Переопределяет `check.playlists.max-routines` (по умолчанию `1`).
```bash
iptvc serve --check --playlists-max-routines 10
```
### `--playlists-per-routine` { id="playlists-per-routine" }
Количество плейлистов на одну процедуру проверки.
Переопределяет `check.playlists.per-routine` (по умолчанию `1`).
```bash
iptvc serve --check --playlists-per-routine 3
```
### `--playlists-user-agent` { id="playlists-user-agent" }
User-Agent для HTTP-запросов плейлистов. Можно указать несколько — будет выбран случайный при каждом запросе.
Переопределяет `check.playlists.user-agent`.
```bash
iptvc serve --check --playlists-user-agent "Mozilla/5.0" "curl/8.0"
```
## Флаги проверки каналов
Эти флаги переопределяют параметры секции `check.channels` из `config.yml`. Доступны для команд `check` и `serve`. Имеют смысл только при включённой фоновой проверке.
### `--channels-timeout` { id="channels-timeout" }
Таймаут HTTP-запроса канала в секундах.
Переопределяет `check.channels.timeout` (по умолчанию `10`).
```bash
iptvc serve --check --channels-timeout 8
```
### `--channels-byte-range` { id="channels-byte-range" }
Объём данных в байтах для загрузки от сервера при проверке канала.
Переопределяет `check.channels.byte-range` (по умолчанию `512`).
```bash
iptvc serve --check --channels-byte-range 1024
```
### `--channels-cooldown` { id="channels-cooldown" }
Задержка в секундах после проверки каждого канала.
Переопределяет `check.channels.cooldown` (по умолчанию `0`).
```bash
iptvc serve --check --channels-cooldown 1
```
### `--channels-max-routines` { id="channels-max-routines" }
Максимум одновременно проверяемых каналов.
Переопределяет `check.channels.max-routines` (по умолчанию `50`).
```bash
iptvc serve --check --channels-max-routines 100
```
### `--channels-per-routine` { id="channels-per-routine" }
Количество каналов на одну процедуру проверки.
Переопределяет `check.channels.per-routine` (по умолчанию `10`).
```bash
iptvc serve --check --channels-per-routine 20
```
### `--channels-user-agent` { id="channels-user-agent" }
User-Agent для HTTP-запросов каналов. Можно указать несколько — будет выбран случайный при каждом запросе.
Переопределяет `check.channels.user-agent`.
```bash
iptvc serve --check --channels-user-agent "Mozilla/5.0" "VLC/3.0"
```
## Флаги кеша
Эти флаги переопределяют параметры секции `cache` из `config.yml`. Доступны для команд `check` и `serve`.
### `--cache-enabled` { id="cache-enabled" }
Включает кеширование результатов в KeyDB/Redis.
Переопределяет `cache.enabled` (по умолчанию `false`).
```bash
iptvc serve --cache-enabled
```
### `--cache-host` { id="cache-host" }
Хост KeyDB/Redis.
Переопределяет `cache.host` (по умолчанию `localhost`).
```bash
iptvc serve --cache-enabled --cache-host 192.168.1.10
```
### `--cache-port` { id="cache-port" }
Порт KeyDB/Redis.
Переопределяет `cache.port` (по умолчанию `6379`).
```bash
iptvc serve --cache-enabled --cache-port 6380
```
### `--cache-username` { id="cache-username" }
Логин для подключения к KeyDB/Redis.
Переопределяет `cache.username`.
```bash
iptvc serve --cache-enabled --cache-username myuser
```
### `--cache-password` { id="cache-password" }
Пароль для подключения к KeyDB/Redis.
Переопределяет `cache.password`.
```bash
iptvc serve --cache-enabled --cache-password secret
```
### `--cache-db` { id="cache-db" }
Номер базы данных KeyDB/Redis.
Переопределяет `cache.db` (по умолчанию `0`).
```bash
iptvc serve --cache-enabled --cache-db 2
```
### `--cache-ttl` { id="cache-ttl" }
TTL записей кеша в секундах.
Переопределяет `cache.ttl` (по умолчанию `30`).
```bash
iptvc serve --cache-enabled --cache-ttl 3600
```
## Примеры
```bash
# просто веб-сервер без проверки
iptvc serve
# веб-сервер с фоновой проверкой каждые 2 минуты
iptvc serve --check --playlists-all-cooldown 120
# веб-сервер на порту 3000 с проверкой 10 случайных плейлистов
iptvc serve -p 3000 --check -r 10
# один цикл проверки, затем только веб-сервер
iptvc serve --check --repeat 1
# веб-сервер с кешем и фоновой проверкой, увеличенные лимиты параллелизма
iptvc serve --check --cache-enabled \
--playlists-max-routines 10 \
--channels-max-routines 100
# веб-сервер с отладкой и кастомным user-agent
iptvc serve --check --debug \
--playlists-user-agent "Mozilla/5.0" \
--channels-user-agent "VLC/3.0"
```
## Веб-маршруты
| Метод | Путь | Описание |
| ----- | -------------------------------- | ---------------------------------------- |
| GET | `/` | Главная страница со списком плейлистов |
| GET | `/page/{N}` | Страница N списка плейлистов |
| GET | `/{code}` | Редирект на прямую ссылку плейлиста |
| GET | `/{code}.m3u` | Редирект на прямую ссылку плейлиста |
| GET | `/{code}.m3u8` | Редирект на прямую ссылку плейлиста |
| GET | `/{code}/details` | Страница с описанием плейлиста |
| GET | `/api` | Редирект на `/api/` (Swagger UI) |
| GET | `/api/` | Swagger UI с описанием REST API |
| GET | `/api/openapi.json` | OpenAPI-схема в формате JSON |
| GET | `/api/playlists` | JSON: массив всех плейлистов |
| GET | `/api/playlists/{code}` | JSON: информация о плейлисте |
| GET | `/api/playlists/{code}/channels` | JSON: список каналов плейлиста |
| GET | `/api/version` | JSON: версии компонентов |
| GET | `/api/health` | JSON: состояние сервиса |
| GET | `/api/stats` | JSON: статистика по плейлистам и каналам |
+20
View File
@@ -0,0 +1,20 @@
---
title: version
tags: [iptvc]
---
# Команда `version`
Выводит версию программы:
```bash
./iptvc version
```
Пример результата:
```
iptvc v1.1.3
```
Версия также доступна через [API](serve.md) — endpoint `GET /api/version`.
+234
View File
@@ -0,0 +1,234 @@
---
# icon: material/architecture
tags: ["iptvc", "разработка", "архитектура"]
---
# Архитектура iptvc
Внутреннее устройство программы для разработчиков.
## Стек технологий
- **Go 1.23+** — язык программирования;
- `net/http` — HTTP-сервер (Go 1.22+ routing patterns);
- `html/template` — шаблоны HTML;
- `//go:embed` — встраивание шаблонов в бинарник;
- `github.com/spf13/cobra` — CLI-фреймворк;
- `gopkg.in/yaml.v3` — парсинг `config.yml`;
- `github.com/joho/godotenv` — загрузка `.env`;
- `github.com/redis/go-redis/v9` — клиент KeyDB/Redis.
## Структура проекта
```
iptvc/
├── main.go # точка входа
├── config.yml # конфигурация
├── .env # переменные окружения (опционально)
├── cmd/ # CLI-команды (Cobra)
│ ├── root.go # корневая команда, глобальные флаги
│ ├── check.go # команда check
│ ├── serve.go # команда serve
│ ├── flags.go # общие флаги check/serve
│ └── version.go # команда version
├── app/
│ ├── app.go # глобальные переменные: Args, Config, Cache
│ ├── config/
│ │ └── config.go # Config, Init(), validate(), IntRange, UserAgents
│ ├── checker/
│ │ └── checker.go # CheckPlaylists(), CheckChannels(), OnPlaylistChecked
│ ├── playlist/
│ │ └── playlist.go # Playlist, Channel, Parse(), Download()
│ ├── inifile/
│ │ └── inifile.go # чтение playlists.ini
│ ├── tagfile/
│ │ └── tagfile.go # чтение channels.json, назначение тегов
│ ├── cache/
│ │ └── cache.go # подключение к KeyDB/Redis
│ ├── logger/
│ │ └── logger.go # настройка логирования
│ ├── utils/
│ │ └── utils.go # Fetch(), ExpandPath(), ArrayUnique(), Md5str()
│ └── web/
│ ├── server.go # Server, Start(), StartBackgroundChecker()
│ ├── handlers.go # HTTP-обработчики
│ ├── views.go # PlaylistView, ChannelView, PageData
│ ├── templates.go # TemplateManager, //go:embed
│ ├── openapi.go # OpenAPI-спецификация, Swagger UI
│ ├── swagger/ # встроенные файлы Swagger UI
│ ├── static/ # встроенные статические файлы (CSS, JS)
│ └── views/ # HTML-шаблоны
│ ├── base.html
│ ├── list.html
│ ├── details.html
│ └── notfound.html
└── go.mod
```
## Пакеты
### `app`
Глобальный контейнер: `Args` (CLI-флаги), `Config` (конфигурация), `Cache` (Redis-клиент).
`Init()` загружает конфигурацию и инициализирует логгер.
`InitCache()` подключается к KeyDB/Redis, если кеш включён.
### `app.config`
Структуры: `Config``AppConfig`, `ServerConfig`, `CheckConfig`, `CacheConfig`.
Кастомные YAML-типы:
- **`IntRange`** — скаляр или `[min, max]`. Метод `Value()` возвращает константу или случайное значение.
- **`UserAgents`** — строка или массив строк. Метод `Pick()` возвращает случайный элемент.
`Init(configPath)` — загружает `config.yml`, применяет env, валидирует.
`validate()` — проверяет все значения, исправляет некорректные с логированием.
### `app.checker`
Содержит логику проверки:
- **`PrepareListsToCheck(files, urls, codes)`** — формирует список плейлистов из файлов, URL и кодов ini-файла.
- **`CheckPlaylists(lists)`** — параллельная проверка плейлистов (семфор `max-routines`), загрузка, парсинг, вызов `CheckChannels` для каждого.
- **`CheckChannels(pls)`** — параллельная проверка каналов (семфор `max-routines`), HTTP-запрос с `Range` header.
- **`OnPlaylistChecked`** — глобальный callback, вызывается после проверки каждого плейлиста. Используется веб-сервером для обновления in-memory кеша.
- **`cachePlaylist(pls)`** — сохранение результата в Redis (если включён).
Параметры проверки берутся из `app.Config.Check.Playlists` и `app.Config.Check.Channels`.
### `app.playlist`
- **`Playlist`** — плейлист: код, URL, контент, каналы, статус.
- **`Channel`** — канал: ID, название, URL, статус, теги.
- **`Download(userAgent, timeout)`** — загрузка по URL.
- **`ReadFromFs()`** — чтение из файла.
- **`Parse()`** — парсинг m3u/m3u8 контента.
### `app.web`
Веб-сервер на `net/http` (Go 1.22 routing).
- **`Server`** — структура: конфиг, кеш, шаблоны, in-memory кеш (`memCache` с `sync.RWMutex`).
- **`Start()`** — запуск HTTP-сервера.
- **`StartBackgroundChecker(opts)`** — фоновая проверка в отдельной горутине.
- **`CheckOptions`** — параметры: Repeat, Random, Files, Urls, Codes.
Маршруты (Go 1.22 patterns):
```
GET /api — редирект на /api/
GET /api/ — Swagger UI
GET /api/openapi.json — OpenAPI-схема
GET /api/playlists — JSON: массив плейлистов
GET /api/playlists/{code} — JSON плейлиста
GET /api/playlists/{code}/channels — JSON каналов плейлиста
GET /api/version — версия
GET /api/health — здоровье сервиса
GET /api/stats — статистика
GET /{$} — главная (catch-all root)
GET /{path...} — все остальные маршруты (catch-all)
```
Catch-all `/{path...}` используется для избежания конфликтов паттернов в Go 1.22 mux.
In-memory кеш (`memCache`) обновляется через `OnPlaylistChecked` callback.
Это позволяет отображать результаты проверки в реальном времени без ожидания завершения цикла и без Redis.
ini-файл кешируется на 30 секунд, кеш сбрасывается при каждом обновлении `memCache`.
## Жизненный цикл `serve --check`
```
main → app.Init() → web.NewServer() → go StartBackgroundChecker() → server.Start()
StartBackgroundChecker:
loop:
runCheckerOnce()
→ checker.PrepareListsToCheck()
→ checker.CheckPlaylists()
→ for each playlist (parallel, per-routine):
→ Download() / ReadFromFs()
→ Parse()
→ CheckChannels()
→ for each channel (parallel, per-routine):
→ HTTP GET with Range header
→ check status + content type
→ OnPlaylistChecked(pls) → memCache update
→ one-cooldown sleep
→ all-cooldown sleep
if repeat > 0 && iteration >= repeat: stop
```
## CLI-флаги
### Общие (`cmd/flags.go`)
Используются командами `check` и `serve --check`:
| Флаг | Поле | Описание |
| -------------------------- | -------------------- | ---------------------------------- |
| `-i, --ini` | `Args.IniPath` | Путь к playlists.ini |
| `-t, --tags` | `Args.TagsPath` | Путь к channels.json |
| `-r, --random` | `Args.RandomCount` | Случайные N плейлистов |
| `--repeat` | `Args.RepeatCount` | Количество циклов (0 = бесконечно) |
| `--playlists-all-cooldown` | `Args.PlAllCooldown` | Секунд между циклами |
### Только `check` (`addCheckOnlyFlags`)
| Флаг | Поле | Описание |
| ------------- | ---------------- | ------------------- |
| `-j, --json` | `Args.NeedJson` | Вывод в JSON |
| `-q, --quiet` | `Args.NeedQuiet` | Подавить логи |
| `-f, --file` | `Args.Files` | Локальные m3u файлы |
| `-u, --url` | `Args.Urls` | URL плейлистов |
| `-c, --code` | `Args.Codes` | Коды из ini-файла |
### Только `serve`
| Флаг | Поле | Описание |
| ------------ | ----------------- | ------------------------- |
| `-p, --port` | `Args.ServerPort` | Порт веб-сервера |
| `--host` | `Args.ServerHost` | Хост привязки |
| `--check` | `Args.NeedCheck` | Включить фоновую проверку |
### Глобальные (`cmd/root.go`)
| Флаг | Поле | Описание |
| --------------- | ----------------- | ------------------- |
| `--config` | `Args.ConfigPath` | Путь к config.yml |
| `-v, --verbose` | `Args.Verbose` | Подробный лог |
| `--debug` | `Args.Debug` | Режим отладки |
| `--log-level` | `Args.LogLevel` | Уровень логирования |
## Конфигурация
Подробное описание параметров — в разделе [config.yml](../../common/config/config.md).
Приоритет: Defaults < `config.yml` < Env < CLI-флаги.
CLI-флаги переопределяют конфигурацию только если переданы явно (`cmd.Flags().Changed()`).
Для этого в Cobra используются zero-value defaults (0, "", false), чтобы отличить «не передан» от «передан со значением по умолчанию».
## Шаблоны
HTML-шаблоны встроены через `//go:embed`:
- `base.html` — общий каркас (header, footer);
- `list.html` — список плейлистов с пагинацией;
- `details.html` — детали плейлиста и список каналов;
- `notfound.html` — страница 404.
SVG-логотипы каналов передаются через `encodeURIComponent` в data-URI для корректной работы с кавычками в HTML.
При отсутствии `playlists.ini` рендерится пустое состояние с alert-блоком.
## Кеширование
Два уровня кеша:
1. **Redis/KeyDB** (опционально) — постоянный кеш результатов проверки. TTL из `cache.ttl`.
2. **In-memory** (`memCache`) — только при `serve --check`. Обновляется в реальном времени через callback. Не требует Redis.
In-memory кеш приоритетнее Redis при отображении в веб-интерфейсе.
+62
View File
@@ -0,0 +1,62 @@
---
title: Компиляция
icon: material/cog
tags: ["iptvc", "сборка"]
---
# Компиляция из исходного кода
Для компиляции потребуется [Go](https://go.dev/dl/) 1.23.6 и выше.
```bash
git clone https://git.axenov.dev/IPTV/iptvc.git
cd iptvc
make linux
# или make help для получения справки по рецептам
```
## Доступные рецепты Makefile
| Команда | Назначение |
| ---------------- | ------------------------------------------------------------- |
| `make linux` | Сборка под Linux (amd64 по умолчанию) |
| `make win` | Сборка под Windows |
| `make darwin` | Сборка под macOS |
| `make release` | Сборка под все платформы (linux/windows/darwin × amd64/arm64) |
| `make test` | Запуск всех тестов |
| `make clear` | Удаление скомпилированных бинарников |
| `make image` | Сборка Docker-образа для текущей платформы |
| `make image-all` | Сборка и отправка multi-arch манифеста в registry |
| `make help` | Вывод списка доступных рецептов |
## Управление архитектурой
Архитектура целевой платформы задаётся переменной `GOARCH`:
```bash
make darwin GOARCH=arm64 # macOS на Apple Silicon
make linux GOARCH=arm64 # Linux ARM (Raspberry Pi и т.п.)
```
## Результат
Скомпилированные бинарники и ZIP-архивы помещаются в директорию `bin/`:
```
bin/
├── linux_amd64/iptvc # или linux_arm64/
├── linux_amd64.zip
├── windows_amd64/iptvc.exe # или windows_arm64/
├── windows_amd64.zip
├── darwin_amd64/iptvc # или darwin_arm64/
└── darwin_amd64.zip
```
## Сборка без Make
Можно скомпилировать напрямую через `go build`:
```bash
go build -o iptvc .
go build -trimpath -ldflags="-s -w" -o iptvc .
```
+68
View File
@@ -0,0 +1,68 @@
---
title: Docker-образ
icon: simple/docker
tags: ["iptvc", "docker"]
---
# Построение Docker-образа
## Сборка
Образ собирается из [Dockerfile](https://git.axenov.dev/IPTV/iptvc/src/branch/master/Dockerfile) на базе `alpine:3.22.5`.
Бинарный файл должен быть предварительно собран через `make linux` — Dockerfile берёт уже готовый бинарь из `bin/linux_${TARGETARCH}/iptvc`.
```bash
# Сборка бинарного файла и образа
make image
# Сборка с указанием архитектуры
make image GOARCH=arm64
# Сборка с указанием тега
make image IMAGE_TAG=v1.1.3
```
Целевая архитектура задаётся через `GOARCH`:
```bash
make linux GOARCH=arm64
make image GOARCH=arm64
```
Для публикации multi-arch манифеста в registry:
```bash
make image-all
```
## Запуск
```bash
# Проверка плейлистов
docker run --rm \
-v ./playlists.ini:/app/playlists.ini \
-v ./channels.json:/app/channels.json \
git.axenov.dev/iptv/iptvc:latest check -i playlists.ini --repeat 0 --playlists-all-cooldown 60
# Веб-сервер с фоновой проверкой
docker run --rm -p 8800:8800 \
-v ./playlists.ini:/app/playlists.ini \
-v ./channels.json:/app/channels.json \
git.axenov.dev/iptv/iptvc:latest serve -i playlists.ini -p 8800 --check
```
## Использование в compose
В `compose.yml` сервиса `iptvc` образ собирается автоматически:
```yaml
iptvc:
image: git.axenov.dev/iptv/iptvc:latest
build:
context: ./iptvc
dockerfile: Dockerfile
command: ["serve", "--check", "--repeat", "0"]
```
Подробнее о полном окружении — в разделе [Развёртывание](../site/deploy.md).
+25 -20
View File
@@ -6,7 +6,7 @@ icon: material/book-cog-outline
## Стилистика и правила оформления ## Стилистика и правила оформления
Все исходники хранятся в директории `src/` в формате **Markdown** (формат файлов `.md`). Все исходники хранятся в директории `content/` в формате **Markdown** (формат файлов `.md`).
Структура проекта и его конфигурация описываются в файле `mkdocs.yml` в корне репозитория. Структура проекта и его конфигурация описываются в файле `mkdocs.yml` в корне репозитория.
@@ -17,14 +17,14 @@ icon: material/book-cog-outline
**Каждое предложение должно быть на одной строке.** **Каждое предложение должно быть на одной строке.**
Это даёт более наглядную разницу (diff) в тексте при работе с git. Это даёт более наглядную разницу (diff) в тексте при работе с git.
Абзацы и списки должны отделяться 1 пустой строкой сверху и снизу. Абзацы и списки должны отделяться 1 пустой строкой до и после.
Допустимо использовать любые стилистические возможности темы **Material for MkDocs** и самого **mkdocs**, но не следует визуально перегружать текст. Допустимо использовать любые стилистические возможности темы **Material for MkDocs** и самого **mkdocs**, но не следует визуально перегружать текст.
Документацию по ним см. по ссылкам ниже. Документацию по ним см. по ссылкам ниже.
## Добавление изображений ## Добавление изображений
**Все изображения хранятся только в директории `src/assets/img/` и вложенных в неё.** **Все изображения хранятся в директории `_assets/` рядом с документом.**
Общая суть такова: Общая суть такова:
@@ -49,8 +49,8 @@ icon: material/book-cog-outline
Совершенно любой текст, lorem ipsum dolor sit amet. Совершенно любой текст, lorem ipsum dolor sit amet.
??? quote "В этом спойлере несколько больших картинок" ??? quote "В этом спойлере несколько больших картинок"
![Подпись-плейсхолдер1](../assets/img/example1.jpg) ![Подпись-плейсхолдер1](_assets/example1.jpg)
![Подпись-плейсхолдер2](../assets/img/example2.jpg) ![Подпись-плейсхолдер2](_assets/example2.jpg)
Продолжение текста, lorem ipsum dolor sit amet. Продолжение текста, lorem ipsum dolor sit amet.
Продолжение текста, lorem ipsum dolor sit amet. Продолжение текста, lorem ipsum dolor sit amet.
@@ -62,10 +62,9 @@ icon: material/book-cog-outline
* make * make
* [docker](https://docker.com) * [docker](https://docker.com)
* [mkdocs](https://www.mkdocs.org/) * [zensical](https://zensical.org/docs/get-started/)
* [squidfunk/mkdocs-material](https://hub.docker.com/r/squidfunk/mkdocs-material) * [zensical/zensical](https://hub.docker.com/r/zensical/zensical)
* <https://squidfunk.github.io/mkdocs-material> * <https://zensical.org/docs/get-started/>
* <https://squidfunk.github.io/mkdocs-material/reference/admonitions/>
* <https://squidfunk.github.io/mkdocs-material/reference/icons-emojis/> * <https://squidfunk.github.io/mkdocs-material/reference/icons-emojis/>
## Запуск mkdocs в контейнере ## Запуск mkdocs в контейнере
@@ -74,24 +73,30 @@ icon: material/book-cog-outline
make live make live
``` ```
Перегенерирует документацию на лету сразу после изменения файлов. Перегенерирует документацию на лету сразу после сохранения файлов.
Открывает [localhost:3000](http://localhost:3000) для просмотра изменений в реальном времени. Документацию в реальном времени можно просматривать по адресу [localhost:3000](http://localhost:3000).
## Генерация статических файлов ## Генерация статического сайта
``` ```
make build make site
``` ```
Генерирует статические файлы, которую можно версионировать и хранить/деплоить отдельно. Генерирует статические файлы, которую можно версионировать, хранить,деплоить отдельно или просматривать на ПК через браузер.
Открывает [localhost:8080/docs](http://localhost:8080/docs) для просмотра сгенерированной документации.
Готовый скомпилированный статический сайт с документацией находится в директории `site/`. Готовый скомпилированный статический сайт с документацией находится в директории `site/`.
Он же хранится в репозитории вместе с исходными файлами, потому что:
* я посчитал это **более удобным для деплоя**: ради редких обновлений нет смысла тратить ресурсы серверов на ci/cd, actions по хукам и перманентную работу `mkdocs` в режиме `build`; ## Генерация docker-образа
* я посчитал это **более удобным для использования**: можно в любой момент открыть актуальную документацию в браузере через `site/index.html` без необходимости `make` и `docker`.
Пересборка должна происходить перед каждым коммитом. ```
make image
```
Собирает docker-образ на основе nginx, генерируя перед этим статический сайт.
Запустить контейнер из этого образа по адресу [localhost:3001](http://localhost:3001) можно командой:
```
make run
```
+550
View File
@@ -0,0 +1,550 @@
Информацию о лицензии см. на странице [Свободное ПО](../../../legal/license.md)
| Иконка | Код для вставки в текст | Код для вставки в frontmatter |
| ------------------------------------------------ | -------------------------------------------------- | ------------------------------------------------ |
| :vscode-account: | `:vscode-account:` | `vscode/account` |
| :vscode-activate-breakpoints: | `:vscode-activate-breakpoints:` | `vscode/activate-breakpoints` |
| :vscode-add: | `:vscode-add:` | `vscode/add` |
| :vscode-add-small: | `:vscode-add-small:` | `vscode/add-small` |
| :vscode-agent: | `:vscode-agent:` | `vscode/agent` |
| :vscode-archive: | `:vscode-archive:` | `vscode/archive` |
| :vscode-arrow-both: | `:vscode-arrow-both:` | `vscode/arrow-both` |
| :vscode-arrow-circle-down: | `:vscode-arrow-circle-down:` | `vscode/arrow-circle-down` |
| :vscode-arrow-circle-left: | `:vscode-arrow-circle-left:` | `vscode/arrow-circle-left` |
| :vscode-arrow-circle-right: | `:vscode-arrow-circle-right:` | `vscode/arrow-circle-right` |
| :vscode-arrow-circle-up: | `:vscode-arrow-circle-up:` | `vscode/arrow-circle-up` |
| :vscode-arrow-down: | `:vscode-arrow-down:` | `vscode/arrow-down` |
| :vscode-arrow-left: | `:vscode-arrow-left:` | `vscode/arrow-left` |
| :vscode-arrow-right: | `:vscode-arrow-right:` | `vscode/arrow-right` |
| :vscode-arrow-small-down: | `:vscode-arrow-small-down:` | `vscode/arrow-small-down` |
| :vscode-arrow-small-left: | `:vscode-arrow-small-left:` | `vscode/arrow-small-left` |
| :vscode-arrow-small-right: | `:vscode-arrow-small-right:` | `vscode/arrow-small-right` |
| :vscode-arrow-small-up: | `:vscode-arrow-small-up:` | `vscode/arrow-small-up` |
| :vscode-arrow-swap: | `:vscode-arrow-swap:` | `vscode/arrow-swap` |
| :vscode-arrow-up: | `:vscode-arrow-up:` | `vscode/arrow-up` |
| :vscode-ask: | `:vscode-ask:` | `vscode/ask` |
| :vscode-attach: | `:vscode-attach:` | `vscode/attach` |
| :vscode-azure: | `:vscode-azure:` | `vscode/azure` |
| :vscode-azure-devops: | `:vscode-azure-devops:` | `vscode/azure-devops` |
| :vscode-beaker: | `:vscode-beaker:` | `vscode/beaker` |
| :vscode-beaker-stop: | `:vscode-beaker-stop:` | `vscode/beaker-stop` |
| :vscode-bell: | `:vscode-bell:` | `vscode/bell` |
| :vscode-bell-dot: | `:vscode-bell-dot:` | `vscode/bell-dot` |
| :vscode-bell-slash: | `:vscode-bell-slash:` | `vscode/bell-slash` |
| :vscode-bell-slash-dot: | `:vscode-bell-slash-dot:` | `vscode/bell-slash-dot` |
| :vscode-blank: | `:vscode-blank:` | `vscode/blank` |
| :vscode-bold: | `:vscode-bold:` | `vscode/bold` |
| :vscode-book: | `:vscode-book:` | `vscode/book` |
| :vscode-bookmark: | `:vscode-bookmark:` | `vscode/bookmark` |
| :vscode-bracket-dot: | `:vscode-bracket-dot:` | `vscode/bracket-dot` |
| :vscode-bracket-error: | `:vscode-bracket-error:` | `vscode/bracket-error` |
| :vscode-briefcase: | `:vscode-briefcase:` | `vscode/briefcase` |
| :vscode-broadcast: | `:vscode-broadcast:` | `vscode/broadcast` |
| :vscode-browser: | `:vscode-browser:` | `vscode/browser` |
| :vscode-bug: | `:vscode-bug:` | `vscode/bug` |
| :vscode-build: | `:vscode-build:` | `vscode/build` |
| :vscode-calendar: | `:vscode-calendar:` | `vscode/calendar` |
| :vscode-call-incoming: | `:vscode-call-incoming:` | `vscode/call-incoming` |
| :vscode-call-outgoing: | `:vscode-call-outgoing:` | `vscode/call-outgoing` |
| :vscode-case-sensitive: | `:vscode-case-sensitive:` | `vscode/case-sensitive` |
| :vscode-chat-export: | `:vscode-chat-export:` | `vscode/chat-export` |
| :vscode-chat-import: | `:vscode-chat-import:` | `vscode/chat-import` |
| :vscode-chat-sparkle: | `:vscode-chat-sparkle:` | `vscode/chat-sparkle` |
| :vscode-chat-sparkle-error: | `:vscode-chat-sparkle-error:` | `vscode/chat-sparkle-error` |
| :vscode-chat-sparkle-warning: | `:vscode-chat-sparkle-warning:` | `vscode/chat-sparkle-warning` |
| :vscode-check: | `:vscode-check:` | `vscode/check` |
| :vscode-check-all: | `:vscode-check-all:` | `vscode/check-all` |
| :vscode-checklist: | `:vscode-checklist:` | `vscode/checklist` |
| :vscode-chevron-down: | `:vscode-chevron-down:` | `vscode/chevron-down` |
| :vscode-chevron-left: | `:vscode-chevron-left:` | `vscode/chevron-left` |
| :vscode-chevron-right: | `:vscode-chevron-right:` | `vscode/chevron-right` |
| :vscode-chevron-up: | `:vscode-chevron-up:` | `vscode/chevron-up` |
| :vscode-chip: | `:vscode-chip:` | `vscode/chip` |
| :vscode-chrome-close: | `:vscode-chrome-close:` | `vscode/chrome-close` |
| :vscode-chrome-maximize: | `:vscode-chrome-maximize:` | `vscode/chrome-maximize` |
| :vscode-chrome-minimize: | `:vscode-chrome-minimize:` | `vscode/chrome-minimize` |
| :vscode-chrome-restore: | `:vscode-chrome-restore:` | `vscode/chrome-restore` |
| :vscode-circle: | `:vscode-circle:` | `vscode/circle` |
| :vscode-circle-filled: | `:vscode-circle-filled:` | `vscode/circle-filled` |
| :vscode-circle-large: | `:vscode-circle-large:` | `vscode/circle-large` |
| :vscode-circle-large-filled: | `:vscode-circle-large-filled:` | `vscode/circle-large-filled` |
| :vscode-circle-slash: | `:vscode-circle-slash:` | `vscode/circle-slash` |
| :vscode-circle-small: | `:vscode-circle-small:` | `vscode/circle-small` |
| :vscode-circle-small-filled: | `:vscode-circle-small-filled:` | `vscode/circle-small-filled` |
| :vscode-circuit-board: | `:vscode-circuit-board:` | `vscode/circuit-board` |
| :vscode-claude: | `:vscode-claude:` | `vscode/claude` |
| :vscode-clear-all: | `:vscode-clear-all:` | `vscode/clear-all` |
| :vscode-clippy: | `:vscode-clippy:` | `vscode/clippy` |
| :vscode-clockface: | `:vscode-clockface:` | `vscode/clockface` |
| :vscode-close: | `:vscode-close:` | `vscode/close` |
| :vscode-close-all: | `:vscode-close-all:` | `vscode/close-all` |
| :vscode-cloud: | `:vscode-cloud:` | `vscode/cloud` |
| :vscode-cloud-download: | `:vscode-cloud-download:` | `vscode/cloud-download` |
| :vscode-cloud-small: | `:vscode-cloud-small:` | `vscode/cloud-small` |
| :vscode-cloud-upload: | `:vscode-cloud-upload:` | `vscode/cloud-upload` |
| :vscode-code: | `:vscode-code:` | `vscode/code` |
| :vscode-code-oss: | `:vscode-code-oss:` | `vscode/code-oss` |
| :vscode-code-review: | `:vscode-code-review:` | `vscode/code-review` |
| :vscode-coffee: | `:vscode-coffee:` | `vscode/coffee` |
| :vscode-collapse-all: | `:vscode-collapse-all:` | `vscode/collapse-all` |
| :vscode-collection: | `:vscode-collection:` | `vscode/collection` |
| :vscode-collection-small: | `:vscode-collection-small:` | `vscode/collection-small` |
| :vscode-color-mode: | `:vscode-color-mode:` | `vscode/color-mode` |
| :vscode-combine: | `:vscode-combine:` | `vscode/combine` |
| :vscode-comment: | `:vscode-comment:` | `vscode/comment` |
| :vscode-comment-discussion: | `:vscode-comment-discussion:` | `vscode/comment-discussion` |
| :vscode-comment-discussion-quote: | `:vscode-comment-discussion-quote:` | `vscode/comment-discussion-quote` |
| :vscode-comment-discussion-sparkle: | `:vscode-comment-discussion-sparkle:` | `vscode/comment-discussion-sparkle` |
| :vscode-comment-draft: | `:vscode-comment-draft:` | `vscode/comment-draft` |
| :vscode-comment-unresolved: | `:vscode-comment-unresolved:` | `vscode/comment-unresolved` |
| :vscode-compass: | `:vscode-compass:` | `vscode/compass` |
| :vscode-compass-active: | `:vscode-compass-active:` | `vscode/compass-active` |
| :vscode-compass-dot: | `:vscode-compass-dot:` | `vscode/compass-dot` |
| :vscode-copilot: | `:vscode-copilot:` | `vscode/copilot` |
| :vscode-copilot-blocked: | `:vscode-copilot-blocked:` | `vscode/copilot-blocked` |
| :vscode-copilot-error: | `:vscode-copilot-error:` | `vscode/copilot-error` |
| :vscode-copilot-in-progress: | `:vscode-copilot-in-progress:` | `vscode/copilot-in-progress` |
| :vscode-copilot-large: | `:vscode-copilot-large:` | `vscode/copilot-large` |
| :vscode-copilot-not-connected: | `:vscode-copilot-not-connected:` | `vscode/copilot-not-connected` |
| :vscode-copilot-snooze: | `:vscode-copilot-snooze:` | `vscode/copilot-snooze` |
| :vscode-copilot-success: | `:vscode-copilot-success:` | `vscode/copilot-success` |
| :vscode-copilot-unavailable: | `:vscode-copilot-unavailable:` | `vscode/copilot-unavailable` |
| :vscode-copilot-warning: | `:vscode-copilot-warning:` | `vscode/copilot-warning` |
| :vscode-copilot-warning-large: | `:vscode-copilot-warning-large:` | `vscode/copilot-warning-large` |
| :vscode-copy: | `:vscode-copy:` | `vscode/copy` |
| :vscode-coverage: | `:vscode-coverage:` | `vscode/coverage` |
| :vscode-credit-card: | `:vscode-credit-card:` | `vscode/credit-card` |
| :vscode-cursor: | `:vscode-cursor:` | `vscode/cursor` |
| :vscode-dash: | `:vscode-dash:` | `vscode/dash` |
| :vscode-dashboard: | `:vscode-dashboard:` | `vscode/dashboard` |
| :vscode-database: | `:vscode-database:` | `vscode/database` |
| :vscode-debug: | `:vscode-debug:` | `vscode/debug` |
| :vscode-debug-all: | `:vscode-debug-all:` | `vscode/debug-all` |
| :vscode-debug-alt: | `:vscode-debug-alt:` | `vscode/debug-alt` |
| :vscode-debug-alt-small: | `:vscode-debug-alt-small:` | `vscode/debug-alt-small` |
| :vscode-debug-breakpoint-conditional: | `:vscode-debug-breakpoint-conditional:` | `vscode/debug-breakpoint-conditional` |
| :vscode-debug-breakpoint-conditional-unverified: | `:vscode-debug-breakpoint-conditional-unverified:` | `vscode/debug-breakpoint-conditional-unverified` |
| :vscode-debug-breakpoint-data: | `:vscode-debug-breakpoint-data:` | `vscode/debug-breakpoint-data` |
| :vscode-debug-breakpoint-data-unverified: | `:vscode-debug-breakpoint-data-unverified:` | `vscode/debug-breakpoint-data-unverified` |
| :vscode-debug-breakpoint-function: | `:vscode-debug-breakpoint-function:` | `vscode/debug-breakpoint-function` |
| :vscode-debug-breakpoint-function-unverified: | `:vscode-debug-breakpoint-function-unverified:` | `vscode/debug-breakpoint-function-unverified` |
| :vscode-debug-breakpoint-log: | `:vscode-debug-breakpoint-log:` | `vscode/debug-breakpoint-log` |
| :vscode-debug-breakpoint-log-unverified: | `:vscode-debug-breakpoint-log-unverified:` | `vscode/debug-breakpoint-log-unverified` |
| :vscode-debug-breakpoint-unsupported: | `:vscode-debug-breakpoint-unsupported:` | `vscode/debug-breakpoint-unsupported` |
| :vscode-debug-connected: | `:vscode-debug-connected:` | `vscode/debug-connected` |
| :vscode-debug-console: | `:vscode-debug-console:` | `vscode/debug-console` |
| :vscode-debug-continue: | `:vscode-debug-continue:` | `vscode/debug-continue` |
| :vscode-debug-continue-small: | `:vscode-debug-continue-small:` | `vscode/debug-continue-small` |
| :vscode-debug-coverage: | `:vscode-debug-coverage:` | `vscode/debug-coverage` |
| :vscode-debug-disconnect: | `:vscode-debug-disconnect:` | `vscode/debug-disconnect` |
| :vscode-debug-line-by-line: | `:vscode-debug-line-by-line:` | `vscode/debug-line-by-line` |
| :vscode-debug-pause: | `:vscode-debug-pause:` | `vscode/debug-pause` |
| :vscode-debug-rerun: | `:vscode-debug-rerun:` | `vscode/debug-rerun` |
| :vscode-debug-restart: | `:vscode-debug-restart:` | `vscode/debug-restart` |
| :vscode-debug-restart-frame: | `:vscode-debug-restart-frame:` | `vscode/debug-restart-frame` |
| :vscode-debug-reverse-continue: | `:vscode-debug-reverse-continue:` | `vscode/debug-reverse-continue` |
| :vscode-debug-stackframe: | `:vscode-debug-stackframe:` | `vscode/debug-stackframe` |
| :vscode-debug-stackframe-active: | `:vscode-debug-stackframe-active:` | `vscode/debug-stackframe-active` |
| :vscode-debug-start: | `:vscode-debug-start:` | `vscode/debug-start` |
| :vscode-debug-step-back: | `:vscode-debug-step-back:` | `vscode/debug-step-back` |
| :vscode-debug-step-into: | `:vscode-debug-step-into:` | `vscode/debug-step-into` |
| :vscode-debug-step-out: | `:vscode-debug-step-out:` | `vscode/debug-step-out` |
| :vscode-debug-step-over: | `:vscode-debug-step-over:` | `vscode/debug-step-over` |
| :vscode-debug-stop: | `:vscode-debug-stop:` | `vscode/debug-stop` |
| :vscode-desktop-download: | `:vscode-desktop-download:` | `vscode/desktop-download` |
| :vscode-device-camera: | `:vscode-device-camera:` | `vscode/device-camera` |
| :vscode-device-camera-video: | `:vscode-device-camera-video:` | `vscode/device-camera-video` |
| :vscode-device-mobile: | `:vscode-device-mobile:` | `vscode/device-mobile` |
| :vscode-diff: | `:vscode-diff:` | `vscode/diff` |
| :vscode-diff-added: | `:vscode-diff-added:` | `vscode/diff-added` |
| :vscode-diff-ignored: | `:vscode-diff-ignored:` | `vscode/diff-ignored` |
| :vscode-diff-modified: | `:vscode-diff-modified:` | `vscode/diff-modified` |
| :vscode-diff-multiple: | `:vscode-diff-multiple:` | `vscode/diff-multiple` |
| :vscode-diff-removed: | `:vscode-diff-removed:` | `vscode/diff-removed` |
| :vscode-diff-renamed: | `:vscode-diff-renamed:` | `vscode/diff-renamed` |
| :vscode-diff-single: | `:vscode-diff-single:` | `vscode/diff-single` |
| :vscode-discard: | `:vscode-discard:` | `vscode/discard` |
| :vscode-download: | `:vscode-download:` | `vscode/download` |
| :vscode-edit: | `:vscode-edit:` | `vscode/edit` |
| :vscode-edit-code: | `:vscode-edit-code:` | `vscode/edit-code` |
| :vscode-edit-session: | `:vscode-edit-session:` | `vscode/edit-session` |
| :vscode-edit-sparkle: | `:vscode-edit-sparkle:` | `vscode/edit-sparkle` |
| :vscode-editor-layout: | `:vscode-editor-layout:` | `vscode/editor-layout` |
| :vscode-ellipsis: | `:vscode-ellipsis:` | `vscode/ellipsis` |
| :vscode-empty-window: | `:vscode-empty-window:` | `vscode/empty-window` |
| :vscode-eraser: | `:vscode-eraser:` | `vscode/eraser` |
| :vscode-error: | `:vscode-error:` | `vscode/error` |
| :vscode-error-small: | `:vscode-error-small:` | `vscode/error-small` |
| :vscode-exclude: | `:vscode-exclude:` | `vscode/exclude` |
| :vscode-expand-all: | `:vscode-expand-all:` | `vscode/expand-all` |
| :vscode-export: | `:vscode-export:` | `vscode/export` |
| :vscode-extensions: | `:vscode-extensions:` | `vscode/extensions` |
| :vscode-extensions-large: | `:vscode-extensions-large:` | `vscode/extensions-large` |
| :vscode-eye: | `:vscode-eye:` | `vscode/eye` |
| :vscode-eye-closed: | `:vscode-eye-closed:` | `vscode/eye-closed` |
| :vscode-feedback: | `:vscode-feedback:` | `vscode/feedback` |
| :vscode-file: | `:vscode-file:` | `vscode/file` |
| :vscode-file-binary: | `:vscode-file-binary:` | `vscode/file-binary` |
| :vscode-file-code: | `:vscode-file-code:` | `vscode/file-code` |
| :vscode-file-media: | `:vscode-file-media:` | `vscode/file-media` |
| :vscode-file-pdf: | `:vscode-file-pdf:` | `vscode/file-pdf` |
| :vscode-file-submodule: | `:vscode-file-submodule:` | `vscode/file-submodule` |
| :vscode-file-symlink-directory: | `:vscode-file-symlink-directory:` | `vscode/file-symlink-directory` |
| :vscode-file-symlink-file: | `:vscode-file-symlink-file:` | `vscode/file-symlink-file` |
| :vscode-file-text: | `:vscode-file-text:` | `vscode/file-text` |
| :vscode-file-zip: | `:vscode-file-zip:` | `vscode/file-zip` |
| :vscode-files: | `:vscode-files:` | `vscode/files` |
| :vscode-filter: | `:vscode-filter:` | `vscode/filter` |
| :vscode-filter-filled: | `:vscode-filter-filled:` | `vscode/filter-filled` |
| :vscode-flag: | `:vscode-flag:` | `vscode/flag` |
| :vscode-flame: | `:vscode-flame:` | `vscode/flame` |
| :vscode-fold: | `:vscode-fold:` | `vscode/fold` |
| :vscode-fold-down: | `:vscode-fold-down:` | `vscode/fold-down` |
| :vscode-fold-up: | `:vscode-fold-up:` | `vscode/fold-up` |
| :vscode-folder: | `:vscode-folder:` | `vscode/folder` |
| :vscode-folder-active: | `:vscode-folder-active:` | `vscode/folder-active` |
| :vscode-folder-library: | `:vscode-folder-library:` | `vscode/folder-library` |
| :vscode-folder-opened: | `:vscode-folder-opened:` | `vscode/folder-opened` |
| :vscode-forward: | `:vscode-forward:` | `vscode/forward` |
| :vscode-game: | `:vscode-game:` | `vscode/game` |
| :vscode-gear: | `:vscode-gear:` | `vscode/gear` |
| :vscode-gift: | `:vscode-gift:` | `vscode/gift` |
| :vscode-gist: | `:vscode-gist:` | `vscode/gist` |
| :vscode-gist-secret: | `:vscode-gist-secret:` | `vscode/gist-secret` |
| :vscode-git-branch: | `:vscode-git-branch:` | `vscode/git-branch` |
| :vscode-git-branch-changes: | `:vscode-git-branch-changes:` | `vscode/git-branch-changes` |
| :vscode-git-branch-conflicts: | `:vscode-git-branch-conflicts:` | `vscode/git-branch-conflicts` |
| :vscode-git-branch-staged-changes: | `:vscode-git-branch-staged-changes:` | `vscode/git-branch-staged-changes` |
| :vscode-git-commit: | `:vscode-git-commit:` | `vscode/git-commit` |
| :vscode-git-compare: | `:vscode-git-compare:` | `vscode/git-compare` |
| :vscode-git-fetch: | `:vscode-git-fetch:` | `vscode/git-fetch` |
| :vscode-git-merge: | `:vscode-git-merge:` | `vscode/git-merge` |
| :vscode-git-pull-request: | `:vscode-git-pull-request:` | `vscode/git-pull-request` |
| :vscode-git-pull-request-closed: | `:vscode-git-pull-request-closed:` | `vscode/git-pull-request-closed` |
| :vscode-git-pull-request-create: | `:vscode-git-pull-request-create:` | `vscode/git-pull-request-create` |
| :vscode-git-pull-request-done: | `:vscode-git-pull-request-done:` | `vscode/git-pull-request-done` |
| :vscode-git-pull-request-draft: | `:vscode-git-pull-request-draft:` | `vscode/git-pull-request-draft` |
| :vscode-git-pull-request-go-to-changes: | `:vscode-git-pull-request-go-to-changes:` | `vscode/git-pull-request-go-to-changes` |
| :vscode-git-pull-request-new-changes: | `:vscode-git-pull-request-new-changes:` | `vscode/git-pull-request-new-changes` |
| :vscode-git-stash: | `:vscode-git-stash:` | `vscode/git-stash` |
| :vscode-git-stash-apply: | `:vscode-git-stash-apply:` | `vscode/git-stash-apply` |
| :vscode-git-stash-pop: | `:vscode-git-stash-pop:` | `vscode/git-stash-pop` |
| :vscode-github: | `:vscode-github:` | `vscode/github` |
| :vscode-github-action: | `:vscode-github-action:` | `vscode/github-action` |
| :vscode-github-alt: | `:vscode-github-alt:` | `vscode/github-alt` |
| :vscode-github-inverted: | `:vscode-github-inverted:` | `vscode/github-inverted` |
| :vscode-github-project: | `:vscode-github-project:` | `vscode/github-project` |
| :vscode-globe: | `:vscode-globe:` | `vscode/globe` |
| :vscode-go-to-editing-session: | `:vscode-go-to-editing-session:` | `vscode/go-to-editing-session` |
| :vscode-go-to-file: | `:vscode-go-to-file:` | `vscode/go-to-file` |
| :vscode-go-to-search: | `:vscode-go-to-search:` | `vscode/go-to-search` |
| :vscode-grabber: | `:vscode-grabber:` | `vscode/grabber` |
| :vscode-graph: | `:vscode-graph:` | `vscode/graph` |
| :vscode-graph-left: | `:vscode-graph-left:` | `vscode/graph-left` |
| :vscode-graph-line: | `:vscode-graph-line:` | `vscode/graph-line` |
| :vscode-graph-scatter: | `:vscode-graph-scatter:` | `vscode/graph-scatter` |
| :vscode-gripper: | `:vscode-gripper:` | `vscode/gripper` |
| :vscode-group-by-ref-type: | `:vscode-group-by-ref-type:` | `vscode/group-by-ref-type` |
| :vscode-heart: | `:vscode-heart:` | `vscode/heart` |
| :vscode-heart-filled: | `:vscode-heart-filled:` | `vscode/heart-filled` |
| :vscode-history: | `:vscode-history:` | `vscode/history` |
| :vscode-home: | `:vscode-home:` | `vscode/home` |
| :vscode-horizontal-rule: | `:vscode-horizontal-rule:` | `vscode/horizontal-rule` |
| :vscode-hubot: | `:vscode-hubot:` | `vscode/hubot` |
| :vscode-inbox: | `:vscode-inbox:` | `vscode/inbox` |
| :vscode-indent: | `:vscode-indent:` | `vscode/indent` |
| :vscode-index-zero: | `:vscode-index-zero:` | `vscode/index-zero` |
| :vscode-info: | `:vscode-info:` | `vscode/info` |
| :vscode-insert: | `:vscode-insert:` | `vscode/insert` |
| :vscode-inspect: | `:vscode-inspect:` | `vscode/inspect` |
| :vscode-issue-draft: | `:vscode-issue-draft:` | `vscode/issue-draft` |
| :vscode-issue-reopened: | `:vscode-issue-reopened:` | `vscode/issue-reopened` |
| :vscode-issues: | `:vscode-issues:` | `vscode/issues` |
| :vscode-italic: | `:vscode-italic:` | `vscode/italic` |
| :vscode-jersey: | `:vscode-jersey:` | `vscode/jersey` |
| :vscode-json: | `:vscode-json:` | `vscode/json` |
| :vscode-kebab-vertical: | `:vscode-kebab-vertical:` | `vscode/kebab-vertical` |
| :vscode-key: | `:vscode-key:` | `vscode/key` |
| :vscode-keyboard-tab: | `:vscode-keyboard-tab:` | `vscode/keyboard-tab` |
| :vscode-keyboard-tab-above: | `:vscode-keyboard-tab-above:` | `vscode/keyboard-tab-above` |
| :vscode-keyboard-tab-below: | `:vscode-keyboard-tab-below:` | `vscode/keyboard-tab-below` |
| :vscode-law: | `:vscode-law:` | `vscode/law` |
| :vscode-layers: | `:vscode-layers:` | `vscode/layers` |
| :vscode-layers-active: | `:vscode-layers-active:` | `vscode/layers-active` |
| :vscode-layers-dot: | `:vscode-layers-dot:` | `vscode/layers-dot` |
| :vscode-layout: | `:vscode-layout:` | `vscode/layout` |
| :vscode-layout-activitybar-left: | `:vscode-layout-activitybar-left:` | `vscode/layout-activitybar-left` |
| :vscode-layout-activitybar-right: | `:vscode-layout-activitybar-right:` | `vscode/layout-activitybar-right` |
| :vscode-layout-centered: | `:vscode-layout-centered:` | `vscode/layout-centered` |
| :vscode-layout-menubar: | `:vscode-layout-menubar:` | `vscode/layout-menubar` |
| :vscode-layout-panel: | `:vscode-layout-panel:` | `vscode/layout-panel` |
| :vscode-layout-panel-center: | `:vscode-layout-panel-center:` | `vscode/layout-panel-center` |
| :vscode-layout-panel-dock: | `:vscode-layout-panel-dock:` | `vscode/layout-panel-dock` |
| :vscode-layout-panel-justify: | `:vscode-layout-panel-justify:` | `vscode/layout-panel-justify` |
| :vscode-layout-panel-left: | `:vscode-layout-panel-left:` | `vscode/layout-panel-left` |
| :vscode-layout-panel-off: | `:vscode-layout-panel-off:` | `vscode/layout-panel-off` |
| :vscode-layout-panel-right: | `:vscode-layout-panel-right:` | `vscode/layout-panel-right` |
| :vscode-layout-sidebar-left: | `:vscode-layout-sidebar-left:` | `vscode/layout-sidebar-left` |
| :vscode-layout-sidebar-left-dock: | `:vscode-layout-sidebar-left-dock:` | `vscode/layout-sidebar-left-dock` |
| :vscode-layout-sidebar-left-off: | `:vscode-layout-sidebar-left-off:` | `vscode/layout-sidebar-left-off` |
| :vscode-layout-sidebar-right: | `:vscode-layout-sidebar-right:` | `vscode/layout-sidebar-right` |
| :vscode-layout-sidebar-right-dock: | `:vscode-layout-sidebar-right-dock:` | `vscode/layout-sidebar-right-dock` |
| :vscode-layout-sidebar-right-off: | `:vscode-layout-sidebar-right-off:` | `vscode/layout-sidebar-right-off` |
| :vscode-layout-statusbar: | `:vscode-layout-statusbar:` | `vscode/layout-statusbar` |
| :vscode-library: | `:vscode-library:` | `vscode/library` |
| :vscode-lightbulb: | `:vscode-lightbulb:` | `vscode/lightbulb` |
| :vscode-lightbulb-autofix: | `:vscode-lightbulb-autofix:` | `vscode/lightbulb-autofix` |
| :vscode-lightbulb-empty: | `:vscode-lightbulb-empty:` | `vscode/lightbulb-empty` |
| :vscode-lightbulb-sparkle: | `:vscode-lightbulb-sparkle:` | `vscode/lightbulb-sparkle` |
| :vscode-link: | `:vscode-link:` | `vscode/link` |
| :vscode-link-external: | `:vscode-link-external:` | `vscode/link-external` |
| :vscode-list-filter: | `:vscode-list-filter:` | `vscode/list-filter` |
| :vscode-list-flat: | `:vscode-list-flat:` | `vscode/list-flat` |
| :vscode-list-ordered: | `:vscode-list-ordered:` | `vscode/list-ordered` |
| :vscode-list-selection: | `:vscode-list-selection:` | `vscode/list-selection` |
| :vscode-list-tree: | `:vscode-list-tree:` | `vscode/list-tree` |
| :vscode-list-unordered: | `:vscode-list-unordered:` | `vscode/list-unordered` |
| :vscode-live-share: | `:vscode-live-share:` | `vscode/live-share` |
| :vscode-loading: | `:vscode-loading:` | `vscode/loading` |
| :vscode-location: | `:vscode-location:` | `vscode/location` |
| :vscode-lock: | `:vscode-lock:` | `vscode/lock` |
| :vscode-lock-small: | `:vscode-lock-small:` | `vscode/lock-small` |
| :vscode-magnet: | `:vscode-magnet:` | `vscode/magnet` |
| :vscode-mail: | `:vscode-mail:` | `vscode/mail` |
| :vscode-mail-read: | `:vscode-mail-read:` | `vscode/mail-read` |
| :vscode-map: | `:vscode-map:` | `vscode/map` |
| :vscode-map-filled: | `:vscode-map-filled:` | `vscode/map-filled` |
| :vscode-map-vertical: | `:vscode-map-vertical:` | `vscode/map-vertical` |
| :vscode-map-vertical-filled: | `:vscode-map-vertical-filled:` | `vscode/map-vertical-filled` |
| :vscode-markdown: | `:vscode-markdown:` | `vscode/markdown` |
| :vscode-mcp: | `:vscode-mcp:` | `vscode/mcp` |
| :vscode-megaphone: | `:vscode-megaphone:` | `vscode/megaphone` |
| :vscode-mention: | `:vscode-mention:` | `vscode/mention` |
| :vscode-menu: | `:vscode-menu:` | `vscode/menu` |
| :vscode-merge: | `:vscode-merge:` | `vscode/merge` |
| :vscode-merge-into: | `:vscode-merge-into:` | `vscode/merge-into` |
| :vscode-mic: | `:vscode-mic:` | `vscode/mic` |
| :vscode-mic-filled: | `:vscode-mic-filled:` | `vscode/mic-filled` |
| :vscode-milestone: | `:vscode-milestone:` | `vscode/milestone` |
| :vscode-mirror: | `:vscode-mirror:` | `vscode/mirror` |
| :vscode-mortar-board: | `:vscode-mortar-board:` | `vscode/mortar-board` |
| :vscode-move: | `:vscode-move:` | `vscode/move` |
| :vscode-multiple-windows: | `:vscode-multiple-windows:` | `vscode/multiple-windows` |
| :vscode-music: | `:vscode-music:` | `vscode/music` |
| :vscode-mute: | `:vscode-mute:` | `vscode/mute` |
| :vscode-new-collection: | `:vscode-new-collection:` | `vscode/new-collection` |
| :vscode-new-file: | `:vscode-new-file:` | `vscode/new-file` |
| :vscode-new-folder: | `:vscode-new-folder:` | `vscode/new-folder` |
| :vscode-new-session: | `:vscode-new-session:` | `vscode/new-session` |
| :vscode-newline: | `:vscode-newline:` | `vscode/newline` |
| :vscode-no-newline: | `:vscode-no-newline:` | `vscode/no-newline` |
| :vscode-note: | `:vscode-note:` | `vscode/note` |
| :vscode-notebook: | `:vscode-notebook:` | `vscode/notebook` |
| :vscode-notebook-template: | `:vscode-notebook-template:` | `vscode/notebook-template` |
| :vscode-octoface: | `:vscode-octoface:` | `vscode/octoface` |
| :vscode-open-in-product: | `:vscode-open-in-product:` | `vscode/open-in-product` |
| :vscode-open-in-window: | `:vscode-open-in-window:` | `vscode/open-in-window` |
| :vscode-open-preview: | `:vscode-open-preview:` | `vscode/open-preview` |
| :vscode-openai: | `:vscode-openai:` | `vscode/openai` |
| :vscode-organization: | `:vscode-organization:` | `vscode/organization` |
| :vscode-output: | `:vscode-output:` | `vscode/output` |
| :vscode-package: | `:vscode-package:` | `vscode/package` |
| :vscode-paintcan: | `:vscode-paintcan:` | `vscode/paintcan` |
| :vscode-pass: | `:vscode-pass:` | `vscode/pass` |
| :vscode-pass-filled: | `:vscode-pass-filled:` | `vscode/pass-filled` |
| :vscode-percentage: | `:vscode-percentage:` | `vscode/percentage` |
| :vscode-person: | `:vscode-person:` | `vscode/person` |
| :vscode-person-add: | `:vscode-person-add:` | `vscode/person-add` |
| :vscode-piano: | `:vscode-piano:` | `vscode/piano` |
| :vscode-pie-chart: | `:vscode-pie-chart:` | `vscode/pie-chart` |
| :vscode-pin: | `:vscode-pin:` | `vscode/pin` |
| :vscode-pinned: | `:vscode-pinned:` | `vscode/pinned` |
| :vscode-pinned-dirty: | `:vscode-pinned-dirty:` | `vscode/pinned-dirty` |
| :vscode-play: | `:vscode-play:` | `vscode/play` |
| :vscode-play-circle: | `:vscode-play-circle:` | `vscode/play-circle` |
| :vscode-plug: | `:vscode-plug:` | `vscode/plug` |
| :vscode-preserve-case: | `:vscode-preserve-case:` | `vscode/preserve-case` |
| :vscode-preview: | `:vscode-preview:` | `vscode/preview` |
| :vscode-primitive-square: | `:vscode-primitive-square:` | `vscode/primitive-square` |
| :vscode-project: | `:vscode-project:` | `vscode/project` |
| :vscode-pulse: | `:vscode-pulse:` | `vscode/pulse` |
| :vscode-python: | `:vscode-python:` | `vscode/python` |
| :vscode-question: | `:vscode-question:` | `vscode/question` |
| :vscode-quote: | `:vscode-quote:` | `vscode/quote` |
| :vscode-quotes: | `:vscode-quotes:` | `vscode/quotes` |
| :vscode-radio-tower: | `:vscode-radio-tower:` | `vscode/radio-tower` |
| :vscode-reactions: | `:vscode-reactions:` | `vscode/reactions` |
| :vscode-record: | `:vscode-record:` | `vscode/record` |
| :vscode-record-keys: | `:vscode-record-keys:` | `vscode/record-keys` |
| :vscode-record-small: | `:vscode-record-small:` | `vscode/record-small` |
| :vscode-redo: | `:vscode-redo:` | `vscode/redo` |
| :vscode-references: | `:vscode-references:` | `vscode/references` |
| :vscode-refresh: | `:vscode-refresh:` | `vscode/refresh` |
| :vscode-regex: | `:vscode-regex:` | `vscode/regex` |
| :vscode-remote: | `:vscode-remote:` | `vscode/remote` |
| :vscode-remote-explorer: | `:vscode-remote-explorer:` | `vscode/remote-explorer` |
| :vscode-remove: | `:vscode-remove:` | `vscode/remove` |
| :vscode-remove-small: | `:vscode-remove-small:` | `vscode/remove-small` |
| :vscode-rename: | `:vscode-rename:` | `vscode/rename` |
| :vscode-replace: | `:vscode-replace:` | `vscode/replace` |
| :vscode-replace-all: | `:vscode-replace-all:` | `vscode/replace-all` |
| :vscode-reply: | `:vscode-reply:` | `vscode/reply` |
| :vscode-repo: | `:vscode-repo:` | `vscode/repo` |
| :vscode-repo-clone: | `:vscode-repo-clone:` | `vscode/repo-clone` |
| :vscode-repo-fetch: | `:vscode-repo-fetch:` | `vscode/repo-fetch` |
| :vscode-repo-force-push: | `:vscode-repo-force-push:` | `vscode/repo-force-push` |
| :vscode-repo-forked: | `:vscode-repo-forked:` | `vscode/repo-forked` |
| :vscode-repo-pinned: | `:vscode-repo-pinned:` | `vscode/repo-pinned` |
| :vscode-repo-pull: | `:vscode-repo-pull:` | `vscode/repo-pull` |
| :vscode-repo-push: | `:vscode-repo-push:` | `vscode/repo-push` |
| :vscode-repo-selected: | `:vscode-repo-selected:` | `vscode/repo-selected` |
| :vscode-report: | `:vscode-report:` | `vscode/report` |
| :vscode-request-changes: | `:vscode-request-changes:` | `vscode/request-changes` |
| :vscode-robot: | `:vscode-robot:` | `vscode/robot` |
| :vscode-rocket: | `:vscode-rocket:` | `vscode/rocket` |
| :vscode-root-folder: | `:vscode-root-folder:` | `vscode/root-folder` |
| :vscode-root-folder-opened: | `:vscode-root-folder-opened:` | `vscode/root-folder-opened` |
| :vscode-rss: | `:vscode-rss:` | `vscode/rss` |
| :vscode-ruby: | `:vscode-ruby:` | `vscode/ruby` |
| :vscode-run-above: | `:vscode-run-above:` | `vscode/run-above` |
| :vscode-run-all: | `:vscode-run-all:` | `vscode/run-all` |
| :vscode-run-all-coverage: | `:vscode-run-all-coverage:` | `vscode/run-all-coverage` |
| :vscode-run-below: | `:vscode-run-below:` | `vscode/run-below` |
| :vscode-run-coverage: | `:vscode-run-coverage:` | `vscode/run-coverage` |
| :vscode-run-errors: | `:vscode-run-errors:` | `vscode/run-errors` |
| :vscode-run-with-deps: | `:vscode-run-with-deps:` | `vscode/run-with-deps` |
| :vscode-save: | `:vscode-save:` | `vscode/save` |
| :vscode-save-all: | `:vscode-save-all:` | `vscode/save-all` |
| :vscode-save-as: | `:vscode-save-as:` | `vscode/save-as` |
| :vscode-screen-cut: | `:vscode-screen-cut:` | `vscode/screen-cut` |
| :vscode-screen-full: | `:vscode-screen-full:` | `vscode/screen-full` |
| :vscode-screen-normal: | `:vscode-screen-normal:` | `vscode/screen-normal` |
| :vscode-search: | `:vscode-search:` | `vscode/search` |
| :vscode-search-fuzzy: | `:vscode-search-fuzzy:` | `vscode/search-fuzzy` |
| :vscode-search-large: | `:vscode-search-large:` | `vscode/search-large` |
| :vscode-search-sparkle: | `:vscode-search-sparkle:` | `vscode/search-sparkle` |
| :vscode-search-stop: | `:vscode-search-stop:` | `vscode/search-stop` |
| :vscode-send: | `:vscode-send:` | `vscode/send` |
| :vscode-send-to-remote-agent: | `:vscode-send-to-remote-agent:` | `vscode/send-to-remote-agent` |
| :vscode-server: | `:vscode-server:` | `vscode/server` |
| :vscode-server-environment: | `:vscode-server-environment:` | `vscode/server-environment` |
| :vscode-server-process: | `:vscode-server-process:` | `vscode/server-process` |
| :vscode-session-in-progress: | `:vscode-session-in-progress:` | `vscode/session-in-progress` |
| :vscode-settings: | `:vscode-settings:` | `vscode/settings` |
| :vscode-settings-gear: | `:vscode-settings-gear:` | `vscode/settings-gear` |
| :vscode-share: | `:vscode-share:` | `vscode/share` |
| :vscode-share-window: | `:vscode-share-window:` | `vscode/share-window` |
| :vscode-shield: | `:vscode-shield:` | `vscode/shield` |
| :vscode-sign-in: | `:vscode-sign-in:` | `vscode/sign-in` |
| :vscode-sign-out: | `:vscode-sign-out:` | `vscode/sign-out` |
| :vscode-skip: | `:vscode-skip:` | `vscode/skip` |
| :vscode-smiley: | `:vscode-smiley:` | `vscode/smiley` |
| :vscode-snake: | `:vscode-snake:` | `vscode/snake` |
| :vscode-sort-precedence: | `:vscode-sort-precedence:` | `vscode/sort-precedence` |
| :vscode-source-control: | `:vscode-source-control:` | `vscode/source-control` |
| :vscode-sparkle: | `:vscode-sparkle:` | `vscode/sparkle` |
| :vscode-sparkle-filled: | `:vscode-sparkle-filled:` | `vscode/sparkle-filled` |
| :vscode-split-horizontal: | `:vscode-split-horizontal:` | `vscode/split-horizontal` |
| :vscode-split-vertical: | `:vscode-split-vertical:` | `vscode/split-vertical` |
| :vscode-squirrel: | `:vscode-squirrel:` | `vscode/squirrel` |
| :vscode-star-empty: | `:vscode-star-empty:` | `vscode/star-empty` |
| :vscode-star-full: | `:vscode-star-full:` | `vscode/star-full` |
| :vscode-star-half: | `:vscode-star-half:` | `vscode/star-half` |
| :vscode-stop-circle: | `:vscode-stop-circle:` | `vscode/stop-circle` |
| :vscode-strikethrough: | `:vscode-strikethrough:` | `vscode/strikethrough` |
| :vscode-surround-with: | `:vscode-surround-with:` | `vscode/surround-with` |
| :vscode-symbol-array: | `:vscode-symbol-array:` | `vscode/symbol-array` |
| :vscode-symbol-boolean: | `:vscode-symbol-boolean:` | `vscode/symbol-boolean` |
| :vscode-symbol-class: | `:vscode-symbol-class:` | `vscode/symbol-class` |
| :vscode-symbol-color: | `:vscode-symbol-color:` | `vscode/symbol-color` |
| :vscode-symbol-constant: | `:vscode-symbol-constant:` | `vscode/symbol-constant` |
| :vscode-symbol-enum: | `:vscode-symbol-enum:` | `vscode/symbol-enum` |
| :vscode-symbol-enum-member: | `:vscode-symbol-enum-member:` | `vscode/symbol-enum-member` |
| :vscode-symbol-event: | `:vscode-symbol-event:` | `vscode/symbol-event` |
| :vscode-symbol-field: | `:vscode-symbol-field:` | `vscode/symbol-field` |
| :vscode-symbol-file: | `:vscode-symbol-file:` | `vscode/symbol-file` |
| :vscode-symbol-interface: | `:vscode-symbol-interface:` | `vscode/symbol-interface` |
| :vscode-symbol-key: | `:vscode-symbol-key:` | `vscode/symbol-key` |
| :vscode-symbol-keyword: | `:vscode-symbol-keyword:` | `vscode/symbol-keyword` |
| :vscode-symbol-method: | `:vscode-symbol-method:` | `vscode/symbol-method` |
| :vscode-symbol-method-arrow: | `:vscode-symbol-method-arrow:` | `vscode/symbol-method-arrow` |
| :vscode-symbol-misc: | `:vscode-symbol-misc:` | `vscode/symbol-misc` |
| :vscode-symbol-namespace: | `:vscode-symbol-namespace:` | `vscode/symbol-namespace` |
| :vscode-symbol-numeric: | `:vscode-symbol-numeric:` | `vscode/symbol-numeric` |
| :vscode-symbol-operator: | `:vscode-symbol-operator:` | `vscode/symbol-operator` |
| :vscode-symbol-parameter: | `:vscode-symbol-parameter:` | `vscode/symbol-parameter` |
| :vscode-symbol-property: | `:vscode-symbol-property:` | `vscode/symbol-property` |
| :vscode-symbol-ruler: | `:vscode-symbol-ruler:` | `vscode/symbol-ruler` |
| :vscode-symbol-snippet: | `:vscode-symbol-snippet:` | `vscode/symbol-snippet` |
| :vscode-symbol-string: | `:vscode-symbol-string:` | `vscode/symbol-string` |
| :vscode-symbol-structure: | `:vscode-symbol-structure:` | `vscode/symbol-structure` |
| :vscode-symbol-variable: | `:vscode-symbol-variable:` | `vscode/symbol-variable` |
| :vscode-sync: | `:vscode-sync:` | `vscode/sync` |
| :vscode-sync-ignored: | `:vscode-sync-ignored:` | `vscode/sync-ignored` |
| :vscode-table: | `:vscode-table:` | `vscode/table` |
| :vscode-tag: | `:vscode-tag:` | `vscode/tag` |
| :vscode-target: | `:vscode-target:` | `vscode/target` |
| :vscode-tasklist: | `:vscode-tasklist:` | `vscode/tasklist` |
| :vscode-telescope: | `:vscode-telescope:` | `vscode/telescope` |
| :vscode-terminal: | `:vscode-terminal:` | `vscode/terminal` |
| :vscode-terminal-bash: | `:vscode-terminal-bash:` | `vscode/terminal-bash` |
| :vscode-terminal-cmd: | `:vscode-terminal-cmd:` | `vscode/terminal-cmd` |
| :vscode-terminal-debian: | `:vscode-terminal-debian:` | `vscode/terminal-debian` |
| :vscode-terminal-git-bash: | `:vscode-terminal-git-bash:` | `vscode/terminal-git-bash` |
| :vscode-terminal-linux: | `:vscode-terminal-linux:` | `vscode/terminal-linux` |
| :vscode-terminal-powershell: | `:vscode-terminal-powershell:` | `vscode/terminal-powershell` |
| :vscode-terminal-secure: | `:vscode-terminal-secure:` | `vscode/terminal-secure` |
| :vscode-terminal-tmux: | `:vscode-terminal-tmux:` | `vscode/terminal-tmux` |
| :vscode-terminal-ubuntu: | `:vscode-terminal-ubuntu:` | `vscode/terminal-ubuntu` |
| :vscode-text-size: | `:vscode-text-size:` | `vscode/text-size` |
| :vscode-thinking: | `:vscode-thinking:` | `vscode/thinking` |
| :vscode-three-bars: | `:vscode-three-bars:` | `vscode/three-bars` |
| :vscode-thumbsdown: | `:vscode-thumbsdown:` | `vscode/thumbsdown` |
| :vscode-thumbsdown-filled: | `:vscode-thumbsdown-filled:` | `vscode/thumbsdown-filled` |
| :vscode-thumbsup: | `:vscode-thumbsup:` | `vscode/thumbsup` |
| :vscode-thumbsup-filled: | `:vscode-thumbsup-filled:` | `vscode/thumbsup-filled` |
| :vscode-tools: | `:vscode-tools:` | `vscode/tools` |
| :vscode-trash: | `:vscode-trash:` | `vscode/trash` |
| :vscode-triangle-down: | `:vscode-triangle-down:` | `vscode/triangle-down` |
| :vscode-triangle-left: | `:vscode-triangle-left:` | `vscode/triangle-left` |
| :vscode-triangle-right: | `:vscode-triangle-right:` | `vscode/triangle-right` |
| :vscode-triangle-up: | `:vscode-triangle-up:` | `vscode/triangle-up` |
| :vscode-twitter: | `:vscode-twitter:` | `vscode/twitter` |
| :vscode-type-hierarchy: | `:vscode-type-hierarchy:` | `vscode/type-hierarchy` |
| :vscode-type-hierarchy-sub: | `:vscode-type-hierarchy-sub:` | `vscode/type-hierarchy-sub` |
| :vscode-type-hierarchy-super: | `:vscode-type-hierarchy-super:` | `vscode/type-hierarchy-super` |
| :vscode-unarchive: | `:vscode-unarchive:` | `vscode/unarchive` |
| :vscode-unfold: | `:vscode-unfold:` | `vscode/unfold` |
| :vscode-ungroup-by-ref-type: | `:vscode-ungroup-by-ref-type:` | `vscode/ungroup-by-ref-type` |
| :vscode-unlock: | `:vscode-unlock:` | `vscode/unlock` |
| :vscode-unmute: | `:vscode-unmute:` | `vscode/unmute` |
| :vscode-unverified: | `:vscode-unverified:` | `vscode/unverified` |
| :vscode-variable-group: | `:vscode-variable-group:` | `vscode/variable-group` |
| :vscode-verified: | `:vscode-verified:` | `vscode/verified` |
| :vscode-verified-filled: | `:vscode-verified-filled:` | `vscode/verified-filled` |
| :vscode-versions: | `:vscode-versions:` | `vscode/versions` |
| :vscode-vm: | `:vscode-vm:` | `vscode/vm` |
| :vscode-vm-active: | `:vscode-vm-active:` | `vscode/vm-active` |
| :vscode-vm-connect: | `:vscode-vm-connect:` | `vscode/vm-connect` |
| :vscode-vm-outline: | `:vscode-vm-outline:` | `vscode/vm-outline` |
| :vscode-vm-pending: | `:vscode-vm-pending:` | `vscode/vm-pending` |
| :vscode-vm-running: | `:vscode-vm-running:` | `vscode/vm-running` |
| :vscode-vm-small: | `:vscode-vm-small:` | `vscode/vm-small` |
| :vscode-vr: | `:vscode-vr:` | `vscode/vr` |
| :vscode-vscode: | `:vscode-vscode:` | `vscode/vscode` |
| :vscode-vscode-insiders: | `:vscode-vscode-insiders:` | `vscode/vscode-insiders` |
| :vscode-wand: | `:vscode-wand:` | `vscode/wand` |
| :vscode-warning: | `:vscode-warning:` | `vscode/warning` |
| :vscode-watch: | `:vscode-watch:` | `vscode/watch` |
| :vscode-whitespace: | `:vscode-whitespace:` | `vscode/whitespace` |
| :vscode-whole-word: | `:vscode-whole-word:` | `vscode/whole-word` |
| :vscode-window: | `:vscode-window:` | `vscode/window` |
| :vscode-window-active: | `:vscode-window-active:` | `vscode/window-active` |
| :vscode-word-wrap: | `:vscode-word-wrap:` | `vscode/word-wrap` |
| :vscode-workspace-trusted: | `:vscode-workspace-trusted:` | `vscode/workspace-trusted` |
| :vscode-workspace-unknown: | `:vscode-workspace-unknown:` | `vscode/workspace-unknown` |
| :vscode-workspace-untrusted: | `:vscode-workspace-untrusted:` | `vscode/workspace-untrusted` |
| :vscode-worktree: | `:vscode-worktree:` | `vscode/worktree` |
| :vscode-worktree-small: | `:vscode-worktree-small:` | `vscode/worktree-small` |
| :vscode-zoom-in: | `:vscode-zoom-in:` | `vscode/zoom-in` |
| :vscode-zoom-out: | `:vscode-zoom-out:` | `vscode/zoom-out` |
+306
View File
@@ -0,0 +1,306 @@
# Компоненты
Здесь показаны примеры компонентов, которые можно внедрять в документацию.
Для синтаксиса обращайся в [документацию Zensical][1] и репозиторий [этой документации][2].
[1]: https://zensical.org/docs
[2]: https://git.axenov.dev/IPTV/docs
## Мелочёвка
=== "Бейджи"
Это кастомный функционал, в основе которого лежит `inline_badges.py` в корне репозитория.
<!-- md:env SOME_VAR -->
<!-- md:arg --arg -->
<!-- md:config server.host -->
<!-- md:version 1.2.3 -->
<!-- md:default default_value -->
<!-- md:beta -->
=== "Frontmatter"
```yaml
---
title: My Page
description: Some page description
icon: material/star
tags: [tag1, tag2]
hide: [navigation, toc, path]
status: new
#status: deprecated
#status: beta
---
# My Super-Duper Page
# Markdown content goes here
```
=== "Тултипы"
:material-information-outline:{ title="текст подсказки" }
[Hover me 1](https://example.com "I'm first tooltip!")
[Hover me 2][example]
[example]: https://example.com "I'm second tooltip!"
=== "Кнопки"
[Серая кнопка](https://example.com/){ .md-button }
[Синяя кнопка](https://example.com/){ .md-button .md-button--primary }
[:fontawesome-solid-paper-plane: Кнопка серая с иконкой и подсказкой](https://example.com/){ .md-button title="текст подсказки 2" }
[:fontawesome-solid-paper-plane: Кнопка синяя с иконкой](https://example.com/){ .md-button .md-button--primary }
---
=== "Простой блок"
```python
def _render_icon(shortcode: str, md) -> str:
emoji_pattern = md.inlinePatterns.get("emoji")
if emoji_pattern is None:
return escape(shortcode)
```
=== "С аннотациями"
```python
def _render_icon(shortcode: str, md) -> str: #(1)!
emoji_pattern = md.inlinePatterns.get("emoji") #(2)!
if emoji_pattern is None:
return escape(shortcode) #(3)!
```
1. сигнатура функции
2. инициализация переменной
3. возврат результата
=== "С заголовком"
```python title="example.py"
def _render_icon(shortcode: str, md) -> str:
emoji_pattern = md.inlinePatterns.get("emoji")
if emoji_pattern is None:
return escape(shortcode)
```
=== "Нумерация с 5"
```python linenums="5"
def _render_icon(shortcode: str, md) -> str:
emoji_pattern = md.inlinePatterns.get("emoji")
if emoji_pattern is None:
return escape(shortcode)
```
=== "Выделение"
```python linenums="1" hl_lines="2-5 8 9 22"
def _render_icon(shortcode: str, md) -> str:
try:
emoji_pattern = md.inlinePatterns["emoji"]
except KeyError:
return escape(shortcode)
icon = emoji_pattern.emoji_index["emoji"].get(shortcode)
if icon is None:
return escape(shortcode)
element = emoji_pattern.generator(
emoji_pattern.emoji_index["name"],
shortcode,
None,
None,
shortcode,
"",
icon.get("category", ""),
emoji_pattern.options,
md,
)
return tostring(element, encoding="unicode", method="html")
```
---
## Врезки
=== "Полные"
!!! note "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! abstract "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! info "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! tip "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! success "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! question "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! warning "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! failure "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! danger "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! bug "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! example "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
!!! quote "Заголовок статичной врезки"
Содержимое, которое может
быть многострочным
=== "Свёрнутые"
??? note "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? abstract "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? info "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? tip "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? success "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? question "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? warning "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? failure "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? danger "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? bug "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? example "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
??? quote "Заголовок свёрнутой врезки"
Содержимое, которое может
быть многострочным
=== "Сворачиваемые"
???+ note "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ abstract "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ info "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ tip "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ success "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ question "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ warning "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ failure "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ danger "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ bug "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ example "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
???+ quote "Заголовок развёрнутой врезки"
Содержимое, которое может
быть многострочным
=== "Короткие"
!!! note
Дефолтный заголовок
!!! abstract ""
Пустой заголовок
!!! info "Пустое содержимое"
=== "Кастомные"
??? image "Врезка для скриншота"
Тут должна быть картинка
!!! boosty "Врезка Boosty"
Тут должна быть релевантная информация
!!! yoomoney "Врезка Yoomoney"
Тут должна быть релевантная информация
---
+71
View File
@@ -0,0 +1,71 @@
---
icon: material/application-brackets-outline
tags: ["iptvc", "разработка"]
---
# :material-application-brackets-outline: Среда разработки
## Быстрый старт
Для локальной разработки `iptvc` достаточно установить [Go 1.23.6+][1] и клонировать репозиторий:
```bash
git clone https://git.axenov.dev/IPTV/iptvc.git
cd iptvc
go run . serve -i playlists.ini -p 8800 --check
```
Веб-интерфейс будет доступен по адресу <http://localhost:8800>.
## Требования
| ПО | Версия | Назначение |
| ----------- | --------------------- | ------------------------------------- |
| [Go][1] | 1.23.6+ | Компиляция и запуск |
| [Make][2] | любая | Сборка через `Makefile` (опционально) |
| [Docker][3] | с `docker compose` v2 | Полное окружение (опционально) |
[1]: https://go.dev/dl/
[2]: https://www.gnu.org/software/make
[3]: https://docs.docker.com/engine/install
## Команды для разработки
```bash
# Запуск веб-сервера с фоновой проверкой
go run . serve -i playlists.ini -p 8800 --check
# Запуск проверки без веб-сервера
go run . check -i playlists.ini --repeat 1
# Сборка бинарника
go build -o iptvc .
# Проверка кода
go vet ./...
# Сборка под все платформы
make release
```
## Файлы конфигурации
Для работы `iptvc` нужны два файла рядом с бинарником:
- `playlists.ini` — список плейлистов. Формат описан в [справочнике форматов](../../common/formats/playlists.md).
- `channels.json` — правила тегов каналов. Формат описан в [справочнике форматов](../../common/formats/channels.md).
- `config.yml` — конфигурация программы. Описание — в [разделе config.yml](../../common/config/config.md).
## Полное Docker-окружение
Полная инфраструктура проекта (nginx, KeyDB, checker, docs) развёртывается через Docker.
Подробнее — в разделе [Развёртывание](../site/deploy.md).
## Дополнительные материалы
- [Компиляция из исходного кода](compile.md)
- [Построение Docker-образа](docker.md)
- [Архитектура iptvc](arch.md)
- [Развёртывание и доставка обновлений](../site/deploy.md)
@@ -31,7 +31,7 @@ icon: material/robot-outline
``` ```
telebit http 8080 telebit http 8080
``` ```
где `8080` -- порт локальной машины, на который проброшен порт 80 из контейнера `iptv-nginx`. где `8080` порт локальной машины, на который проброшен порт 80 из контейнера `iptv-nginx`.
Для выключения выполнить: Для выключения выполнить:
``` ```
telebit http telebit http
+70
View File
@@ -0,0 +1,70 @@
---
icon: material/download
tags: ["iptvc"]
---
# Установка и запуск
## Запуск готовой программы
Достаточно скачать и распаковать архив с подходящим исполняемым файлом [со страницы последнего релиза][rel_page] в любую удобную директорию:
| ОС | Скачать для `amd64` | Скачать для `arm64` |
| ------- | ---------------------------------- | ---------------------------------- |
| Linux | [linux_amd64.zip][linux_amd64] | [linux_arm64.zip][linux_arm64] |
| MacOS | [darwin_amd64.zip][darwin_amd64] | [darwin_arm64.zip][darwin_arm64] |
| Windows | [windows_amd64.zip][windows_amd64] | [windows_arm64.zip][windows_arm64] |
[rel_page]: https://git.axenov.dev/IPTV/iptvc/releases/latest
[linux_amd64]: https://git.axenov.dev/IPTV/iptvc/releases/download/latest/linux_amd64.zip
[linux_arm64]: https://git.axenov.dev/IPTV/iptvc/releases/download/latest/linux_arm64.zip
[darwin_amd64]: https://git.axenov.dev/IPTV/iptvc/releases/download/latest/darwin_amd64.zip
[darwin_arm64]: https://git.axenov.dev/IPTV/iptvc/releases/download/latest/darwin_arm64.zip
[windows_amd64]: https://git.axenov.dev/IPTV/iptvc/releases/download/latest/windows_amd64.zip
[windows_arm64]: https://git.axenov.dev/IPTV/iptvc/releases/download/latest/windows_arm64.zip
## Запуск релизного образа
Релизные docker-образы строятся для платформы `linux` и архитектур `amd64` и `arm64`.
Найти их можно здесь: <https://git.axenov.dev/IPTV/-/packages/container/iptvc>
Тег `latest` всегда соответствует последней версии, он подразумевается по умолчанию:
=== "Запуск последней версии"
```shell
docker run \
--pull always \
--name iptvc \
git.axenov.dev/iptv/iptvc \ #(1)!
КОМАНДА [АРГУМЕНТЫ]
```
1. Подразумевается `:latest`, можно указать явно
=== "Запуск другой версии"
```shell
docker run \
--pull always \
--name iptvc \
git.axenov.dev/iptv/iptvc:v1.1.3 \ #(1)!
КОМАНДА [АРГУМЕНТЫ]
```
1. Список версий доступен на [релизной странице][rel_page]
## Использование образа в Docker compose
```yaml title="compose.yml"
services:
#...
iptvc:
container_name: iptvc
image: git.axenov.dev/iptv/iptvc:latest
command: [serve] #(1)!
#...
```
1. Доступные команды и аргументы см. в [**Справочнике команд**](commands/index.md)
+73
View File
@@ -0,0 +1,73 @@
---
title: Введение
icon: octicons/sparkles-fill-16
hide: [toc]
---
# IPTV Checker (iptvc)
Это простая программа для проверки IPTV плейлистов, входящая в состав проекта m3u.su.
Поддерживает проверку как локальных файлов `*.m3u`/`*.m3u8`, так и удалённых плейлистов по прямым ссылкам, с возможностью кеширования результатов и его вывода в различных форматах.
> Программа не предназначена для хранения, воспроизведения или распространения пиратского контента.
<div class="grid cards" markdown>
- :material-flag-checkered: **Быстрый старт**
---
Коротко о главном, если не терпится
[Подробности :material-arrow-right:][start]{ .md-button .md-button--primary }
- :material-application-brackets-outline: **Запуск сайта**
---
Создайте свой агрегатор плейлистов
[Подробности :material-arrow-right:][site]{ .md-button .md-button--primary }
- :octicons-terminal-24: **Работа в терминале**
---
Как обрабатывать плейлисты без GUI
[Подробности :material-arrow-right:][cli]{ .md-button .md-button--primary }
- :material-file-cog: **Конфигурация**
---
Настройте `iptvc` для своих целей
[Подробности :material-arrow-right:][cfg]{ .md-button .md-button--primary }
</div>
[start]: quickstart.md "Перейти к разделу"
[site]: site/first-steps.md "Перейти к разделу"
[cli]: commands/index.md "Перейти к разделу"
[cfg]: ../common/config/config.md "Перейти к разделу"
<!--
## :material-cog-sync-outline: Как работает `iptvc`
Принцип её работы очень простой:
1. Получить плейлист по ссылке.
2. Полученный плейлист распарсить:
обрабатывается полученный текст в формате [m3u](../common/formats/m3u.md), из него вычленется информация о каналах и их группировке.
3. Каждый найденный канал проверить:
получить информацию по ссылке и принять решение — работает ли канал (технически) или нет.
4. Если необходимо, каждому каналу [присвоить теги](../common/formats/channels.md#доступные-теги) согласно [правилам](../common/formats/channels.md).
5. Если необходимо, закешировать результаты проверок.
Во время работы программа пишет лог проверки плейлиста и каналов в достаточно компактном человекочитаемом виде, а в конце пишет результаты проверок.
При желании, можно включить более подробный вывод, чтобы тщательно следить за ходом проверки в реальном времени, а также вывод результатов в машиночитаемом виде (в формате json).
Подробности см. в разделе [Команда `check`](../iptvc/commands/check.md).
-->
+85
View File
@@ -0,0 +1,85 @@
---
icon: material/flag-checkered
tags: ["iptvc"]
---
# :material-flag-checkered: Быстрый старт
Для простоты представим, что программа скачана и распакована в любую директорию и вы находитесь в ней.
Используйте тот способ запуска, который выбрали на шаге [установки](install.md).
Ниже представлены лишь частые примеры запуска программы с разными аргументами под разные случаи.
## Проверить плейлист по прямой ссылке
```
./iptvc check -u https://example.com/pls.m3u
./iptvc check --url https://example.com/pls.m3u
```
## Проверить файл плейлиста с диска
```
./iptvc check -f /home/user/pls.m3u
./iptvc check --file /home/user/pls.m3u
```
## Проверить плейлист по короткому коду из [`playlists.ini`](../common/formats/playlists.md)
```
./iptvc check -c X
./iptvc check --code X
```
Файл `playlists.ini` должен лежать рядом с `iptvc`.
Если файл лежит в другой директории, то можно явно указать путь к нему:
```
./iptvc check --ini /home/user/playlists.ini --code X
```
Если ini-файл не будет найден, программа предупредит об этом.
## Присвоить каналам тематические теги
Для этого рядом с `iptvc` должен лежать файл [channels.json](../common/formats/channels.md).
Если файл лежит в другой директории, то можно указать её явно:
```
./iptvc check --tags /home/user/channels.json
```
Если json-файл не будет найден, то программа предупредит о том, что теги не будут присвоены, и продолжит работу.
## Проверить несколько плейлистов одновременно
Для этого можно комбинировать все аргументы, перечисленные выше, с учётом особенностей их работы:
```shell
./iptvc check \
--ini /home/user/p.ini \ #(1)!
--tags /home/user/c.json \ #(2)!
--code Y \ #(3)!
--file /home/user/tv.m3u \ #(4)!
--url https://example.com/pls1.m3u \ #(5)!
-u https://example.com/pls2.m3u #(6)!
```
1. Из этого файла будет взят список плейлистов
2. Из этого файла будут взяты правила для присвоения тегов каналам
3. Из ini-списка будет проверен только плейлист с кодом `Y`
4. Это отдельный файл плейлиста на диске, который будет проверен в дополнение к основному списку
5. Плейлист на каком-то удалённом сервере, который будет загружен и проверен вместе с прошлыми двумя
6. Ещё один по ссылке, просто через короткий вариант аргумента `--url`
Символ `\` нужен только для наглядного разделения аргументов на несколько строк.
Всё это можно писать в одну строку.
Переданные плейлисты будут обработаны в следующем порядке:
1. локальные файлы (`-f|--file`);
2. по ссылкам (`-u|--url`);
3. по кодам из ini-файла (`-i|--ini`, `-c|--code`).
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

@@ -5,10 +5,10 @@ tags: ["плееры", "плейлисты"]
# :material-television-play: Как подключить плейлист # :material-television-play: Как подключить плейлист
1. Найти какой-нибудь [плеер](./players.md) 1. Найти какой-нибудь [плеер](../../common/players.md)
2. Узнать как в него добавить плейлист по ссылке 2. Узнать как в него добавить плейлист по ссылке
3. Найти желаемый плелист из [списка](./list.md) 3. Найти желаемый плелист из [списка](./list.md)
4. Найти на странице ["Ссылку для ТВ"](details.md#ссылка-для-тв) и ввести (скопировать) её в поле ввода адреса в плеере 4. Найти на странице ["Ссылку для ТВ"](details.md#shortlink) и ввести (скопировать) её в поле ввода адреса в плеере
Для некоторых [плееров](./players.md) уже есть информация как добавить плейлист. Для некоторых [плееров](../../common/players.md) уже есть информация как добавить плейлист.
+191
View File
@@ -0,0 +1,191 @@
---
icon: material/upload-network
tags: ["iptvc", "docker", "deploy"]
---
# :material-upload-network: Развёртывание и доставка обновлений
В этом разделе описан фактический порядок настройки окружения `iptv` с приложением `iptvc`, хранилищем KeyDB и сайтом документации.
## Требования { id="requirements" }
- [Docker](https://docs.docker.com/engine/install/) с плагином `docker compose` версии 2
- Git и доступ к репозиторию `git.axenov.dev/IPTV`
- Права на запись в каталог данных KeyDB
- Доступ к registry `git.axenov.dev`, если вы публикуете образ `iptvc`
!!! warning "Только docker compose v2"
Используйте команду `docker compose`.
Устаревшая команда `docker-compose` в этом сценарии не поддерживается.
## Подготовка репозитория { id="repository" }
Клонируйте основной репозиторий и перейдите в его каталог:
```bash
git clone https://git.axenov.dev/IPTV.git
cd IPTV
```
В корне проекта должны находиться `compose.yml`, `.env`, `config.yml`, `playlists.ini`, `channels.json` и каталог `docker/keydb`.
Исходный код приложения располагается в `iptvc/`, а документации — в `docs/`.
## Настройка файлов { id="configuration" }
Создайте файлы локальной конфигурации на основе примеров:
```bash
cp .env.example .env
cp iptvc/config.yml.example config.yml
```
Отредактируйте `config.yml`.
В контейнере `iptvc` он подключается как `/app/config.yml`.
Минимально проверьте следующие параметры:
- `app.playlists` — путь к файлу плейлистов;
- `app.tags` — путь к файлу тегов каналов;
- `server.host` и `server.port` — адрес и порт веб-интерфейса;
- `cache.enabled` и параметры `cache` — использование KeyDB;
- `site.base-url` — внешний адрес приложения.
Скопируйте входные данные плейлистов в корень окружения:
```bash
cp /path/to/playlists.ini ./playlists.ini
cp /path/to/channels.json ./channels.json
```
Эти файлы монтируются в контейнер как `/app/playlists.ini` и `/app/channels.json`.
В `.env` задаются параметры приложения из `iptvc/.env.example`, включая `SITE_BASE_URL`, `SERVER_PORT`, `CACHE_ENABLED`, `CACHE_HOST`, `CACHE_PORT`, `CACHE_DB` и `CACHE_TTL`.
Для подключения к KeyDB из контейнера укажите имя сервиса `keydb` в `CACHE_HOST`, а не `localhost`.
## Состав окружения { id="services" }
Файл `compose.yml` запускает три сервиса:
| Сервис | Образ | Назначение |
| ------- | -------------------------------------- | ------------------------------------------- |
| `iptvc` | `git.axenov.dev/iptv/iptvc:latest` | Веб-интерфейс и фоновая проверка плейлистов |
| `keydb` | `eqalpha/keydb:latest` | Кеш результатов проверки |
| `docs` | `git.axenov.dev/iptv/iptv-docs:latest` | Сайт документации |
Сервис `iptvc` публикует порт `8800`, а `docs` — порт `8801`.
KeyDB публикует порт, заданный `KEYDB_PORT`, по умолчанию `6379`.
Сервис `iptvc` зависит от `keydb` и запускается с командой `serve --check --repeat 0`.
Это поднимает веб-сервер и запускает бесконечную фоновую проверку.
## Запуск { id="start" }
После подготовки файлов соберите и запустите окружение из корня проекта:
```bash
docker compose up -d --build
```
Проверьте состояние контейнеров и журналы:
```bash
docker compose ps
docker compose logs -f iptvc
```
Веб-интерфейс приложения доступен на `http://localhost:8800`.
Документация доступна на `http://localhost:8801`.
Для остановки окружения выполните:
```bash
docker compose down
```
## Сборка образа iptvc { id="image" }
Для сборки и публикации образа используйте цели Makefile в каталоге `iptvc/`:
```bash
cd iptvc
# Сборка одноархитектурного образа (linux/amd64 по умолчанию)
make image
# Сборка под arm64
make image GOARCH=arm64
# Сборка с указанием тега
make image IMAGE_TAG=v1.2.3
```
Для публикации multi-arch манифеста (linux/amd64 + linux/arm64):
```bash
make image-all
```
Цель `image-all` всегда отправляет образ в registry — это ограничение `docker buildx`: multi-arch манифест нельзя загрузить в локальный Docker daemon.
Перед публикацией войдите в registry, если это требуется вашей настройкой:
```bash
docker login git.axenov.dev
```
!!! warning "Рабочее дерево Git"
Версия и коммит вшиваются в бинарь из `git describe` и `git rev-parse` на хосте в момент запуска `make`.
Перед сборкой убедитесь, что рабочая копия чистая и находится на нужном теге или коммите.
## Обновление { id="update" }
После изменения конфигурации или исходного кода пересоберите и перезапустите сервисы:
```bash
docker compose up -d --build
```
Чтобы пересобрать только приложение `iptvc`:
```bash
docker compose build iptvc
docker compose up -d iptvc
```
Чтобы использовать опубликованный образ вместо локальной сборки, загрузите его и пересоздайте сервис:
```bash
docker compose pull iptvc
docker compose up -d iptvc
```
Обновление документации выполняется пересборкой сервиса `docs`:
```bash
docker compose build docs
docker compose up -d docs
```
## Диагностика { id="diagnostics" }
Для просмотра журналов отдельных сервисов используйте:
```bash
docker compose logs -f iptvc
docker compose logs -f keydb
docker compose logs -f docs
```
Для проверки конфигурации Compose выполните:
```bash
docker compose config
```
Если `iptvc` не подключается к кешу, проверьте, что в `.env` параметр `CACHE_HOST` имеет значение `keydb`, а сервис `keydb` запущен.
@@ -7,7 +7,7 @@ tags: ["сайт", "статусы", "каналы"]
Страница содержит подробности об одном конкретном плейлисте. Страница содержит подробности об одном конкретном плейлисте.
В её заголовке указано [название плейлиста](../formats/playlists.md#name). В её заголовке указано [название плейлиста](../../common/formats/playlists.md#name).
Ниже страница разделена на две части: слева две вкладки с информацией и список каналов справа. Ниже страница разделена на две части: слева две вкладки с информацией и список каналов справа.
@@ -15,31 +15,31 @@ tags: ["сайт", "статусы", "каналы"]
## Вкладка "Основная информация" ## Вкладка "Основная информация"
![Вкладка "Основная информация"](../assets/img/pls-details/tab1.jpg) ![Вкладка "Основная информация"](_assets/pls-details/tab1.jpg)
На этой вкладке выводится таблица со следующими строками: На этой вкладке выводится таблица со следующими строками:
* **Код** -- короткий уникальный [код плейлиста](../formats/playlists.md#code); * **Код** короткий уникальный [код плейлиста](../../common/formats/playlists.md#code);
* **Описание** -- [описание плейлиста](../formats/playlists.md#desc) (при наличии); * **Описание** [описание плейлиста](../../common/formats/playlists.md#desc) (при наличии);
* **Ccылка для ТВ** -- короткая ссылка, которую можно использовать для [подключения плейлиста](../common/connect.md), подробнее о ней см. ниже; * **Ccылка для ТВ** короткая ссылка, которую можно использовать для [подключения плейлиста](connect.md), подробнее о ней см. ниже;
* **Источник** -- [ссылка на ресурс](../formats/playlists.md#src), где была найдена ссылка на плейлист (при наличии); * **Источник** [ссылка на ресурс](../../common/formats/playlists.md#src), где была найдена ссылка на плейлист (при наличии);
* **Наполнение**: * **Наполнение**:
* группы -- количество групп, на которые поделены каналы; * группы количество групп, на которые поделены каналы;
* каналы -- количества каналов общее, онлайн и оффлайн; * каналы количества каналов общее, онлайн и оффлайн;
(всё по нулям, если плейлист <span class="badge offline">offline</span>) (всё по нулям, если плейлист <span class="badge offline">offline</span>)
* **Возможности** -- наличие программы передач и перемотки каналов; * **Возможности** наличие программы передач и перемотки каналов;
* **M3U** -- [прямая ссылка](../formats/playlists.md#pls) на плейлист; * **M3U** [прямая ссылка](../../common/formats/playlists.md#pls) на плейлист;
* **Проверка плейлиста** -- дата и время последней [проверки](../common/checks.md) плейлиста с помощью [iptvc](../iptvc/how-it-works.md); * **Проверка плейлиста** дата и время последней [проверки](../../aggregator/checks.md) плейлиста с помощью [iptvc](../overview.md);
* **Ошибка проверки** -- текст ошибки, которая возникла при проверке * **Ошибка проверки** текст ошибки, которая возникла при проверке
(только если плейлист <span class="badge offline">offline</span>) (только если плейлист <span class="badge offline">offline</span>)
Если при проверке плейлиста возникла ошибка, то она будет отображена красным цветом сразу под заголовком: Если при проверке плейлиста возникла ошибка, то она будет отображена красным цветом сразу под заголовком:
??? quote "Скриншот страницы с ошибкой" ??? quote "Скриншот страницы с ошибкой"
![Страница с ошибкой проверки плейлиста](../assets/img/pls-details/error.jpg) ![Страница с ошибкой проверки плейлиста](_assets/pls-details/error.jpg)
!!! info !!! info
Если в тексте ошибки фигурирует слово `Timeout` и плейлист <span class="badge offline">offline</span> -- это ерунда. Если в тексте ошибки фигурирует слово `Timeout` и плейлист <span class="badge offline">offline</span> это ерунда.
Скорее всего, при следующей проверке статус позеленеет. Скорее всего, при следующей проверке статус позеленеет.
Просто в момент проверки сервер не получил файл плейлиста вовремя, а т. к. долго ждать он не может, поэтому плюнул и пошёл проверять другие. Просто в момент проверки сервер не получил файл плейлиста вовремя, а т. к. долго ждать он не может, поэтому плюнул и пошёл проверять другие.
@@ -49,27 +49,23 @@ tags: ["сайт", "статусы", "каналы"]
## Вкладка "Исходный текст" ## Вкладка "Исходный текст"
![Вкладка "Исходный текст"](../assets/img/pls-details/tab2.jpg) ![Вкладка "Исходный текст"](_assets/pls-details/tab2.jpg)
Здесь выводится плейлист как он есть. Здесь выводится плейлист как он есть.
Над этим текстом -- две кнопки: Над этим текстом две кнопки:
* зелёная с кодом плейлиста для скачивания файла; * зелёная с кодом плейлиста для скачивания файла;
* нажатие на **QR-код** покажет, внезапно, QR-код, в который закодирована "Ссылка для ТВ". * нажатие на **QR-код** покажет, внезапно, QR-код, в который закодирована "Ссылка для ТВ".
## Список каналов ## Список каналов
![Cписок каналов](../assets/img/pls-details/ch-list.jpg)
В заголовке пишется их общее количество. В заголовке пишется их общее количество.
Если общее количество каналов 500 и более, то под заголовком отобразится подсказка, чтобы ты не убегал раньше времени. ![Cписок каналов](_assets/pls-details/ch-list.jpg)
Надо просто подождать несколько секунд, список догрузится и подсказка исчезнет.
??? quote "Скриншот подсказки" В списке всегда отображается не более 100 каналов.
!!! success "Да, это недоработка, подпёртая костылём, но это беспокоит меня меньше всего."
Может быть когда-нибудь сделаю лучше. Или нет. Воспользуйтесь поиском, чтобы найти интересующий.
![Скриншот подсказки над списком каналов](../assets/img/pls-details/hint.jpg)
### Поиск каналов ### Поиск каналов
@@ -77,29 +73,28 @@ tags: ["сайт", "статусы", "каналы"]
Под заголовком есть **выпадающий список групп**. Под заголовком есть **выпадающий список групп**.
Он отображается только если плейлист поделён на группы. Он отображается только если плейлист поделён на группы.
Справа -- **кнопка сброса** для отображения всех каналов. Справа **кнопка сброса** для отображения всех каналов.
Под списком групп расположилась **строка поиска**. Под списком групп расположилась **строка поиска**.
Она есть вообще всегда. Она есть вообще всегда.
Туда можно начать вводить название канала, и по мере ввода список будет сужаться. Туда можно начать вводить название канала, и по мере ввода список будет сужаться.
Справа от строки поиска есть **кнопки фильтрации каналов по их статусу**. Справа от строки поиска есть **кнопки фильтрации каналов по их статусу**.
Справа -- **кнопка сброса** для отображения всех каналов. Справа **кнопка сброса** для отображения всех каналов.
Под строкой поиска есть [**облако тегов**](../formats/channels.md#доступные-теги). Под строкой поиска есть [**облако тегов**](../../common/formats/channels.md#доступные-теги).
!!! question inline end "Про теги" !!! question inline end "Про теги"
Откуда они там появляются, можешь прочесть [здесь](../common/how-it-works.md) и [здесь](../iptvc/how-it-works.md). Откуда они там появляются, можешь прочесть [здесь](../../common/index.md) и [здесь](../overview.md).
На любой из них можно нажать, и тогда в списке останутся каналы только с выбранными тегами. На любой из них можно нажать, и тогда в списке останутся каналы только с выбранными тегами.
Выбранные теги подсвечиваются серым. Выбранные теги подсвечиваются серым.
**Сбросить** выбор можно повторным нажатием на каждый, либо кнопкой сброса у строки поиска. **Сбросить** выбор можно повторным нажатием на каждый, либо кнопкой сброса у строки поиска.
??? quote "Пример фильтрации" ??? quote "Пример фильтрации"
![Скриншот используемого фильтра списка каналов](../assets/img/pls-details/filter.jpg) ![Скриншот используемого фильтра списка каналов](_assets/pls-details/filter.jpg)
<a id="ссылка-для-тв"></a> ## Ссылка для ТВ { id="shortlink" }
## Ссылка для ТВ
Она может быть задана в нескольких форматах. Она может быть задана в нескольких форматах.
Поясню базовые принципы формирования адреса: Поясню базовые принципы формирования адреса:
@@ -107,7 +102,7 @@ tags: ["сайт", "статусы", "каналы"]
1. необязателен префикс протокола `http://` или `https://` перед доменом 1. необязателен префикс протокола `http://` или `https://` перед доменом
2. обязателен домен `m3u.su` 2. обязателен домен `m3u.su`
3. обязателен `/код` плейлиста после домена 3. обязателен `/код` плейлиста после домена
4. необязателен постфикс расширения после кода `.m3u` или `.m3u8` 4. необязателен суффикс расширения после кода `.m3u` или `.m3u8`
На примере ниже я наглядно покажу все возможные ссылки на один и тот же плейлист с кодом `ru`: На примере ниже я наглядно покажу все возможные ссылки на один и тот же плейлист с кодом `ru`:
@@ -123,19 +118,19 @@ m3u.su/ru.m3u
m3u.su/ru m3u.su/ru
``` ```
!!! info "" !!! info "Адрес может быть любым"
Запоминать их не надо. Смотря как будет развёрнут сайт, ты можешь заходить на localhost, либо по прямому IP-адресу или доменному имения.
Главное помнить как они формируются. См. раздел [**Развёртывание**](deploy.md) для подробностей.
По идее, можешь использовать любую сылку из подобных, т. к. технически они отработают одинаково. По идее, можешь использовать любую ссылку из подобных, т. к. технически они отработают одинаково.
А вот твой [плеер](players.md) может не принять какую-то из них. А вот твой [плеер](../../common/players.md) может не принять какую-то из них.
Так что, если не подойдёт один формат, используй другой -- добавь префикс или суффикс. Так что, если не подойдёт один формат, используй другой добавь префикс или суффикс.
Префикс плееру требуется чаще всего, потому что он при добавлении плейлиста проверяет -- а ссылку ли мне вообще предоставил пользователь? Префикс плееру требуется чаще всего, потому что он при добавлении плейлиста проверяет а ссылку ли мне вообще предоставил пользователь?
По наличию суффикса плеер может определить -- а прямая ли это ссылка на файл плейлиста? По наличию суффикса плеер может определить а прямая ли это ссылка на файл плейлиста?
Технически -- нет, непрямая, потому что файла плейлиста у меня на сервере нет физически и сервер должен сделать редирект уже на сам плейлист. Технически нет, непрямая, потому что файла плейлиста у меня на сервере нет физически и сервер должен сделать редирект уже на сам плейлист.
Но благодаря такой обманке плеер его наверняка подгрузит. Но благодаря такой обманке плеер его наверняка подгрузит.
Или нет. Или нет.
+417
View File
@@ -0,0 +1,417 @@
---
title: Первые шаги
icon: material/rocket-launch
tags: ["iptvc", "serve", "сайт"]
---
# :material-rocket-launch: Первые шаги для запуска сайта
В этой статье мы шаг за шагом запустим собственный сайт-агрегатор IPTV-плейлистов — от простейшего варианта до полной конфигурации с кешем и тонкой настройкой проверки.
Программа `iptvc` уже должна быть [установлена](../install.md).
Все команды ниже выполняются в терминале из директории, где лежит бинарник.
---
## Шаг 1. Запускаем пустой сайт
Минимальный запуск — одна команда:
```bash
./iptvc serve
```
Сайт откроется на `http://localhost:8800`.
Это пустая страница: нет ни одного плейлиста, потому что программе пока неоткуда их взять.
Чтобы изменить порт или хост, не трогая файл конфигурации:
```bash
./iptvc serve -p 3000 --host 0.0.0.0
```
Подробнее об этих флагах — в [документации команды `serve`](../commands/serve.md).
---
## Шаг 2. Добавляем плейлисты
Список плейлистов описывается в файле [`playlists.ini`](../../common/formats/playlists.md).
Создадим его рядом с `iptvc`:
```ini title="playlists.ini"
[ru]
name = Российские каналы
desc = Основные федеральные каналы
pls = 'https://example.com/ru.m3u'
src = 'https://example.com/ru-playlist'
[movies]
name = Фильмы
pls = 'https://example.com/movies.m3u'
```
Каждая секция `[code]` — это плейлист.
Код используется в коротких ссылках вида `http://localhost:8800/ru`.
Параметр `pls` обязателен, остальные — по желанию.
Теперь запустим:
```bash
./iptvc serve
```
Сайт покажет оба плейлиста, но их статус — `unknown` (неизвестно).
Это нормально: программа знает о них, но ещё не проверяла.
Чтобы статусы появились, нужно включить фоновую проверку.
!!! tip "Путь к ini-файлу"
Если файл лежит не рядом с программой, укажите путь через флаг [`-i`](../commands/serve.md#ini) или в [`config.yml`](../../common/config/config.md) → `app.playlists`.
---
## Шаг 3. Включаем фоновую проверку
Без проверки сайт просто показывает список.
Чтобы плейлисты и каналы проверялись автоматически, добавим флаг `--check`:
```bash
./iptvc serve --check
```
Теперь программа в фоне загружает каждый плейлист, парсит каналы и проверяет их доступность.
Результаты сразу попадают в оперативную память и отображаются на сайте.
Можно настроить интервал между циклами проверки:
```bash
# пауза 120 секунд между циклами, бесконечно
./iptvc serve --check --playlists-all-cooldown 120
# проверить один раз и остановить
./iptvc serve --check --repeat 1
```
Если не хочется каждый раз писать `--check`, можно включить проверку через [`config.yml`](../../common/config/config.md):
```yaml title="config.yml"
check:
start-on-serve: true
```
Тогда обычный `./iptvc serve` автоматически запустит фоновую проверку.
---
## Шаг 4. Настраиваем внешний вид сайта
Сайт можно настроить под себя: заголовок, иконку, навигацию в шапке и ссылки в подвале.
Всё это — в секции [`site`](../../common/config/config.md) файла `config.yml`.
```yaml title="config.yml"
site:
base-url: http://localhost:8800
repo-url: https://git.axenov.dev/IPTV
page-size: 20 # пагинация по 20 плейлистов на страницу (0 — без пагинации)
favicon: /favicon.ico # путь к иконке
header:
title: Мой IPTV # заголовок в шапке и вкладке браузера
menu:
- title: Документация
url: /docs
icon: document-text-outline
- title: Telegram
icon: bullhorn-variant-outline
children:
- title: Канал
url: https://t.me/my_channel
icon: megaphone-outline
- title: Чат
url: https://t.me/my_chat
icon: chatbubbles-outline
footer:
links:
- title: Исходники
url: https://git.axenov.dev/IPTV
icon: code-slash-outline
- title: Мой сайт
url: https://example.com
icon: person-outline
```
--8<-- "icons.md"
!!! note "base-url"
Параметр `base-url` используется для формирования внутренних ссылок.
Если публикуете сайт на домене, укажите его здесь, например `https://my-iptv.ru`.
---
## Шаг 5. Добавляем теги каналам
Теги помогают посетителям находить каналы по темам: спорт, фильмы, музыка и так далее.
Правила описываются в файле [`channels.json`](../../common/formats/channels.md).
```json title="channels.json"
[
{
"tvg-id": "^ru-",
"tags": ["russian"]
},
{
"title": "спорт",
"tags": ["sport"]
},
{
"title": "кино|фильм",
"tags": ["film"]
}
]
```
Путь к файлу указывается в [`config.yml`](../../common/config/config.md) → `app.tags` или через флаг [`-t`](../commands/serve.md#tags):
```bash
./iptvc serve --check -t /path/to/channels.json
```
Полный список доступных тегов — в [справочнике по channels.json](../../common/formats/channels.md#доступные-теги).
---
## Шаг 6. Подключаем кеш (KeyDB/Redis)
По умолчанию результаты проверки хранятся только в оперативной памяти.
Если программу перезапустить — все результаты пропадут, и плейлисты снова станут `unknown` до следующей проверки.
Кеш решает эту проблему: результаты сохраняются в KeyDB (или Redis) и переживают перезапуск.
Включается одной строкой в [`config.yml`](../../common/config/config.md):
```yaml title="config.yml"
cache:
enabled: true
host: localhost
port: 6379
ttl: 1800 # секунды (по умолчанию 30)
```
Или через переменные окружения:
```bash
CACHE_ENABLED=true CACHE_TTL=3600 ./iptvc serve --check
```
Или через флаги:
```bash
./iptvc serve --check --cache-enabled --cache-host 192.168.1.10 --cache-ttl 3600
```
!!! tip "KeyDB или Redis"
KeyDB — это форк Redis, полностью совместимый по протоколу.
Подойдёт любой из них.
Если кеш включён, но сервер недоступен — сайт продолжит работать, просто без кеширования.
---
## Шаг 7. Тонкая настройка проверки
Когда плейлистов много, полезно управлять параллелизмом, таймаутами и задержками.
Все параметры — в секции [`check`](../../common/config/config.md) файла `config.yml`.
### Параллелизм
```yaml title="config.yml"
check:
playlists:
max-routines: 10 # сколько плейлистов проверять одновременно
per-routine: 5 # сколько плейлистов в одной процедуре
channels:
max-routines: 100 # сколько каналов проверять одновременно
per-routine: 20 # сколько каналов в одной процедуре
```
Чем больше значения — тем быстрее проверка, но выше нагрузка на процессор и сеть.
Начните со значений по умолчанию и увеличивайте при необходимости.
### Таймауты
```yaml title="config.yml"
check:
playlists:
timeout: 10 # секунд на загрузку плейлиста
channels:
timeout: 10 # секунд на проверку одного канала
byte-range: 512 # сколько байт скачать от сервера канала
```
Если плейлисты или каналы медленные, увеличьте `timeout`.
Если сервер блокирует большие запросы — уменьшите `byte-range`.
### Задержки (cooldown)
Чтобы не перегружать серверы-источники, между проверками можно делать паузы.
Параметры поддерживают как фиксированное значение, так и диапазон `[min, max]` — тогда пауза будет случайной при каждом проходе:
```yaml title="config.yml"
check:
playlists:
all-cooldown: 5 # 5 секунд после всех плейлистов
one-cooldown: [1, 3] # 1–3 секунды после каждого плейлиста
channels:
cooldown: 0 # 0 секунд после каждого канала
```
### User-Agent
Некоторые серверы блокируют запросы без правильного User-Agent.
Можно указать один или несколько — тогда при каждом запросе будет выбран случайный:
```yaml title="config.yml"
check:
playlists:
user-agent:
- Mozilla/5.0 WINK/1.31.1 (AndroidTV/9) HlsWinkPlayer
- Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36
channels:
user-agent: Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36
```
Все эти параметры можно также задавать через [переменные окружения](../../common/config/config.md) или [CLI-флаги](../commands/serve.md) — они имеют наивысший приоритет.
---
## Полный пример
Соберём всё вместе в одном `config.yml`:
```yaml title="config.yml"
app:
timezone: GMT+3
debug: false
log_level: info
playlists: ./playlists.ini
tags: ./channels.json
server:
host: 0.0.0.0
port: 8800
site:
base-url: https://my-iptv.ru
repo-url: https://git.axenov.dev/IPTV
page-size: 20
favicon: /favicon.ico
header:
title: Мой IPTV
menu:
- title: Статус
url: https://status.my-iptv.ru
icon: pulse-outline
- title: Документация
url: /docs
icon: document-text-outline
- title: Telegram
icon: bullhorn-variant-outline
children:
- title: Канал
url: https://t.me/my_channel
icon: megaphone-outline
- title: Чат
url: https://t.me/my_chat
icon: chatbubbles-outline
footer:
links:
- title: Исходники
url: https://git.axenov.dev/IPTV
icon: code-slash-outline
- title: Контакты
url: https://example.com
icon: person-outline
check:
start-on-serve: true
playlists:
user-agent:
- Mozilla/5.0 WINK/1.31.1 (AndroidTV/9) HlsWinkPlayer
- Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36
timeout: 10
all-cooldown: 5
one-cooldown: [1, 3]
max-routines: 10
per-routine: 5
channels:
user-agent: Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36
timeout: 10
byte-range: 512
cooldown: 0
max-routines: 100
per-routine: 20
cache:
enabled: true
host: localhost
port: 6379
ttl: 3600
```
Запуск:
```bash
./iptvc serve
```
Поскольку `start-on-serve: true`, фоновая проверка запустится автоматически.
Кеш включён, так что результаты переживут перезапуск.
Сайт доступен на `http://0.0.0.0:8800`.
---
## Docker
Удобно запускать сайт в контейнере. Образ `iptvc` уже включает бинарник:
```yaml title="compose.yml"
services:
iptvc:
image: git.axenov.dev/iptv/iptvc:latest
command: [serve]
ports:
- "8800:8800"
volumes:
- ./config.yml:/app/config.yml:ro
- ./playlists.ini:/app/playlists.ini:ro
- ./channels.json:/app/channels.json:ro
environment:
- CHECK_START_ON_SERVE=true
- CACHE_ENABLED=true
- CACHE_HOST=keydb
- CACHE_PORT=6379
keydb:
image: eqalpha/keydb:latest
restart: unless-stopped
```
```bash
docker compose up -d
```
Подробнее об установке образа — в [документации по установке](../install.md).
---
## Краткая шпаргалка
| Задача | Как |
| ----------------------------- | ---------------------------------------------------- |
| Запустить сайт | `./iptvc serve` |
| С проверкой плейлистов | `./iptvc serve --check` |
| На другом порту | `./iptvc serve -p 3000` |
| С ini-файлом из другого места | `./iptvc serve -i /path/to/playlists.ini` |
| С кешем | `./iptvc serve --check --cache-enabled` |
| Один цикл проверки | `./iptvc serve --check --repeat 1` |
| Пауза 2 минуты между циклами | `./iptvc serve --check --playlists-all-cooldown 120` |
| Подробные логи | `./iptvc serve --check --verbose` |
Полный список параметров — в [справочнике по `config.yml`](../../common/config/config.md), [переменным окружения](../../common/config/config.md) и [команде `serve`](../commands/serve.md).
@@ -5,10 +5,10 @@ tags: ["плейлисты", "каналы", "теги", "iptvc", "плееры"
# :material-cogs: Как работает сервис # :material-cogs: Как работает сервис
1. В специальном файле [playlists.ini](../formats/playlists.md) описываются плейлисты, которые кем-то опубликованы в интернете. 1. В специальном файле [playlists.ini](../../common/formats/playlists.md) описываются плейлисты, которые кем-то опубликованы в интернете.
Каждому плейлисту присваивается свой уникальный **короткий код**. Каждому плейлисту присваивается свой уникальный **короткий код**.
2. В специальном файле [channels.json](../formats/channels.md) описываются **ключевые слова** (метки, теги), которые характеризуют каналы. 2. В специальном файле [channels.json](../../common/formats/channels.md) описываются **ключевые слова** (метки, теги), которые характеризуют каналы.
3. В фоновом режиме [работает ПО](../iptvc/how-it-works.md), которое периодически [проверяет все плейлисты](checks.md) из п. 1 и **присваивает теги** каналам из п. 2. 3. В фоновом режиме [работает ПО](../overview.md), которое периодически [проверяет все плейлисты](../../aggregator/checks.md) из п. 1 и **присваивает теги** каналам из п. 2.
4. На главной странице сайта выводится [весь список плейлистов](list.md), которые описаны в п. 1: с тегами, описаниями и короткими кодами. 4. На главной странице сайта выводится [весь список плейлистов](list.md), которые описаны в п. 1: с тегами, описаниями и короткими кодами.
5. Каждому плейлисту на сайте посвящена [своя страничка](details.md), где отображаются результаты его проверки, проверки его каналов (с присвоенными тегами) и пр. 5. Каждому плейлисту на сайте посвящена [своя страничка](details.md), где отображаются результаты его проверки, проверки его каналов (с присвоенными тегами) и пр.
6. Когда пользователь [обращается к плейлисту](connect.md) по короткому коду (например, `https://m3u.su/xyz`), то происходит **переадресация** на исходный плейлист. 6. Когда пользователь [обращается к плейлисту](connect.md) по короткому коду (например, `https://m3u.su/xyz`), то происходит **переадресация** на исходный плейлист.
+36
View File
@@ -0,0 +1,36 @@
---
icon: fontawesome/solid/list-check
tags: ["сайт", "плейлисты"]
---
# :fontawesome-solid-list-check: Список плейлистов
Это главная страница сайта.
![Скриншот с примером главной страницы на десктопе](../_assets/img/pls-list/pc.jpg)
Наверху отображаются:
* дата последнего изменения файла [playlists.ini](../../common/formats/playlists.md)
* общее количество плейлистов и с разделением по статусам.
Ниже — спиcок плейлистов.
## Из чего состоит список
* **Код** — короткий уникальный [код плейлиста](../../common/formats/playlists.md#code)
* **Информация о плейлисте**
* [статус плейлиста](../../aggregator/checks.md#playlists)
* может быть [значок 18+](../../aggregator/checks.md#adult)
* [название плейлиста](../../common/formats/playlists.md#name) — ссылка на [страницу плейлиста](details.md)
под ним:
* [иконки возможностей плейлиста](../../aggregator/checks.md#extra) (только при статусе <span class="badge online">online</span>)
* [описание плейлиста](../../common/formats/playlists.md#desc) (при наличии)
* [список тегов](../../common/formats/channels.md#доступные-теги), собранный со всех каналов после их проверки (только при статусе <span class="badge online">online</span>)
* ещё одна ссылка на [страницу плейлиста](details.md)
* **Каналов** — фактическое количество каналов в плейлисте (только при статусе <span class="badge online">online</span>) или 0 (при других статусах)
* **Ссылка для ТВ** — [короткая ссылка](details.md#shortlink), которую можно использовать для [подключения плейлиста](connect.md).
В зависимости от ширины экрана, для экономии места может быть скрыто описание с иконками возможностей и короткая ссылка.
![Скриншот с примером главной страницы на смартфоне](../_assets/img/pls-list/mobile.jpg)
+20
View File
@@ -0,0 +1,20 @@
---
icon: material/wallet-outline
---
# :material-wallet-outline: Правообладателям
Данные, представленные на сайте https://m3u.su, получены автоматически из открыто доступных в интернете IPTV-плейлистов, опубликованных третьими лицами.
При наличии технической возможности, источник плейлиста может быть указан на вкладке [«Основные данные»](../iptvc/site/details.md).
Сервис https://m3u.su не размещает и не транслирует медиаконтент, не создаёт, не призывает использовать и распространять плейлисты третьих лиц, а также не оказывает услуг по ретрансляции телепрограмм.
Подробности о проекте и о том, как здесь оказались объекты ваших прав, читайте на страницах этой документации.
Информация о телеканалах (наименования, логотипы, технический статус и другие сведения) формируется исключительно путём обработки содержимого самого плейлиста.
Вся информация носит технический и ознакомительный характер, и её достоверность не гарантируется.
Все права на торговые марки и графические изображения принадлежат их законным владельцам.
Если вы являетесь правообладателем и считаете, что сведения на этой странице затрагивают ваши права, вы можете направить конфиденциальное уведомление на адрес **<abuse@m3u.su>**.
Плейлисты, нарушающие законодательство, удаляются с сайта окончательно по факту обращения от правообладателя.
+25
View File
@@ -0,0 +1,25 @@
---
title: Свободное ПО
icon: material/license
---
# Лицензии свободного ПО
Весь этот проект, включая документацию, основан на свободном программном обеспечении.
Ниже перечислен полный список этих проектов с их текстами лицензий.
??? quote "[microsoft/vscode-codicons](https://github.com/microsoft/vscode-codicons/blob/main/LICENSE) — Creative Commons Attribution 4.0 International"
```
--8<-- "licenses/vscode-codicons.txt"
```
??? quote "[zensical/zensical](https://github.com/zensical/zensical/blob/master/LICENSE.md) — MIT"
```
--8<-- "licenses/zensical.txt"
```
---
Я благодарю каждого, кто вносит свой посильный вклад в open-source и развивает подобного рода проекты.
Ваш труд лежит в основе технологического прогресса.
+36
View File
@@ -0,0 +1,36 @@
---
title: Лицензия
icon: material/license
---
# MIT License
=== "Оригинальный текст"
```plaintext { .wordwrap }
MIT License
Copyright (c) 2025-2026 Антон Аксенов (Anthony Axenov)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```
=== "Русский перевод"
```plaintext { .wordwrap }
Лицензия MIT
Copyright (c) 2025-2026 Антон Аксенов (Anthony Axenov)
Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации (в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно использовать Программное Обеспечение без ограничений, включая неограниченное право на использование, копирование, изменение, слияние, публикацию, распространение, сублицензирование и/или продажу копий Программного Обеспечения, а также лицам, которым предоставляется данное Программное Обеспечение, при соблюдении следующих условий:
Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые части данного Программного Обеспечения.
ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
```
!!! tip "[Построчный разбор лицензии MIT](https://habr.com/ru/articles/310976/)"
View File
+13 -10
View File
@@ -21,21 +21,24 @@ tags: ["telegram"]
* ❌ Никаких войсов, кружочков, игр, контактов, историй или локаций (предупреждение) * ❌ Никаких войсов, кружочков, игр, контактов, историй или локаций (предупреждение)
* ❌ Не удаляй свои сообщения, если на них успели ответить (предупреждение) * ❌ Не удаляй свои сообщения, если на них успели ответить (предупреждение)
🔶 Настоятельно рекомендуется прочесть хотя бы FAQ, а лучше всю документацию. Там наверняка уже давно есть ответ, который ты ищешь. Санитарка Роза или админ могут реагировать на твои сообщения и выдавать подсказки либо предупреждения (5 штук = бан). 🔶 Настоятельно рекомендуется прочесть хотя бы FAQ, а лучше всю документацию. Там наверняка уже давно есть ответ, который ты ищешь. Санитарка Роза или админ могут реагировать на твои сообщения и применять меры на своё усмотрение.
<a id="чеклист"></a> !!! failure "Не забывай"
## Я только спросить! К людям надо относиться по-человечески.
Твой юмор могут не понять, а пассивную агрессию и дерзость не любит никто.
## Я только спросить! { id="checklist" }
Держи чеклист: Держи чеклист:
1. **Найди ответ в [FAQ](../faq.md)** 1. **Найди ответ в [FAQ](../aggregator/faq.md)**
2. Если не нашёл ответ -- прочти остальное 2. Если не нашёл ответ прочти остальное
3. Если не нашёл ответ и это проблема: 3. Если не нашёл ответ и это проблема:
* опиши всю ситуацию -- сухо и по существу, в чём проблема, чего хочешь и ожидаешь; * опиши всю ситуацию сухо и по существу, в чём проблема, чего хочешь и ожидаешь;
* если вопрос связан с плейлистом из сервиса -- приложи его код и ссылку, которую указываешь в плеере; * если вопрос связан с плейлистом из сервиса приложи его код и ссылку, которую указываешь в плеере;
* если вопрос связан с другим плейлистом -- приложи его ссылку; * если вопрос связан с другим плейлистом приложи его ссылку;
* если вопрос связан с плеером -- укажи хотя бы его название; * если вопрос связан с плеером укажи хотя бы его название;
* если есть возможность, приложи скриншот или чёткое фото -- чтобы можно было разглядеть проблему и подсказать решение. * если есть возможность, приложи скриншот или чёткое фото чтобы можно было разглядеть проблему и подсказать решение.
!!! info "Скорее всего, это надо только тебе" !!! info "Скорее всего, это надо только тебе"
Чем больше ты предоставишь полезной информации, тем лучше. Чем больше ты предоставишь полезной информации, тем лучше.
+1 -2
View File
@@ -1,7 +1,6 @@
--- ---
icon: simple/telegram icon: simple/telegram
hide: hide: [toc]
- toc
--- ---
# :simple-telegram: Ресурсы в Telegram # :simple-telegram: Ресурсы в Telegram
+85
View File
@@ -0,0 +1,85 @@
# https://github.com/squidfunk/mkdocs-material/blob/master/src/overrides/hooks/shortcodes.py
from __future__ import annotations
import re
import unicodedata
from html import escape
from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor
SHORTCODE_RE = re.compile(r"<!--\s*md:(env|arg|config|version|default|beta)\s*(.*?)\s*-->", re.I)
# Страница с описанием конфигурации. Указана явно, потому что в проекте
# нет страницы `/iptvc/config` — соответствующий раздел живёт в
# `content/common/config/config.md`. С `use_directory_urls = false` конечные
# ссылки должны включать `.html`.
CONFIG_PAGE = "/common/config/config.html"
class BadgePreprocessor(Preprocessor):
def run(self, lines: list[str]) -> list[str]:
return [SHORTCODE_RE.sub(self._replace, line) for line in lines]
def _replace(self, match: re.Match[str]) -> str:
kind, args = match.groups()
args = args.strip()
if kind == "env":
value = args or "(нет)"
return _badge(":vscode-symbol-variable:", f"`{value}`", 'Переменная окружения')
if kind == "arg":
value = args or "(нет)"
return _badge(":vscode-terminal:", f"`{value}`", 'Аргумент командной строки')
if kind == "config":
value = args or "(нет)"
anchor = _slugify(args)
return _badge(":material-cog-outline:", f"[`{value}`]({CONFIG_PAGE}#{anchor})", 'Параметр файла конфигурации')
if kind == "version":
return _badge(":material-tag-outline:", escape(args), 'Версия, в которой появился этот функционал')
if kind == "default":
value = escape(args or "null")
return _badge(":material-water-outline:", f"`{value}`", 'Значение по умолчанию')
if kind == "beta":
return _badge(":material-beta:", '', 'Экспериментальный функционал')
return match.group(0)
# Slugify, совместимый с дефолтным slugify в pymdownx.slugs:
# NFKD-нормализация → отбрасывание не-ASCII → замена не-alphanumeric на дефис →
# склейка дефисов → trim → lowercase. Позволяет заранее предсказать якорь,
# не дожидаясь обработки Markdown
def _slugify(value: str) -> str:
if not value:
return ""
normalized = unicodedata.normalize("NFKD", value)
ascii_only = normalized.encode("ascii", "ignore").decode("ascii")
slug = re.sub(r"[^a-zA-Z0-9]+", "-", ascii_only)
return slug.strip("-").lower()
# Формирует HTML-разметку бейджа. Иконки остаются Zensical-шорткодами
# и рендерятся штатным `pymdownx.emoji` на следующем этапе обработки Markdown.
def _badge(icon: str, text: str = "", title: str = "") -> str:
return "".join([
f'<span class="mdx-badge" title="{title}">',
f'<span class="mdx-badge__icon">{icon}</span>',
*([f'<span class="mdx-badge__text">{text}</span>'] if text else []),
'</span>',
])
class InlineBadgeExtension(Extension):
def extendMarkdown(self, md):
md.preprocessors.register(BadgePreprocessor(md), "inline_badges", 175)
def makeExtension(**kwargs):
return InlineBadgeExtension(**kwargs)
-139
View File
@@ -1,139 +0,0 @@
site_name: Документация iptv.axenov.dev
site_description: Описание сервиса iptv.axenov.dev и его компонентов
site_author: Антон Аксенов
copyright: Антон Аксенов &copy; 2025 MIT License
repo_name: Репозиторий
repo_url: https://git.axenov.dev/IPTV/docs
edit_uri: src/branch/master/src/
remote_branch: master
docs_dir: ./src
use_directory_urls: false
watch:
- src
extra_css:
- assets/css/custom.css
extra_javascript:
- https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js
extra:
homepage: /docs
logo: assets/img/favicon/logo.png
favicon: assets/img/favicon/logo.png
social:
- name: Канал @iptv_aggregator
icon: simple/telegram
link: https://t.me/iptv_aggregator
- name: Чат @iptv_aggregator_chat
icon: simple/telegram
link: https://t.me/iptv_aggregator_chat
theme:
name: 'material'
language: ru
features:
- toc.follow
- search.suggest
- navigation.top
- navigation.footer
- navigation.indexes
- content.action.edit
icon:
repo: simple/gitea
edit: material/pencil
view: material/eye
palette:
# Автотема
- media: "(prefers-color-scheme)"
toggle:
icon: material/brightness-auto
name: Switch to light mode
# Тёмная тема
- scheme: slate
media: "(prefers-color-scheme: light)"
accent: teal
primary: black
toggle:
name: Светлая тема
icon: material/weather-sunny
# Светлая тема
- scheme: default
media: "(prefers-color-scheme: dark)"
accent: teal
primary: teal
toggle:
name: Тёмная тема
icon: material/weather-night
plugins:
- minify:
minify_html: true
- tags:
tags: true
listings: true
- social:
cards_layout_options:
background_color: teal
background_image: null
- search:
separator: '[\s\-\.]+'
# indexing: 'full'
validation:
omitted_files: warn
absolute_links: warn
unrecognized_links: warn
anchors: warn
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences
- attr_list
- md_in_html
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- toc:
permalink: true
nav:
- index.md
- docs.md
- support.md
- 'Общая информация':
- common/index.md
- common/how-it-works.md
- common/selection.md
- common/checks.md
- common/list.md
- common/details.md
- common/connect.md
- common/players.md
- 'IPTV Checker (iptvc)':
- iptvc/index.md
- iptvc/quickstart.md
- iptvc/how-it-works.md
- iptvc/env.md
- 'Работа в терминале':
- iptvc/cli/index.md
- iptvc/cli/help.md
- iptvc/cli/check.md
- iptvc/cli/version.md
- 'Для разработчиков':
- dev/index.md
- dev/local-dev.md
- dev/tgbot.md
- dev/docs.md
- dev/deploy.md
- 'Форматы файлов':
- formats/index.md
- 'playlists.ini': 'formats/playlists.md'
- 'channels.json': 'formats/channels.md'
- '*.m3u (*.m3u8)': 'formats/m3u.md'
- 'Telegram':
- tg/index.md
- 'Бот': tg/bot.md
- 'Чат': tg/chat.md
- 'FAQ (ЧаВо)': 'faq.md'

Some files were not shown because too many files have changed in this diff Show More