Наткнулся на интересную статью, где описываются некоторые возможности, которые можно реализовать на Ростелеком домофоне:

https://habr.com/ru/articles/791602/

Как вы, возможно, помните из моих прошлых статей, у нас есть чат «попутчики» где люди ищут себе попутчиков для передвижения в Москву из области и обратно. И вот что я заметил. Очень часто в часы пик, люди фоткают очереди, чтоб уже на подъезде к остановке понять, а нужно ли двигать сейчас домой, может переждать или может даже выбрать самокат вместо маршрутки или такси. По счастливому стечению обстоятельств один из подъездов, оборудованный Ростелеком домофоном захватывает камерой прямо остановку и место за ней, где обычно и скапливается очередь. А значит можно, опираясь на вышеуказанную статью, добавить в чат кнопку «посмотреть очередь», чем я собственно и занялся.

И так для доступа конкретно к стриму камеры необходимо получить stream_token. Чтоб это сделать нам необходим BEARER_TOCKEN, его мы храним статично. Как говорил автор статьи выше — BEARER_TOCKEN живет до 1 года, что вполне устраивает нас. Раз в год можно заходить с акаунта любого пользователя, брать оттуда токен и снова год не знать бед. Собственно метод:

def getStreamTocken() -> str:
    
    headers = {
        "Authorization": config.BEARERTOCKEN
    }
    response = requests.get(config.AUTHRTCAM,
                 headers=headers)
    data = response.json()
    return data['data']['device']['video_data']['stream_token']

Отлично. теперь у нас есть токен для доступа к видео потоку. Сам видео поток приходит в websocket фрагментами. К сожалению я не разобрался что это за фрагменты. Декодирование полученных байтов из WS приводили только к таким картинкам:

результат
результат

Поэтому я пошел другим путем. Видеопоток можно захватить (это также описано в статье, ссылку на которую я оставил выше) с помощью всем известного curl.

cURL — служебная программа командной строки, позволяющая взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.

И здесь нас встречает 2 ограничения.

Первое — сам видеопоток имеет название файла очень длинное, что не пропустить cURL при сохранении.

Второе — видеофаил идет бесконечным потоком. Его нужно как‑то остановить, например на 1 секунде скачивания. Иначе мы просто будем бесконечно загружать дорожку пока не кончится память.

Интересно что на моем Mac в терминале вполне работала такая реализация:

curl --max-filesice 5000000 -o newname https://url.com

Однако в Ubuntu, на которой и крутятся все мои боты, оно не работало и не ограничивала загрузку, иногда ограничивала но не сохраняла фаил, иногда ругалась что слишком длинное название(менял местами конфиг). После 2х дней поисков и разговоров тет-а-тет с GPT-4 я все же нашел рабочее решение:

curl https://url.com | head -c 400000 > live.mp4

Конечно бывалые пользователи терминала уверенно сами бы до этого дошли. Но я не могу назвать себя таковым. Для остальных поясню что тут происходит

head -c 400000 — ограничивает размер файла в битах. я для своих нужд путем пробы и ошибок выставил именно 400000, так как это ровно 1 секунда видео потока.

> live.mp4 — указывает на новое имя файла для сохранения.

Прикладываю код всего метода:

def getVideo(*args) -> None:
    
    message: Message = args[0]

    oldMessage = bot.send_message(message.chat.id,
                                  reply_to_message_id=message.id,
                                  text="Загрузка...")
    os.system("curl 'https:url&token=" +
              getStreamTocken() +
              "' | head -c 400000 > live.mp4")
    
    bot.edit_message_text(chat_id = oldMessage.chat.id,
                          message_id = oldMessage.id,
                          text="Еще чучуть...")
    
    videoMessage = bot.send_video_note(message.chat.id, open("live.mp4", "rb"),
                                       reply_to_message_id=message.id,
                                       protect_content=True)
    
    if videoMessage:
        bot.delete_message(oldMessage.chat.id, oldMessage.id)
        os.system("rm live.mp4")
        time.sleep(60)
        bot.delete_message(videoMessage.chat.id, videoMessage.id)
        bot.delete_message(message.chat.id, message.id)

принимает args, потому что я исторически использовал telebot, не асинхронный, поэтому для таких задач использую threads (потоки), так как функция потенциально может на пару секунд положить весь основной функционал.

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

Видео отображается как белый экран на скрине из-за настройки protect_content=True
Видео отображается как белый экран на скрине из-за настройки protect_content=True

Спасибо за внимание, надеюсь моя третья попытка в написании подобных статей более удачна.

Комментарии (14)


  1. kuzzdra
    12.07.2024 09:26

    Э.. это же python.. какой curl? import requests же по питоновски? :)