229 lines
12 KiB
Markdown
229 lines
12 KiB
Markdown
---
|
||
# 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-обработчики
|
||
│ ├── templates.go # TemplateManager, //go:embed
|
||
│ └── views/ # HTML-шаблоны
|
||
│ ├── base.html
|
||
│ ├── list.html
|
||
│ ├── details.html
|
||
│ └── notfound.html
|
||
└── go.mod
|
||
```
|
||
|
||
## Пакеты
|
||
|
||
### `app`
|
||
|
||
Глобальный контейнер: `Args` (CLI-флаги), `Config` (конфигурация), `Cache` (Redis-клиент).
|
||
`Init()` загружает конфигурацию, инициализирует логгер и подключение к кешу.
|
||
|
||
### `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)`** — параллельная проверка плейлистов (семфор `per-routine`), загрузка, парсинг, вызов `CheckChannels` для каждого.
|
||
- **`CheckChannels(pls)`** — параллельная проверка каналов (семфор `per-routine`), 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`** — параметры: Every, 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
|
||
sleep(every)
|
||
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` | Подробный лог |
|
||
|
||
## Конфигурация
|
||
|
||
Подробное описание параметров — в разделе [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 при отображении в веб-интерфейсе.
|