Skip to content
09КейсSW · 09 из 10

Три рантайма, одна команда, один месяц: сеть цифровых вывесок для площадки, рекламодателя и экрана.

Три аудитории, один продукт. Операторы площадок не могли управлять расписаниями экранов, рекламодатели не могли покупать инвентарь, а самим экранам нужен был плеер, подходящий разнородному железу. Мы построили все три поверхности за 27 дней.

КлиентКонфиденциально
Год2026
Длительность1 yrs
СтекTypeScript · Next.js · React · PHP · Laravel · C++ · Qt · Supabase · AWS
Hero image for ooh-signage-networkFIG 01 · Главное изображение

Три аудитории, один продукт, ноль общих допущений.

У OOH-рекламной сети три разных пользователя, у которых нет ничего общего. Владельцы площадок хотят знать, что и когда идёт на их экранах. Рекламодатели хотят покупать слоты, таргетировать по местоположению и видеть число показов. Сами экраны вообще не пользователи — это устройства, которым нужно надёжно получать расписания и проигрывать их без участия человека.

У клиента были рабочая концепция и сжатое окно запуска. Им нужен был маркетплейс-бэкенд для согласований рекламодателей, биллинга и планирования кампаний. Консоль, в которой операторы и владельцы площадок перетаскивают слоты в календарь. И плеер-рантайм, способный работать на Android-планшетах за стойкой отеля, Linux-киосках на транспортном узле и Windows-приставках в розничной сети — без развёртывания отдельной кодовой базы для каждого.

Удобным дефолтом для плеера был Electron. Electron упаковывает экземпляр Chromium и рантайм Node и запускается везде. Но он же весит сотни мегабайт, обновляется непредсказуемо и добавляет сложности на целый браузер к тому, что должно быть медиаплеером. На ограниченном железе киосков размер имел значение. История с обновлениями — ещё большее.

Маркетплейс-бэкенд, консоль с drag-and-drop и нативный плеер, работающий на четырёх операционных системах.

Бэкенд — это многоарендное приложение на Laravel 12, реализующее всё, что нужно маркетплейсу: подключение и согласование рекламодателей, планирование кампаний с разбивкой по времени суток и гео-таргетингом, сверку биллинга Stripe, загрузку и транскодирование медиа в очереди через слой фоновых задач Laravel и модель агрегации показов, питающую отчётность. Sanctum отвечает за аутентификацию API. Ассеты хранятся на S3.

Консоль оператора — это админ-поверхность на Next.js 16 с планировщиком drag-and-drop на @dnd-kit. Владельцы площадок видят свои экраны и свои слоты. Рекламодатели видят свои кампании и свой охват. Роль администратора видит всё. Все трое — одно и то же приложение, разграниченное по роли.

Плеер — это Qt 6 и C++20. Он работает на macOS, Linux, Windows и Android из единой CMake-кодовой базы с пресетами под каждую платформу. При запуске он подключается к Supabase по WebSockets и подписывается на свой канал расписания. Когда оператор перемещает слот в консоли, изменение приходит на экран в реальном времени — без опроса, без цикла обновления. Если сеть пропадает, плеер откатывается к локально кэшированным расписанию и медиа. Экран продолжает проигрывать.

F · 01Многоарендная консоль
Владельцы площадок, рекламодатели и администратор в одной поверхности, разграниченной по роли. Никаких отдельных порталов, никаких дублирующихся кодовых баз.
F · 02Планировщик drag-and-drop
Слоты, гео-зоны и интервалы суток, расставленные в календаре на @dnd-kit. Операторы видят конфликты до сохранения.
F · 03Синхронизация плеера в реальном времени
Изменения расписания доходят до парка плееров через Supabase WebSockets в момент сохранения оператором. Без опроса, без перезапуска.
F · 04Плеер с поддержкой офлайн
Qt Sql с SQLite кэширует текущее расписание и медиа локально. Пропадание Wi-Fi не гасит экран.
F · 05Кросс-платформенный нативный бинарник
Одна CMake-кодовая база, четыре целевые платформы: macOS, Linux, Windows, Android. Каждая сборка — самодостаточный нативный бинарник — без Chromium, без V8.
F · 06Биллинг маркетплейса
Сверка биллинга Stripe и агрегация показов в Laravel-бэкенде. Рекламодатели платят за подтверждённые показы.

Выбор Qt и чего он стоил против дефолта Electron.

Electron был очевидным выбором. Вся команда знала JavaScript. Админ-консоль уже была на TypeScript. Плеер на Electron разделял бы треть кодовой базы с веб-поверхностью. Мы отказались от него в первую же неделю.

Решающим фактором стала реальность развёртывания. Целевое железо варьировалось от Android-планшетов на 4G до Windows-мини-ПК на корпоративном Wi-Fi и Linux-киосков с заблокированными образами ОС. Размер Chromium в Electron — обычно 150–300 МБ в установленном виде — был жёстким «нет» для ограниченных устройств. Поведение автообновления Chromium, конфликтующее с заблокированными политиками ОС киосков, было ещё более жёстким «нет» для корпоративных развёртываний.

Qt 6 с C++20 даёт нативный бинарник под каждую платформу, скомпилированный примерно в 15 МБ на Linux и менее 30 МБ на Windows и Android. Путь обновления — это замена файла, оркеструемая самим плеером. Нет движка браузера, нет V8, нет брокера протоколов Electron. WebSocket-соединение с Supabase идёт через Qt Network; воспроизведение медиа — через Qt Multimedia. Офлайн-кэш использует Qt Sql с SQLite. Каждая зависимость поставляется в бинарнике.

Плеер, подходящий железу, лучше плеера, подходящего команде.

Цена была в переключении контекста. Команда вела три рантайма — PHP, TypeScript, C++ — параллельно. Кросс-рантайм-интеграционные тесты требовали больше обвязки, чем потребовал бы одноязычный стек. CMake-матрица сборки под четыре целевые платформы добавляла сложности в CI. GitHub Actions запускает четыре задачи сборки плеера на каждый push; сборка под Linux на базе Docker оказалась самой надёжной базой для воспроизводимости.

Трёхуровневая OOH-платформа, выпущенная меньше чем за месяц.

Первая версия всех трёх поверхностей была готова к продакшену за 27 дней: 223 коммита в трёх репозиториях, мультиплатформенный CI на каждом push, бинарники плеера подтверждены на всех четырёх целевых операционных системах до финального ревью. Маркетплейс-бэкенд берёт на себя сверку биллинга рекламодателей и агрегацию показов. Консоль берёт на себя планирование без таблиц. Плеер справляется с пропаданием сети, не гася экран.

Платформа работает. Консоль оператора — система учёта расписаний экранов по всей сети. Парк плееров получает обновления в реальном времени через Supabase WebSockets — изменения расписания, сделанные в консоли, доходят до физических экранов без перезапуска или ручной синхронизации.

Мораль выбора стека проста. Electron — верный ответ, когда удобство команды и скорость поставки являются связывающими ограничениями. Здесь он не был верным ответом. Выбирайте плеер-рантайм, подходящий вашему железу, а не тот, что подходит зоне комфорта вашей команды.

Цитата / 04
Мы сказали им, что плеер должен работать на всём, что мы уже закупили. Они вернулись с бинарником, который это делал.
ОсновательСеть наружной рекламы
Outcome
Days to first production build
27
Repos shipped in parallel
3
Player target platforms
4
Real-time push (WebSocket)
< 1 s
NEXTКейс 10SW · 10 из 10
Consumer wellness2018 — 2019

Когда платформа не говорит на языке вашего бизнеса, вы пишете модули, которые говорят.