Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Video room controls and permissions #1614

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion app/channels/room_channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ class RoomChannel < ApplicationCable::Channel
def subscribed
@room = find_room
stream_for @room
@room.update(participant_count: (@room.participant_count + 1))
broadcast_to @room, { type: 'ping', from: params[:client_id] }
end

Expand All @@ -10,6 +11,12 @@ def unsubscribed
find_room,
target: "medium_#{current_client.id}"
)
@room.update(participant_count: (@room.participant_count + -1))
Turbo::StreamsChannel.broadcast_replace_to(
target: 'room-stats',
partial: 'rooms/participants',
locals: { room: find_room }
)
end

def greet(data)
Expand All @@ -20,11 +27,17 @@ def greet(data)
partial: 'media/medium',
locals: { client_id: data['from'], user: user}
)
Turbo::StreamsChannel.broadcast_replace_to(
data['to'],
target: 'room-stats',
partial: 'rooms/participants',
locals: { room: find_room }
)
end

private

def find_room
Room.new(id: params[:id])
Room.find_by(external_room_id: params[:id])
end
end
4 changes: 2 additions & 2 deletions app/channels/signaling_channel.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
class SignalingChannel < ApplicationCable::Channel
def subscribed
stream_for Room.new(id: params[:id])
stream_for Room.find_by(external_room_id: params[:id])
end

def signal(data)
broadcast_to(Room.new(id: params[:id]), data)
broadcast_to(Room.find_by(external_room_id: params[:id]), data)
end
end
16 changes: 15 additions & 1 deletion app/controllers/comfy/admin/rooms_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
class Comfy::Admin::RoomsController < Comfy::Admin::Cms::BaseController
def new
@room = Room.new
render 'rooms/new'
end

def create
redirect_to room_path(SecureRandom.uuid)
external_room_id = SecureRandom.uuid
Room.create!(room_params.merge(external_room_id: external_room_id, user_id: current_user.id))
redirect_to room_path(external_room_id)
end

private

def room_params
params.require(:room).permit(
:name,
:active,
:require_authentication,
:owner_broadcast_only,
)
end
end
23 changes: 22 additions & 1 deletion app/controllers/rooms_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
class RoomsController < ApplicationController
before_action :load_room
before_action :check_authentication, only: [:show]

def show
@is_room_owner = false
@client = Client.new(id: SecureRandom.uuid)
cookies.encrypted[:client_id] = @client.id
@room = Room.new(id: params[:id])

if current_user
@is_room_owner = current_user.id == @room.user_id
end
@user = current_user
@visit = current_visit
end

private

def check_authentication
if @room.require_authentication
unless current_user
flash.alert = "you need to sign in first!"
redirect_to new_user_session_path
end
end
end

def load_room
@room = Room.find_by(external_room_id: params[:id])
end
end
15 changes: 11 additions & 4 deletions app/javascript/controllers/room_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,16 @@ export default class RoomController extends Controller {

async enter () {
try {
const constraints = { audio: true, video: true }
this.client.stream = await navigator.mediaDevices.getUserMedia(constraints)
let video = $('#allowVideo').is(':checked')
let audio = $('#allowAudio').is(':checked')
const constraints = { audio: audio, video: video }
this.client.constraints = constraints
if (video || audio) {
this.client.stream = await navigator.mediaDevices.getUserMedia(constraints)
}
this.localMediumTarget.srcObject = this.client.stream
this.localMediumTarget.muted = true // Keep muted on Firefox
this.enterTarget.hidden = true
this.enterTarget.hidden = true // Hide enter controls

this.subscription.start()
this.signaller.start()
Expand Down Expand Up @@ -95,7 +100,9 @@ export default class RoomController extends Controller {
}

startStreamingTo (otherClient) {
this.client.streamTo(otherClient)
if (this.client.constraints.video || this.client.constraints.audio) {
this.client.streamTo(otherClient)
}
}

startStreamingFrom (id, { track, streams: [stream] }) {
Expand Down
1 change: 1 addition & 0 deletions app/javascript/models/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export default class Client {
constructor (id) {
this.callbacks = {}
this.id = id
this.constraints = {}
}

get peerConnection () {
Expand Down
8 changes: 1 addition & 7 deletions app/models/room.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
class Room
attr_reader :id

def initialize(id:)
@id = id
end

class Room < ApplicationRecord
def to_param
id
end
Expand Down
4 changes: 4 additions & 0 deletions app/views/rooms/_controls.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= label_tag "enable video?" %>
<%= check_box_tag :allowVideo, true %>
<%= label_tag "enable audio?" %>
<%= check_box_tag :allowAudio, true %>
1 change: 1 addition & 0 deletions app/views/rooms/_participants.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>Participants: <%= room.participant_count %></p>
14 changes: 12 additions & 2 deletions app/views/rooms/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
<p>Early access, feature under active development</p>
</div>

<%= form_with url: rooms_path do |form| %>
<%= form.submit 'Create Room' %>
<%= form_for @room, url: rooms_path do |form| %>
<div>
<%= form.label "Room owner broadcast (spectators only)" %>
<%= form.check_box :owner_broadcast_only, checked: false %>
</div>
<div>
<%= form.label "Require authentication (no visitors)" %>
<%= form.check_box :require_authentication, checked: false %>
</div>
<div>
<%= form.submit 'Create Room', class: 'btn btn-primary' %>
</div>
<% end %>
26 changes: 23 additions & 3 deletions app/views/rooms/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
<%= turbo_stream_from @room %>
<%= turbo_stream_from @client.id %>

<p>Room ID: <%= @room.id %></p>
<p>
<% if @room.require_authentication %>
Secure Room ID: <%= @room.external_room_id %>
<% else %>
Public Room ID: <%= @room.external_room_id %>
<% end %>
</p>
<div id="room-stats">
<%= render 'rooms/participants', room: @room %>
</div>

<article
class="room"
data-controller="room"
data-room-id-value="<%= @room.id %>"
data-room-id-value="<%= @room.external_room_id %>"
data-room-client-id-value="<%= @client.id %>"
>
<div class="media" id="media">
Expand All @@ -20,7 +29,18 @@
</div>
<% end %>
<div class="medium-actions">
<%= button_tag 'Enter', data: { room_target: 'enter', action: 'room#enter' } %>
<div class="enter" data-room-target="enter">
<% if !@room.owner_broadcast_only %>
<%= render "controls" %>
<% else %>
<% if @is_room_owner %>
<%= render "controls" %>
<% else %>
<p>The owner of the room does not allow participants to share audio/video</p>
<% end %>
<% end %>
<%= button_tag 'Enter', data: { action: 'room#enter' }, class: 'btn btn-primary d-block' %>
</div>
</div>
</div>
</div>
Expand Down
16 changes: 16 additions & 0 deletions db/migrate/20230925182202_add_rooms_table.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class AddRoomsTable < ActiveRecord::Migration[6.1]
def change
create_table :rooms do |t|
t.string :name
t.string :external_room_id, null: false
t.boolean :active, default: true
t.references :user, null: true, foreign_key: true
t.boolean :require_authentication, default: true
t.boolean :owner_broadcast_only, default: true
t.integer :participant_count, default: 0

t.timestamps
end
add_index :rooms, :external_room_id, unique: true
end
end
17 changes: 16 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.