---
# 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
│   ├── flags_test.go        # тесты применения CLI-переопределений
│   └── version.go           # команда version
├── app/
│   ├── app.go               # глобальные переменные: Args, Config, Cache
│   ├── config/
│   │   ├── config.go        # Config, Init(), validate(), IntRange, UserAgents
│   │   └── config_test.go   # тесты конфигурации
│   ├── 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
│       ├── views_test.go    # тесты представлений
│       ├── templates.go     # TemplateManager, //go:embed
│       ├── templates_test.go # тесты шаблонов
│       ├── openapi.go       # OpenAPI-спецификация, Swagger UI
│       ├── openapi_test.go  # тесты OpenAPI
│       ├── details_test.go  # тесты страницы деталей плейлиста
│       ├── testdata/         # тестовые фикстуры
│       ├── 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](../../ref/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 при отображении в веб-интерфейсе.
