◂ База знанийRYA.AEAudio Enhance — чистка накамерного звука через Adobe Podcastобновлено 12 июл 2026
Накамерный / пушка-микрофон часто пишет спикера тихо и с шумом комнаты. Adobe Podcast «Enhance Speech» (podcast.adobe.com/enhance) убирает шум и вытягивает голос. У сервиса нет публичного API — гоняем его приватный бэкенд phonos напрямую по bearer-токену из браузера, чтобы обрабатывать всю папку смены разом, а не тягать файлы по одному. На выходе — стерео-WAV, секунда-в-секунду с исходником, ложится 1:1 на клип в Premiere.
📌 Когда применять. Любой материал, где голос спикера тихий/шумный и нужно его вычистить: накамерный микрофон, пушка на камере, комментарий в кадре, интервью без петли. Требования: залогиненный в Adobe Chrome-профиль ([email protected]) + ffmpeg; для get_token.py — venv с playwright и yt-dlp (шаг 1 в «Запуске»). Лимиты аккаунта (GET /api/v1/user): 4 ч/день, 2 ч и 1 ГБ на файл (грузится извлечённый WAV, поэтому биндит именно 1 ГБ; видео-лимит 5 ГБ не задействован). Не для: удаления конкретного спикера (см. грабли) и музыкального сведе́ния.
Adobe Podcast Enhance даёт лучший на сегодня denoise + подъём речи для интервью/комментария — но у него нет ни публичного API, ни API-ключа. Веб-приложение общается с приватным сервисом phonos по Adobe IMS bearer-токену, который выпускается в браузере после логина. Поэтому весь фокус — переиспользовать уже залогиненную сессию Chrome и повторить сетевой контракт, который делает сам сайт. Всё это упаковано в ~/YTAI/scripts/999_extra/adobe_podcast_enhance/.
нет публичного APIIMS bearer из браузерабэкенд = phonos
Авторизация: токен из браузера (get_token.py)
Пароль не трогаем. get_token.py экспортирует Adobe-cookies из Chrome (модулем cookie-экстракции yt-dlp; нормализацию хромовских меток времени µs-с-1601 скрипт делает сам), грузит их в headless real-Chrome, открывает страницу enhance и снифаетaccess_token из ответа POST adobeid-na1.services.adobe.com/ims/check/v6/token. Токен живёт ~24 ч; признак протухшего — enhance.py падает с list 401 / direct_uploads 401: заново get_token.py и повторить ту же команду.
Грабля. Headless Chrome должен стартовать с --disable-http2 --disable-quic, иначе SPA-бандл не грузится (ERR_HTTP2_PROTOCOL_ERROR) и страница пустая. Токен при этом выпускается нормально — нам нужен именно он, а не UI.
REST-флоу phonos
База https://phonos-server-flex.adobe.io — ⚠️ direct_uploads живёт в корне хоста, под /api/v1 только enhance_speech_tracks*. Заголовки Authorization: Bearer <token> + x-api-key: phonos-server-prod. Порядок на один файл:
шаг
вызов
init upload
POST /rails/active_storage/direct_uploads{blob:{filename,content_type,byte_size,checksum=base64(md5)}} → signed_id + presigned S3 PUT + заголовки
upload
PUT байты на тот S3-URL — только возвращённые заголовки, без Bearer (S3 отвергает двойную авторизацию)
create
POST /api/v1/enhance_speech_tracks{id:<uuid>, track_name, model_version:"v2", signed_id} → 201, id трека
poll
GET /api/v1/enhance_speech_tracks/{id} пока merged_media_url ≠ null и есть finished_processing_at (следим за error_type, limits.is_limited?)
result
GET /api/v1/enhance_speech_tracks/{id}/merged_media → {url:<presigned FLAC>}
Есть ещё .../enhanced_audio — mono чистая речь (изолированный сухой голос). Не используем: merged-стемы дают полнее (ниже).
Переиспользование треков. Перед батчем тулза делает GET /api/v1/enhance_speech_tracks и подхватывает уже готовые треки по track_name (последний по created_at). Повторный прогон / пересведение не грузит файлы заново и не тратит дневной лимит — только скачивание FLAC + локальный ffmpeg. ⚠️ Матчинг по голому имени файла по всему аккаунту: одинаковые имена клипов из разных смен (C0001.MP4…) молча подтянут чужой трек — имена между сменами должны быть уникальны.
Ключ: merged_media — это 8 каналов с 4 стемами, а не готовый микс
merged_media отдаёт 7.1 FLAC с раздельными стемами, которые веб-UI смешивает слайдерами Speech/Music/Background. Раскладку вскрыли методом наименьших квадратов, подогнав микс под известный экспорт из UI (остаток 0.03%):
ch2 + ch3 = Речь (спикер + интервьюер)ch4/ch5 = Backgroundch6/ch7 = Musicch0/ch1 = не используется
Слайдеры UI «Speech 50% / Background 10% / Music 10%» — это просто линейные усиления: 0.3536·(c2+c3) + 0.0348·bg + 0.0348·music (где 0.3536 = 0.5/√2). Зная это, любой микс из UI воспроизводится (или улучшается) чистым ffmpeg pan — полный контроль над глубиной голоса на нашей стороне. ⚠️ В тулзе на UI-шкалу замаплена только речь (×0.7071): гейны bg/music в рецептах — сырые коэффициенты pan, не UI-проценты (UI-10% ≈ 0.0348).
Рецепты микса (enhance.py --recipe, дефолт «C»)
Роман A/B-сравнил 4 профиля на клипе 0645 и выбрал «C — тёплый с телом». Ниже спектральный центроид = глубже голос.
id
pan речь/bg/music
EQ
уровень
характер
A
0.5 / 0.1 / 0.1
—
−25.5 LUFS
UI 50/10/10 по речи; bg/music сырые 0.10 (≈+9 дБ к UI) — тихо, старт Романа
B
1.0 / 0 / 0
пик +3@160, −2@400, shelf −2@9к
−18
чистый сухой голос, самый низкий центроид (262 Гц)
Подбор рецепта под новый голос — как делали для 0645: отрендерить варианты на одном клипе, дать Роману послушать A/B и выбрать. Стенд — make_variants.py (одноразовый: пути к FLAC внутри правятся под клип, вывод в Downloads); постоянные рецепты живут в словаре RECIPES в enhance.py. enhance.py применяет защиту громкости: если после pan+EQ integrated < −40 LUFS (тишина/нет речи), loudnorm не применяется — дубль не подтягивается вверх и комнатный тон не усиливается.
Нужен установленный Google Chrome (playwright запускает channel="chrome"). Сам enhance.py — чистый stdlib: хватает системного python3 + ffmpeg/ffprobe; venv нужен только для get_token.py (+ numpy, если гонять make_variants.py).
2
Один раз в ~24 ч — токен
./venv/bin/python get_token.py → token.txt рядом со скриптом (enhance.py читает его оттуда же, cwd не важен).
Профиль — флаг --profile (имя папки Chrome-профиля, дефолт Profile 1 = [email protected]); Chrome закрывать не нужно. «no token captured» → открыть Chrome в этом профиле, залогиниться на podcast.adobe.com, повторить.
3
Прогнать папку/файл
python3 enhance.py --in "/Volumes/.../Office_Gem_Commentary" --recipe C
Обрабатывает все *.MP4 / *.MOV / *.wav (регистр не важен; без рекурсии — только верхний уровень папки), пишет <stem>_enhanced.wav в <папка>/Audio_Enhanced/ (или --out). Идемпотентно: готовые (уже стерео) пропускает — ⚠️ пропуск не смотрит на рецепт: пересвести другим --recipe = задать --out в новую папку или убрать старые _enhanced.wav (по квоте бесплатно — трек переиспользуется с сервера). Есть ретраи на обрывы сети.
Вывод: стерео WAV 48 кГц / 16-бит, длительность = исходник — ложится 1:1 на клип в Premiere. Проверка в скрипте: abs(dur_src − dur_out) < 0.05с. Итог прогона: консоль === done: ok=N/M recipe=… === + пофайловый отчёт Audio_Enhanced/_enhance_report.json (ok / dur_mismatch / fail) — перед сдачей убедиться, что ok=N/N. ⚠️ Файл со статусом dur_mismatch остаётся на диске и при повторе будет пропущен как готовый: удалить его _enhanced.wav, проверить исходник, перегнать. Промежуточные WAV/FLAC — в Audio_Enhanced/_tmp/, после прогона папку можно удалить (при сбоях там остаются крупные FLAC).
Боевой случай и грабли
YTUVI / Office_Gem_Commentary (12 июл 2026). 30 клипов FX3A (≈70 мин), женский ведущий голос + мужской интервьюер. Извлекли WAV из каждого MP4 → enhance v2 → микс «C». Общая длительность в пределах дневного лимита 240 мин. Все 30 _enhanced.wav секунда-в-секунду с исходником.
30 клиповrecipe C1:1 по длительности
Грабли. ① Мужской интервьюер сидит в том же стем-канале речи — Enhance не разделяет по спикеру, вырезать его этим методом нельзя; нужна диаризация + сепарация (pyannote / wordrole) поверх стема речи — отдельная задача. ② S3-загрузка: presigned-URL несёт свою подпись — не слать туда Authorization, иначе InvalidArgument: only one auth mechanism allowed. ③ Headless Chrome — --disable-http2 --disable-quic. ④ checksum для direct_upload — это base64 от сырого MD5 (Rails ActiveStorage), не hex. ⑤ Крупные WAV рвут соединение — в req() ретраи с бэк-оффом. ⑥ MCP-инструмент Adobe media_enhance_speech в headless-сессии бесполезен (нет poll-виджета) — поэтому прямой REST. ⑦ Дневной лимит: при limits.is_limited? скрипт печатает RATE LIMITED (с retry_at_time) и прерывает пачку — остаток не обработан; после отката повторить ту же команду, готовое пропустится, треки переиспользуются. ⑧ Poll ждёт максимум 20 мин на файл — по таймауту файл помечен fail, но трек доваривается на сервере: повторный прогон подхватит его по имени без квоты.