Есть у нас с друзьями одна традиция. Вот уже больше десяти лет, каждую зиму, в конце января, мы устраиваем шоу. С видеоконтентом, музыкальным сопровождением, трансляцией в интернет с нескольких камер. Иногда - с музыкальными номерами.

Шоу называется Ролевой Оскар. Делается для ролевой тусовки в Сибири: лучшие игры, игроки, мастера. Десяток номинаций, ролики по номинациям, статуэтки Оскара победителям. Красиво, красочно, торжественно.

Но не для техперсонала. Один человек за звуковым пультом, второй на запуске видео, третий на запуске звука. Постоянно следить за сценой и сверяться с распечатанным сценарием. И кого угодно не посадишь на место оператора, потому что и - интрига!

Я решил это упростить. Благо сейчас каждый может написать какой угодно софт с проворными ИИ-агентами. Сначала думал сделать программку для запуска аудио. Потом добавил еще функционала. Потом еще.

Мне нужен был не ещё один медиаплеер, а пульт: нажал кнопку - зал услышал и увидел. Да, существуют Screen Monkey, Resolume Arena и прочие. Либо дорого, либо со своими проблемами, либо с интерфейсом проблемы. Поэтому решил делать своё - со своим блэк-джеком и всем остальным.

Программа называется Yerick AE Oscar Player, сейчас 3.7.0, чисто для Windows.

Сетка аудио, сетка видео, три слота фоновой музыки, пять кнопок веба, пять - захвата чужих окон. Справа - две шкалы авто уровня выходного сигнала и бегунок. Слева, если включить в настройках, текст сценария. Между сеткой и нижним рядом транспорт для аудио и видео с красной Стоп всё. Кнопки слушаются мыши, горячие клавиши и MIDI-клавиатуры. Наблюдаем за залом, сценарием, нажимаем кнопочки.

То есть теперь один оператор управляет и видео, и аудио. Я планирую еще и микшером левой рукой управлять. Может вывести на экран сайт (голосование, например) или через захват окна звонок в Zoom в реальном времени.

Пульт и проектор

Операторский интерфейс нельзя тащить на большой экран: зал не должен видеть, какую кнопку вы сейчас промазали. Поэтому вывод - отдельное окно. Его перетаскивают на второй монитор или выбирают экран в настройках. Двойной клик разворачивает на полный экран; в конфиг это не пишется. Выбрано путем проб и ошибок.

Кнопка с файлом зеленеет точкой «уже играло» - на длинной церемонии это спасает от повторного запуска. Пропавший с диска файл не исчезает с кнопки: видно имя и пометку «нет файла». Можно сразу назначить новый, а не гадать, куда делся прошлый.

Аудио: два плеера, чтобы зал не услышал шов

Пустая кнопка - выбрать файл. Кнопка с файлом - старт. Другая кнопка - текущий трек затухает примерно за две секунды, новый уже идёт. Повторный клик по играющей - стоп. Всё как на звуковом пульте.

Под капотом не один QMediaPlayer, а два. Один доигрывает fade, второй уже звучит.

Дополнительно три кнопки для фона. Это не ещё три отбивки, а цикл: слот кончился - со следующего, наложение те же две секунды, по кругу. Фоновая музыка служит для заполнения пространства до начала шоу, например. Или антракта. Прекращают работу автоматически при нажатии любой другой кнопки с файлом.

Видео: один экран — одна картинка

Ролик и картинки уходят в окно на проекторе. Под сеткой - плюс/минус для добавления кнопок, заглушка с фоновой картинкой, чёрный фон и белый. Иногда надо сцену подсветить через белый фон, или убрать фоновую картинку. Проектор не выключишь, а бегать закрывать его картонкой уже не серьезно.

На этом окне одновременно живёт только один источник. Стартовали видео - веб и захват погасли. Стартовали захват - ролик остановился. Двух картинок на сцене не бывает.

Внутри окна стопка слоёв. Видно всегда ровно один.

Обратный отсчёт воспроизводимого файла на ка каждой кнопке есть у аудио и видео. У веба и захвата его нет: у живой страницы нет "осталось 0:17".

Веб и чужие окна

Нижний ряд — пять кнопок URL и пять кнопок захвата.

Веб: задали адрес, клик - страница в том же окне плеера. Полезно для онлайн голосований, например. На кнопке превью. Движок - встроенный Chromium из Qt WebEngine. Это не отдельный Chrome: часть сайтов ведёт себя иначе, и это надо проверить до начала. Превью рисует один скрытый QWebEngineView в очереди.

Захват только Windows. Клик по пустой кнопке, появляется список открытых окон, Zoom или приложение привязался, на кнопке снова превью. Клик - живая картинка на проекторе, примерно 15 кадров в секунду. Не шаринг всего рабочего стола с почтой в трее и окном "а точно ли мы в эфире?".

Почему два способа захвата. Обычный снимок окна часто даёт чёрный прямоугольник, если программа рисует через GPU. Тогда включается Windows Graphics Capture; если сессия не стартовала - откат на PrintWindow. Свёрнутое или защищённое окно может не отдаться никак. Это не настройка, это Windows. Это не я придумал. Это после ого как я попросил агента "а проверь-ка проект на всякие ошибки, логику и вообще взгляни как опытный архитектор ПО".

Громкость, которая не спорит с рукой

Справа на всю высоту две шкалы и бегунок. До - что в файле. После - оценка выхода. Бегунок устанавливает уровень общей громкости пульта, аудио и звук видео. Галочка "Авто" тянет уровень к цели по сигналу: громкий ролик приседает, тихий после паузы поднимается. Бегунок при этом не едет сам и авто его не компенсирует. Сделано потому что для выступления разные команды приносят свою музыкальную подложку, и все они с разным уровнем громкости. Оперативно на микшере не всегда успеваешь среагировать. А уж если звук для подложки вообще длительностью несколько секунд, и он кааак заорет...

Сценарий, который не нажимает кнопки за вас

Отдельная гордость - слежение за текстом. В настройках выбор слежения за сценарием и выбор файла .txt. Слева появляется сценарий, не шире 30% окна. Без галочки панель скрыта, путь к файлу не сбрасывается: сценарий то нужен, то мешает, особенно когда ведёте саундчек без ведущих. У саундчека нет права отвлекаться на сценарий - он и так весь в себе.

Дальше слежение по микрофону. На отдельно выделенный вход идёт сигнал с пульта. Лучше линия, не микрофон крышки ноутбука: крышка слышит зал, аплодисменты и того парня, который орет имя номинанта. Текущий абзац подсвечивается и держится в верхней трети панели. В шапке статусы: следит, пауза или потерял. Ведущие отходят от текста. Это нормально. Редкие слова и пауза на импровизации это переживают; если совсем ушли, то клик по абзацу ставит позицию и не утаскивает скролл из-под глаз.

В тексте на отдельной строке можно указать кнопку, тогда кнопка подсвечивается как следующая

Лучший мужской костюм
[видео 3]
Вручает Вася

Годятся [аудио N], [видео N], [фон N], [веб N], [захват N]. Номер как на кнопке, с единицы. Ремарки в скобках и строки // в слежение не входят.

Метка подсвечивает кнопку янтарной рамкой, у видео ещё лёгкая заливка. Сама кнопка не запускается. Программа не доверяет себе настолько, чтобы жать кнопки за оператора. И правильно делает: ведущий ещё не договорил, а "умный режиссёр" уже запустил ролик. Молчаливый суфлёр лучше самоуверенного режиссёра. Рамка не ставится на то, что уже играет: подсказка про следующую свободную, а не про "нажми ещё раз то же самое". Зелёная точка "уже было" подсветке не мешает - повтор той же кнопки на церемонии бывает нужен.

Итого по цветам, если коротко: следующая- жёлтая, играет - красная, уже играла - зелёная.

Распознавание опционально: Vosk и папка модели рядом с программой - или тяжёлый whisper. Без пакета пульт стартует, клик по тексту работает, микрофон в шкалы уровня не идёт и никуда не отправляется.

Ведущие не читают текст дословно. Точное совпадение и жёсткое следование сценарию ломаются на первой же шутке. Алайнер монотонный: в основном шаг вперёд, иногда пропуск нескольких абзацев, назад - только если уверенность высокая. Редкие слова весят больше частых. Окно распознавания - последняя прозвучавшая фраза

Пока с кнопки играет аудио или видео, распознавание на паузе. Фоновые кнопки не стопят слежение. Клик по абзацу в паузе работает.

Руки, клавиши, железо

Красная Стоп всё слева на транспорте и Escape. Мгновенно, без fade: аудио (даже если уже затухает), видео, веб, захват. Критические ситуации бывают, и глазами высматривать что именно сейчас играет бывает сложновато. Поэтому одна кнопка на прекращение всего и сразу.

Остальной транспорт - стоп, play, пауза и ползунок текущего ролика или трека. Звуковые и видео кнопки позволяют работать одновременно. Если играют обе, ползунок слушает видео. Веб и захват без перемотки: некуда.

Шоу в папке, а не в десяти конфигах

Файл → Сохранить шоу… пишет show.json в выбранную папку. Пути к медиа относительные: папку можно унести на другой ноутбук и не чинить диск D:. Рабочие конфиги рядом с программой - это сессия «как оставили вчера». Шоу - то, что везут на площадку. Сценарий и слежение в шоу не едут: это настройки сессии. Как правило сценарий вообще могут выдать за час до шоу.

В результате. Скинул все файлы в папку, назначил их на кнопки, выбрал фоновую картинку. Затем загрузил сценарий - и шоу должно продолжаться. А ты сидишь и только кнопочки нажимаешь. Ну, почти: ещё смотришь на жёлтую рамку и надеешься, что ведущий не начнёт с конца.

На чём это стоит

Python и PyQt6. Звук и видео - QtMultimedia. Страницы - WebEngine. Окна Windows - pywin32, по желанию Graphics Capture. Vosk для распознавания сценария. MIDI и речь подключаются, если стоят пакеты; если нет, то пульт не падает на заставке. Это сознательно: на чужом ноутбуке в зале не всегда есть модель на гигабайт, а кнопки нажать всё равно надо.

Python 3.8+ · PyQt6 · QtMultimedia · PyQt6-WebEngine · pywin32 · mido / python-rtmidi · опционально vosk или faster-whisper · сборка PyInstaller onedir + Inno Setup.

Чего это сознательно не делает

Это не DAW: нет дорожек, автоматизации и плагинов. Это не vMix: нет микшера камер и титров на три слоя.

Мне этого хватает. На сцене нужна понятная сетка, один экран. Раньше два ноута, разные проигрыватели, один подключен к проектору, все они подключены к пульту. Сейчас - все в одном окне.

Увидимся на шоу!