Обзор: Добавьте в свое сообщество видеозалы и голосовые комнаты в стиле Discord 🎙

Кажется, на мобильной версии можно только создавать комнаты, но при попытке войти в уже созданную комнату ничего не происходит. Если перейти по ссылке /resenha/r/topic, то она открывается. Это баг?

У меня на телефоне всё работает нормально. Вы обновляли свой экземпляр Discourse на этой неделе?

Экспериментальным плагинам требуются частые обновления

5 лайков

В этом месяце вышло много обновлений, но главное из них — это опциональная поддержка SFU через LiveKit на уровне отдельных комнат.

Полноценный P2P WebRTC остается основным и поддерживаемым по умолчанию протоколом передачи данных, но теперь SFU также поддерживается для тех, кому необходимо обслуживать сотни участников в одной комнате.

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

15 лайков

Спасибо за это, SFU был действительно недостающим элементом. Именно это изначально подтолкнуло меня к поддержке отдельной ветки; теперь, когда это поддерживается в основной ветке, я могу отказаться от своей вилки и отслеживать resenha напрямую.

Я воздерживался от вопросов о LiveKit, потому что не знал, хотите ли вы двигаться в этом направлении. Теперь, когда вы это сделали: открыты ли вы к PR с новыми функциями и небольшими улучшениями?

4 лайка

@Falco Привет! Я столкнулся с несколькими воспроизводимыми проблемами во время тестирования обновленной версии Resenha. Делюсь своими находками и патчами, которые я использую локально, на случай, если они пригодятся в основном репозитории.

1. Административный интерфейс тихо отбрасывает room_type, и комнаты не могут сохранять тип этапа
app/controllers/resenha/admin_rooms_controller.rb:65 (room_params) исключает :room_type из списка разрешенных параметров, а admin_room_serializer.rb никогда не сериализует его.

Любое назначение этапа, выполненное через административный интерфейс, отбрасывается: комнаты создаются как открытые и «возвращаются» к открытому статусу при любом последующем редактировании в админке. Подтверждено через логи продакшена; пользовательский контроллер разрешает этот параметр, но административный путь его теряет.

# admin_rooms_controller.rb — добавьте :room_type в список разрешенных параметров, затем:
if permitted.key?(:room_type)
  value = Resenha::Room::ROOM_TYPES[permitted[:room_type].to_s]
  raise Discourse::InvalidParameters.new(:room_type) if value.nil?
  permitted[:room_type] = value
end
# admin_room_serializer.rb — сериализуйте room_type, чтобы форма инициализировалась корректно

2. Недействительный room_type тихо заменяется на открытый
app/controllers/resenha/rooms_controller.rb:575ROOM_TYPES[...] || ROOM_TYPE_OPEN.

Любой некорректный или устаревший room_type в иначе валидном редактировании комнаты тихо превращает комнату этапа в открытую. Возврат кода 400 сделает проблемного вызывающего код видимым, вместо того чтобы портить состояние комнаты.

value = Resenha::Room::ROOM_TYPES[permitted[:room_type].to_s]
raise Discourse::InvalidParameters.new(:room_type) if value.nil?
permitted[:room_type] = value

3. Heartbeat (сердцебиение) оживляет пользователя, который только что ушел (привидение присутствия)
rooms_controller.rb:234 (heartbeat) безоговорочно добавляет присутствие, поэтому heartbeat, находящийся в процессе передачи, когда обрабатывается leave (:220), приходит позже и заново создает ушедшего пользователя.

«Привидение» остается до момента очистки по TTL, что не транслируется — клиенты показывают пользователя «в комнате» до минуты.

Воспроизведено на устройстве; kick имеет идентичную уязвимость.

# ParticipantTracker: 15-секундная метка удаления
def mark_left(room_id, user_id)  = redis.setex(left_key(room_id, user_id), 15, "1")
def recently_left?(room_id, user_id) = redis.exists?(left_key(room_id, user_id))
# leave/kick → mark_left; join/livekit_token → clear_left; heartbeat:
return head :no_content if Resenha::ParticipantTracker.recently_left?(@room.id, current_user.id)

4. Устаревший webhook participant_left фантомно выкидывает новую сессию
livekit_webhooks_controller.rb:37/57 — уходы сопоставляются только по идентификатору пользователя.
При быстром отключении и повторном подключении participant_left от предыдущей сессии приходит с опозданием и удаляет присутствие новой сессии (пользователь «выкидывается» ~3 секунды после повторного входа, возвращаясь ~15 секунд спустя).
Воспроизведено на устройстве; эвристика gone_at не может различить сессии, когда повторное подключение происходит раньше отключения старой сессии.

# participant_joined → записать текущий SID
Resenha::ParticipantTracker.set_livekit_sid(room.id, user_id, event.dig("participant", "sid"))
# expire_participant → пропустить уход от предыдущей сессии
known = Resenha::ParticipantTracker.livekit_sid(room.id, user_id)
return if sid.present? && known.present? && sid != known

5. DeleteRoom 404 после ухода последнего участника затапливает логи
lib/resenha/livekit/room_service_client.rb:84 выдает предупреждение при любом ответе, отличном от 200.
SFU автоматически закрывает комнату в момент, когда она пустеет, поэтому последний уход часто опережает вызов DeleteRoom — «запрошенная комната не существует» является ожидаемым конечным состоянием, а не ошибкой.
Это попадает в Logster при каждом уходе последнего участника.

elsif method == "DeleteRoom" && response.status == 404
  Rails.logger.debug("[resenha-livekit] DeleteRoom no-op for room #{room.id}: already gone")
  true

Я могу отправить pull request, если желаешь.

Как я говорил ранее, я не знаю твоего полного плана развития, но ты можешь посмотреть на мои идеи, так как я создал пост здесь → Discourse Desktop Mac App - #10 by nicolsdennis

2 лайка

Это было бы просто великолепно — имеет смысл интегрировать это с механизмом прямой трансляции, что обеспечит связанный канал чата в рамках прекрасного интегрированного опыта.

4 лайка

Привет! При изменении названия комнаты slug не обновляется. Кроме того, я не могу удалять комнаты — вылетает ошибка сервера.

Было бы здорово иметь возможность визуально менять порядок комнат и то, как они отображаются.

Спасибо за подробный отчет, я только что объединил PR для этих:

6 лайков

У меня есть пара мелких замечаний по UI/UX, касающихся частных голосовых комнат:

1. Иконка замка/приватности слишком маленькая.

Отлично, что у нас есть иконка, указывающая на статус приватности комнаты; к сожалению, она слишком мала и сильно перекрывает основную иконку. Кроме того, это не согласуется с остальной частью боковой панели:

image   image

2. Список участников трудно найти

Получить доступ к списку участников в частной голосовой комнате можно только во время самого звонка.

Это не очень интуитивно понятно. Я ожидал, что смогу редактировать список участников/приглашённых в настройках, доступных даже когда голосовая комната неактивна. На самом деле, в неактивном состоянии доступно достаточно настроек (включая выбор между публичной и частной комнатой, максимальное количество участников и т.д.), просто отсутствует сам список участников.

3 лайка

Я исправил обе эти проблемы, спасибо за сообщение, @nathank!

2 лайка

Интересно, можно ли связать конкретную комнату через ссылки href? Хочу добавить ярлык для одной тестовой комнаты, чтобы упростить пользовательский интерфейс.

Да, теперь это возможно в таком формате

https://discourse-on-a-pi5.falco.dev/resenha/r/watercooler

4 лайка

Также стоит отметить недавние улучшения:

Поддержка частных STUN/TURN через аутентификацию coturn

Это позволяет ограничить доступ к вашему экземпляру coturn только для вас, и его очень легко настроить с помощью общего секретного ключа между Discourse и Coturn.

Поддержка записи сессий при использовании LiveKit

Всё это доступно через интерфейс звонка: одно нажатие для начала записи, одно нажатие для остановки.

Поддержка флагов для участников звонка

Хотя флаг будет лишен контекста, пользователи могут предоставить его через текстовое поле

Контекстное меню (правый клик) на видео-плитках

Простая регулировка громкости собеседника, отключение и т.д.

Опциональное повышение качества для голоса, видео и совместного использования экрана

Моя любимая функция недели: это позволяет пользователям включить значительно более высокие ограничения битрейта и получить гораздо лучшее качество аудио/видео.

6 лайков

Намного лучше!!! Спасибо, что прислушались (и приняли меры)!!

image

2 лайка

Это проблема моего экземпляра или, возможно, стоит разобраться глубже? При попытке удалить голосовые комнаты из панели администратора я получаю ошибку 500:

ActiveRecord::InvalidForeignKey (PG::ForeignKeyViolation: ERROR:  update or delete on table "resenha_rooms" violates foreign key constraint "fk_rails_ID" on table "resenha_sessions" DETAIL:  Key (id)=(1) is still referenced from table "resenha_sessions".
1 лайк

Спасибо за отчет, исправление объединено

2 лайка

Спасибо за эту сверхбыструю правку; всё работает как и ожидалось.

Небольшое дополнение: изменение имени комнаты по умолчанию не обновило путь /resenha/r/watercooler, но теперь я смог удалить предыдущую комнату по умолчанию и добавить новую.

1 лайк

Теперь я сделал слаг явно редактируемым, так что он работает как категории

6 лайков

Вы также можете добавить некоторые параметры URL, чтобы открыть его определённым образом:

https://discourse-on-a-pi5.falco.dev/resenha/r/watercooler?join — просто входит в комнату, без кнопки «Вступить»

https://discourse-on-a-pi5.falco.dev/resenha/r/watercooler?join&widget (добавлено ранее на этой неделе) — открывается и переходит в режим виджета

https://discourse-on-a-pi5.falco.dev/resenha/r/watercooler?join&chat — открывается в режиме чата

2 лайка