Skip to content

2.4.1: голосовое загружается, но сервер не доводит обработку до конца — video.not.ready #103

Description

@azatsh

Кратко

В 2.4.1 отправка голосового доходит дальше, чем в 2.4.0, но по-прежнему не работает.

upload_voice заливает аудио, получает HTTP 200 — и следующий за этим MSG_SEND сервер
отклоняет:

Key: errors.process.attachment.video.not.ready [errors.process.attachment.video.not.ready]

Похоже, сервер принимает байты, но никогда не переводит вложение в состояние «готово».
Ожидание не помогает: я вставлял паузу между загрузкой и отправкой (то есть внутрь
upload_voice, перед return) — 5 секунд, затем 15 секунд, и повторял всю попытку заново.
Суммарно больше 30 секунд ожидания на одно голосовое — ответ всё тот же not.ready.

Параллельно NOTIF_ATTACH (opcode 136) для голосовой загрузки не приходит вообще, ни разу, ни
через минуту. Для File в той же сессии и по тому же соединению он приходит меньше чем за секунду.

Окружение

maxapi-python2.4.1 (и 2.4.0 до неё)
Python3.12, Linux (Docker)
Серверapi.oneme.ru
Аккаунтобычный пользовательский
Аудиоголосовые из Telegram: Opus в контейнере Ogg, 29–57 КБ, длительность передаётся явно

Логи снимались с ExtraConfig(log_level="DEBUG").

Что в 2.4.1 стало заметно лучше

Спасибо за исправление — по логам видно, что диагноз изменился:

  • 2.4.0 отвечал errors.process.attachment.video.not.supported — форма payload была не та.
  • 2.4.1 отвечает errors.process.attachment.video.not.ready — форма уже принимается, дело
    теперь в незавершённой обработке.

То есть переход на VoiceAttachPayloadduration и wave) сервер устраивает.

Воспроизведение

awaitclient.send_message(
chat_id=...,
text="тест",
attachments=[Voice(raw=ogg_bytes, name="voice.ogg", duration=3400)],
)

ogg_bytes — обычное голосовое из Telegram (Opus/Ogg). Воспроизводится каждый раз, на разных
файлах и разной длительности.

Хронология из логов

1. В 2.4.0 уведомление не приходит вовсе

16:39:49 Voice upload waiter registered voice_id=3698883559635
16:39:49 Voice upload HTTP response status=200 voice_id=3698883559635
16:39:49 Waiting for voice processing notification voice_id=3698883559635
[следующие 60 с: только opcode=49 (CHAT_HISTORY) и opcode=1 (PING)]
16:40:49 Timed out waiting for voice processing notification voice_id=3698883559635

Для сравнения — загрузка File минутой позже, в той же сессии:

16:40:49 File upload waiter registered file_id=4760217947
16:40:50 File upload HTTP response status=200 file_id=4760217947
16:40:50 dispatching event type=EventType.FILE_READY
16:40:50 calling handler event=EventType.FILE_READY callback=UploadService.on_file_attach
16:40:50 File upload waiter resolved file_id=4760217947

За весь прогон opcode 136 встретился ровно один раз — вот этот, файловый.

2. В 2.4.1 отправка отклоняется сразу

15:05:34 Uploading voice
15:05:35 MAX rejected the voice note (errors.process.attachment.video.not.ready)

3. Пауза между загрузкой и отправкой не помогает

Обёрнутый upload_voice, который спит перед return (то есть MSG_SEND уходит уже после паузы),
15 секунд, две полные попытки подряд — каждая со своей загрузкой и своей паузой:

15:35:18 Uploading voice (пауза 15 с, затем отправка → not.ready)
15:35:36 Uploading voice (пауза 15 с, затем отправка → not.ready)
15:35:51 rejected: errors.process.attachment.video.not.ready

Отдельно отмечу: повторять всю отправку бессмысленно, потому что send_message заново
загружает файл, и жалоба каждый раз относится к новому, только что созданному вложению. Пауза
обязана находиться между загрузкой и отправкой — выше именно такой вариант.

Наблюдение: User-Agent в голосовой загрузке закодирован percent-encoding

Не утверждаю, что это причина, но это единственное, чем заголовки голосовой загрузки отличаются от
остальных. api/uploads/service.py:

user_agent= (
f"OKMessages/{self.app.config.app_version}"+f" ({self.app.config.device.user_agent.os_version};"+f" {self.app.config.device.user_agent.device_name};"+f" {self.app.config.device.user_agent.screen})"
)
headers= {
"Content-Disposition": f"attachment; filename={quote(voice.name)}",
...
"User-Agent": quote(user_agent), # <- целиком через quote()
}

quote() применяется ко всей строке User-Agent, и на выходе получается:

OKMessages/26.27.1%20%28Android%2013%3B%20Samsung%20SM-A525F%3B%20405dpi%20405dpi%201080x2400%29

Пробелы, скобки и точки с запятой экранированы — настоящий клиент такой заголовок не отправляет.
Для filename внутри Content-Disposition строкой выше quote() уместен, для User-Agent
целиком — вряд ли.

Что делает это наблюдение любопытным: голосовая загрузка — единственная из трёх, которая вообще
шлёт User-Agent
. upload_video и upload_file его не отправляют, и обе работают. И именно
голосовая — единственная, обработку которой сервер не завершает.

Что проверено и можно не перепроверять

  • Аудио валидное: контейнер Ogg (сигнатура OggS), Opus, из Telegram без перекодирования.
  • Загрузка проходит: POST отвечает 200, в логе нет Voice upload failed with status.
  • Дело не в размере: 29–57 КБ.
  • Дело не в длительности: duration передаётся явно, в миллисекундах.
  • Дело не в отсутствии bytes в Content-Range: в upload_file он тоже без префикса
    (f"0-{file_size - 1}/{file_size}"), и файлы загружаются нормально. Так что исправление этого
    префикса в голосовой ветке, скорее всего, было не тем, что чинило проблему.
  • Дело не в нехватке времени: больше 30 секунд ожидания суммарно, результат не меняется.

Мелочь на будущее

В 2.4.1 ожидание убрано (закомментировано), но вокруг него остался мёртвый код: on_voice_attach
и очистка voice_upload_waiters в finally. Если ожидание когда-нибудь вернут, стоит учесть, что
регистрация и разрешение waiter-а шли по разным ключам: upload_voice клал future в
voice_upload_waiters[video_id], а on_voice_attach искал по attach.audio_id. Плюс
resolve_attach определяет тип фрейма перебором FileUploadSignalVideoUploadSignal
AudioUploadSignal, и у всех моделей extra="allow" — значит payload с videoId совпадёт с
VideoUploadSignal первым и уйдёт в on_video_attach. Это вывод из чтения исходников, а не
наблюдение: подходящего фрейма мы так и не увидели.

Вопросы

  1. Работает ли отправка голосовых у вас сейчас, на актуальном сервере? Если да — очень поможет
    узнать версию приложения/сборки, под которую клиент представляется.
  2. С какого клиента снимался протокол голосовой загрузки? Веб-версия MAX голосовые записывать не
    умеет (в меню вложений только «Фото или видео», «Файл», «Контакт», «Опрос», и микрофона в
    поле ввода нет), так что, видимо, с мобильного — и, возможно, с тех пор что-то поменялось.
  3. Стоит ли ждать NOTIF_ATTACH для голосовых в принципе, или у аудио другой путь
    подтверждения? В pymax.protocol.Opcode не занят код 85 (между VIDEO_PLAY=83 и
    FILE_UPLOAD=87) — это ни на что не намекает, просто бросилось в глаза.

Готов снять любые дополнительные логи — DEBUG со стороны PyMax включается одной строкой, и
воспроизводится это стабильно.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions