Merge pull request #20703 from danielpetri1/webcam-background-url

feat: Accept custom webcamBackgroundURL
This commit is contained in:
Anton Georgiev 2024-07-23 15:55:56 -04:00 committed by GitHub
commit 5387fef101
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 199 additions and 109 deletions

View File

@ -57,7 +57,9 @@ trait RegisterUserReqMsgHdlr {
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
msg.body.name, msg.body.role, msg.body.authToken,
msg.body.avatarURL, ColorPicker.nextColor(liveMeeting.props.meetingProp.intId), msg.body.guest, msg.body.authed, guestStatus, msg.body.excludeFromDashboard, false)
msg.body.avatarURL,
msg.body.webcamBackgroundURL,
ColorPicker.nextColor(liveMeeting.props.meetingProp.intId), msg.body.guest, msg.body.authed, guestStatus, msg.body.excludeFromDashboard, false)
checkUserConcurrentAccesses(regUser)
@ -98,7 +100,7 @@ trait RegisterUserReqMsgHdlr {
val g = GuestApprovedVO(regUser.id, GuestStatus.ALLOW)
UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID)
case GuestStatus.WAIT =>
val guest = GuestWaiting(regUser.id, regUser.name, regUser.role, regUser.guest, regUser.avatarURL, regUser.color, regUser.authed, regUser.registeredOn)
val guest = GuestWaiting(regUser.id, regUser.name, regUser.role, regUser.guest, regUser.avatarURL, regUser.webcamBackgroundURL, regUser.color, regUser.authed, regUser.registeredOn)
addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting)
notifyModeratorsOfGuestWaiting(Vector(guest), liveMeeting.users2x, liveMeeting.props.meetingProp.intId)
val notifyEvent = MsgBuilder.buildNotifyRoleInMeetingEvtMsg(

View File

@ -34,7 +34,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
def registerUserInRegisteredUsers() = {
val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId,
msg.body.callerIdName, Roles.VIEWER_ROLE, msg.body.intId, "",
msg.body.callerIdName, Roles.VIEWER_ROLE, msg.body.intId, "", "",
userColor, true, true, GuestStatus.WAIT, true, false)
RegisteredUsers.add(liveMeeting.registeredUsers, regUser)
}
@ -57,6 +57,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
presenter = false,
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
avatar = "",
webcamBackground = "",
color = userColor,
clientType = "",
pickExempted = false,
@ -67,7 +68,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
def registerUserAsGuest() = {
if (GuestsWaiting.findWithIntId(liveMeeting.guestsWaiting, msg.body.intId) == None) {
val guest = GuestWaiting(msg.body.intId, msg.body.callerIdName, Roles.VIEWER_ROLE, true, "", userColor, true, System.currentTimeMillis())
val guest = GuestWaiting(msg.body.intId, msg.body.callerIdName, Roles.VIEWER_ROLE, true, "", "", userColor, true, System.currentTimeMillis())
GuestsWaiting.add(liveMeeting.guestsWaiting, guest)
notifyModeratorsOfGuestWaiting(guest, liveMeeting.users2x, liveMeeting.props.meetingProp.intId)

View File

@ -6,47 +6,4 @@ trait UserJoinedVoiceConfMessageHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
/*
def startRecordingVoiceConference() {
if (Users.numUsersInVoiceConference(liveMeeting.users) == 1 &&
props.recordProp.record &&
!MeetingStatus2x.isVoiceRecording(liveMeeting.status)) {
MeetingStatus2x.startRecordingVoice(liveMeeting.status)
log.info("Send START RECORDING voice conf. meetingId=" + props.meetingProp.intId + " voice conf=" + props.voiceProp.voiceConf)
outGW.send(new StartRecordingVoiceConf(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf))
}
}
def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) = {
log.info("Received user joined voice. meetingId=" + props.meetingProp.intId + " callername=" + msg.callerIdName
+ " userId=" + msg.userId + " extUserId=" + msg.externUserId)
Users.findWithId(msg.userId, liveMeeting.users) match {
case Some(user) => {
// this is used to restore the mute state on reconnect
val previouslyMuted = user.voiceUser.muted
val nu = Users.restoreMuteState(user, liveMeeting.users, msg.voiceUserId, msg.userId, msg.callerIdName,
msg.callerIdNum, msg.muted, msg.talking, msg.avatarURL, msg.listenOnly)
log.info("User joined voice. meetingId=" + props.meetingProp.intId + " userId=" + user.id + " user=" + nu)
outGW.send(new UserJoinedVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status) || previouslyMuted) {
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record,
nu.id, nu.id, props.voiceProp.voiceConf,
nu.voiceUser.userId, true))
}
startRecordingVoiceConference()
}
case None => {
startRecordingVoiceConference()
}
}
}
*/
}

View File

@ -68,7 +68,7 @@ class GuestsWaiting {
}
}
case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, avatar: String, color: String, authenticated: Boolean, registeredOn: Long)
case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, avatar: String, webcamBackground: String, color: String, authenticated: Boolean, registeredOn: Long)
case class GuestPolicy(policy: String, setBy: String)
object GuestPolicyType {

View File

@ -5,7 +5,7 @@ import org.bigbluebutton.core.domain.BreakoutRoom2x
object RegisteredUsers {
def create(userId: String, extId: String, name: String, roles: String,
token: String, avatar: String, color: String, guest: Boolean, authenticated: Boolean,
token: String, avatar: String, webcamBackground: String, color: String, guest: Boolean, authenticated: Boolean,
guestStatus: String, excludeFromDashboard: Boolean, loggedOut: Boolean): RegisteredUser = {
new RegisteredUser(
userId,
@ -14,6 +14,7 @@ object RegisteredUsers {
roles,
token,
avatar,
webcamBackground,
color,
guest,
authenticated,
@ -198,6 +199,7 @@ case class RegisteredUser(
role: String,
authToken: String,
avatarURL: String,
webcamBackgroundURL: String,
color: String,
guest: Boolean,
authed: Boolean,

View File

@ -413,6 +413,7 @@ case class UserState(
locked: Boolean,
presenter: Boolean,
avatar: String,
webcamBackground: String,
color: String,
roleChangedOn: Long = System.currentTimeMillis(),
lastActivityTime: Long = System.currentTimeMillis(),

View File

@ -70,6 +70,7 @@ trait HandlerHelpers extends SystemConfiguration {
presenter = false,
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
avatar = regUser.avatarURL,
webcamBackground = regUser.webcamBackgroundURL,
color = regUser.color,
clientType = clientType,
pickExempted = false,

View File

@ -16,7 +16,7 @@ object UserJoinedMeetingEvtMsgBuilder {
raiseHand = userState.raiseHand,
away = userState.away,
pin = userState.pin,
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar, color = userState.color,
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar, webcamBackground = userState.webcamBackground, color = userState.color,
clientType = userState.clientType, userCustomData = userState.userCustomData)
val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body)

View File

@ -20,13 +20,13 @@ trait FakeTestData {
val guest1 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false,
talking = false, listenOnly = false)
Users2x.add(liveMeeting.users2x, guest1)
val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role, guest1.guest, "", "#ff6242", guest1.authed, System.currentTimeMillis())
val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role, guest1.guest, "", "", "#ff6242", guest1.authed, System.currentTimeMillis())
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait1)
val guest2 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false,
talking = false, listenOnly = false)
Users2x.add(liveMeeting.users2x, guest2)
val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, "", "#ff6242", guest2.authed, System.currentTimeMillis())
val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, "", "", "#ff6242", guest2.authed, System.currentTimeMillis())
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2)
val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
@ -69,7 +69,8 @@ trait FakeTestData {
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, pin = false,
mobile = false, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, locked = false, presenter = false,
avatar = regUser.avatarURL, color = "#ff6242", clientType = "unknown",
avatar = regUser.avatarURL, webcamBackground = regUser.webcamBackgroundURL,
color = "#ff6242", clientType = "unknown",
pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
}

View File

@ -52,10 +52,12 @@ object FakeUserGenerator {
val authToken = RandomStringGenerator.randomAlphanumericString(16)
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".png"
val webcamBackgroundURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".jpg"
val color = "#ff6242"
val ru = RegisteredUsers.create(userId = id, extId, name, role,
authToken, avatarURL, color, guest, authed, guestStatus = GuestStatus.ALLOW, false, false)
authToken, avatarURL, webcamBackgroundURL, color, guest, authed, guestStatus = GuestStatus.ALLOW, false, false)
RegisteredUsers.add(users, ru)
ru
}

View File

@ -12,9 +12,11 @@ object TestDataGen {
val authToken = RandomStringGenerator.randomAlphanumericString(16)
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".png"
val webcamBackgroundURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
RandomStringGenerator.randomAlphanumericString(10) + ".jpg"
val ru = RegisteredUsers.create(userId = id, extId, name, role,
authToken, avatarURL, guest, authed, GuestStatus.ALLOW, false)
authToken, avatarURL, webcamBackgroundURL, guest, authed, GuestStatus.ALLOW, false)
RegisteredUsers.add(users, ru)
ru
@ -48,7 +50,7 @@ object TestDataGen {
val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
emoji = "none", reactionEmoji = "none", raiseHand = false, away = false, pin = false, mobile = false,
locked = false, presenter = false, avatar = regUser.avatarURL, color = "#ff6242",
locked = false, presenter = false, avatar = regUser.avatarURL, regUser.webcamBackgroundURL, color = "#ff6242",
clientType = "unknown", pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
Users2x.add(liveMeeting.users2x, u)
u

View File

@ -115,8 +115,8 @@ case class UserVO(id: String, externalId: String, name: String, role: String,
guest: Boolean, authed: Boolean, guestStatus: String, emojiStatus: String,
presenter: Boolean, hasStream: Boolean, locked: Boolean, webcamStreams: Set[String],
phoneUser: Boolean, voiceUser: VoiceUserVO, listenOnly: Boolean, avatarURL: String,
joinedWeb: Boolean)
webcamBackgroundURL: String, joinedWeb: Boolean)
case class VoiceUserVO(userId: String, webUserId: String, callerName: String,
callerNum: String, joined: Boolean, locked: Boolean, muted: Boolean,
talking: Boolean, avatarURL: String, listenOnly: Boolean)
talking: Boolean, avatarURL: String, webcamBackgroundURL: String, listenOnly: Boolean)

View File

@ -6,7 +6,7 @@ case class RegisterUserReqMsg(
body: RegisterUserReqMsgBody
) extends BbbCoreMsg
case class RegisterUserReqMsgBody(meetingId: String, intUserId: String, name: String, role: String,
extUserId: String, authToken: String, avatarURL: String,
extUserId: String, authToken: String, avatarURL: String, webcamBackgroundURL: String,
guest: Boolean, authed: Boolean, guestStatus: String, excludeFromDashboard: Boolean,
userCustomData: Map[String, AnyRef])
@ -90,24 +90,25 @@ case class UserJoinedMeetingEvtMsg(
body: UserJoinedMeetingEvtMsgBody
) extends BbbCoreMsg
case class UserJoinedMeetingEvtMsgBody(
intId: String,
extId: String,
name: String,
role: String,
guest: Boolean,
authed: Boolean,
guestStatus: String,
emoji: String,
reactionEmoji: String,
raiseHand: Boolean,
away: Boolean,
pin: Boolean,
presenter: Boolean,
locked: Boolean,
avatar: String,
color: String,
clientType: String,
userCustomData: Map[String, String]
intId: String,
extId: String,
name: String,
role: String,
guest: Boolean,
authed: Boolean,
guestStatus: String,
emoji: String,
reactionEmoji: String,
raiseHand: Boolean,
away: Boolean,
pin: Boolean,
presenter: Boolean,
locked: Boolean,
avatar: String,
webcamBackground: String,
color: String,
clientType: String,
userCustomData: Map[String, String]
)
/**

View File

@ -126,10 +126,10 @@ public class MeetingService implements MessageListener {
public void registerUser(String meetingID, String internalUserId,
String fullname, String role, String externUserID,
String authToken, String avatarURL, Boolean guest,
String authToken, String avatarURL, String webcamBackgroundURL, Boolean guest,
Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby) {
handle(new RegisterUser(meetingID, internalUserId, fullname, role,
externUserID, authToken, avatarURL, guest, authed, guestStatus, excludeFromDashboard, leftGuestLobby));
externUserID, authToken, avatarURL, webcamBackgroundURL, guest, authed, guestStatus, excludeFromDashboard, leftGuestLobby));
Meeting m = getMeeting(meetingID);
if (m != null) {
@ -437,7 +437,7 @@ public class MeetingService implements MessageListener {
gw.registerUser(message.meetingID,
message.internalUserId, message.fullname, message.role,
message.externUserID, message.authToken, message.avatarURL, message.guest,
message.externUserID, message.authToken, message.avatarURL, message.webcamBackgroundURL, message.guest,
message.authed, message.guestStatus, message.excludeFromDashboard, userCustomData);
}
@ -933,7 +933,7 @@ public class MeetingService implements MessageListener {
}
User user = new User(message.userId, message.externalUserId,
message.name, message.role, message.avatarURL, message.guest, message.guestStatus,
message.name, message.role, message.avatarURL, message.webcamBackgroundURL, message.guest, message.guestStatus,
message.clientType);
if(m.getMaxUsers() > 0 && m.countUniqueExtIds() >= m.getMaxUsers()) {
@ -1053,8 +1053,8 @@ public class MeetingService implements MessageListener {
} else {
if (message.userId.startsWith("v_")) {
// A dial-in user joined the meeting. Dial-in users by convention has userId that starts with "v_".
User vuser = new User(message.userId, message.userId, message.name, "DIAL-IN-USER", "",
true, GuestPolicy.ALLOW, "DIAL-IN");
User vuser = new User(message.userId, message.userId, message.name, "DIAL-IN-USER", "", "",
true, GuestPolicy.ALLOW, "DIAL-IN");
vuser.setVoiceJoined(true);
m.userJoined(vuser);
}

View File

@ -84,6 +84,8 @@ public class ParamsProcessorUtil {
private Integer defaultHttpSessionTimeout = 14400;
private Boolean useDefaultAvatar = false;
private String defaultAvatarURL;
private Boolean useDefaultWebcamBackground = false;
private String defaultWebcamBackgroundURL;
private String defaultGuestPolicy;
private Boolean authenticatedGuest;
private Boolean defaultAllowPromoteGuestToModerator;
@ -741,6 +743,7 @@ public class ParamsProcessorUtil {
}
String avatarURL = useDefaultAvatar ? defaultAvatarURL : "";
String webcamBackgroundURL = useDefaultWebcamBackground ? defaultWebcamBackgroundURL : "";
int html5InstanceId = processHtml5InstanceId(params.get(ApiParams.HTML5_INSTANCE_ID));
@ -760,6 +763,7 @@ public class ParamsProcessorUtil {
.withTelVoice(telVoice).withWebVoice(webVoice)
.withDialNumber(dialNumber)
.withDefaultAvatarURL(avatarURL)
.withDefaultWebcamBackgroundURL(webcamBackgroundURL)
.withAutoStartRecording(autoStartRec)
.withAllowStartStopRecording(allowStartStoptRec)
.withRecordFullDurationMedia(_recordFullDurationMedia)
@ -1296,6 +1300,14 @@ public class ParamsProcessorUtil {
this.defaultAvatarURL = url;
}
public void setUseDefaultWebcamBackground(Boolean value) {
this.useDefaultWebcamBackground = value;
}
public void setDefaultWebcamBackgroundURL(String uri) {
this.defaultWebcamBackgroundURL = uri;
}
public void setDefaultGuestPolicy(String guestPolicy) {
this.defaultGuestPolicy = guestPolicy;
}

View File

@ -79,6 +79,7 @@ public class Meeting {
private Integer maxPinnedCameras = 0;
private String dialNumber;
private String defaultAvatarURL;
private String defaultWebcamBackgroundURL;
private String guestPolicy = GuestPolicy.ASK_MODERATOR;
private String guestLobbyMessage = "";
private Map<String,String> usersWithGuestLobbyMessages;
@ -148,6 +149,7 @@ public class Meeting {
logoutUrl = builder.logoutUrl;
logoutTimer = builder.logoutTimer;
defaultAvatarURL = builder.defaultAvatarURL;
defaultWebcamBackgroundURL = builder.defaultWebcamBackgroundURL;
record = builder.record;
autoStartRecording = builder.autoStartRecording;
allowStartStopRecording = builder.allowStartStopRecording;
@ -462,6 +464,10 @@ public class Meeting {
return defaultAvatarURL;
}
public String getDefaultWebcamBackgroundURL() {
return defaultWebcamBackgroundURL;
}
public void setWaitingPositionsInWaitingQueue(HashMap<String, String> guestUsersWithPositionInWaitingLine) {
this.guestUsersWithPositionInWaitingLine = guestUsersWithPositionInWaitingLine;
}
@ -909,6 +915,7 @@ public class Meeting {
private Map<String, String> metadata;
private String dialNumber;
private String defaultAvatarURL;
private String defaultWebcamBackgroundURL;
private long createdTime;
private boolean isBreakout;
private String guestPolicy;
@ -1056,6 +1063,11 @@ public class Meeting {
return this;
}
public Builder withDefaultWebcamBackgroundURL(String w) {
defaultWebcamBackgroundURL = w;
return this;
}
public Builder isBreakout(Boolean b) {
isBreakout = b;
return this;

View File

@ -31,6 +31,7 @@ public class User {
private String fullname;
private String role;
private String avatarURL;
private String webcamBackgroundURL;
private Map<String,String> status;
private Boolean guest;
private String guestStatus;
@ -45,6 +46,7 @@ public class User {
String fullname,
String role,
String avatarURL,
String webcamBackgroundURL,
Boolean guest,
String guestStatus,
String clientType) {
@ -53,6 +55,7 @@ public class User {
this.fullname = fullname;
this.role = role;
this.avatarURL = avatarURL;
this.webcamBackgroundURL = webcamBackgroundURL;
this.guest = guest;
this.guestStatus = guestStatus;
this.status = new ConcurrentHashMap<>();
@ -128,6 +131,14 @@ public class User {
this.avatarURL = avatarURL;
}
public String getWebcamBackgroundUrl() {
return webcamBackgroundURL;
}
public void setWebcamBackgroundUrl(String webcamBackgroundURL) {
this.webcamBackgroundURL = webcamBackgroundURL;
}
public boolean isModerator() {
return "MODERATOR".equalsIgnoreCase(this.role);
}

View File

@ -42,6 +42,7 @@ public class UserSession {
public String logoutUrl = null;
public String defaultLayout = "NOLAYOUT";
public String avatarURL;
public String webcamBackgroundURL;
public String guestStatus = GuestPolicy.ALLOW;
public String clientUrl = null;
public Boolean excludeFromDashboard = false;
@ -134,6 +135,10 @@ public class UserSession {
return avatarURL;
}
public String getWebcamBackgroundURL() {
return webcamBackgroundURL;
}
public String getGuestStatus() {
return guestStatus;
}

View File

@ -10,6 +10,7 @@ public class RegisterUser implements IMessage {
public final String externUserID;
public final String authToken;
public final String avatarURL;
public final String webcamBackgroundURL;
public final Boolean guest;
public final Boolean authed;
public final String guestStatus;
@ -17,7 +18,7 @@ public class RegisterUser implements IMessage {
public final Boolean leftGuestLobby;
public RegisterUser(String meetingID, String internalUserId, String fullname, String role, String externUserID,
String authToken, String avatarURL, Boolean guest,
String authToken, String avatarURL, String webcamBackgroundURL, Boolean guest,
Boolean authed, String guestStatus, Boolean excludeFromDashboard, Boolean leftGuestLobby) {
this.meetingID = meetingID;
this.internalUserId = internalUserId;
@ -25,7 +26,8 @@ public class RegisterUser implements IMessage {
this.role = role;
this.externUserID = externUserID;
this.authToken = authToken;
this.avatarURL = avatarURL;
this.avatarURL = avatarURL;
this.webcamBackgroundURL = webcamBackgroundURL;
this.guest = guest;
this.authed = authed;
this.guestStatus = guestStatus;

View File

@ -7,6 +7,7 @@ public class UserJoined implements IMessage {
public final String name;
public final String role;
public final String avatarURL;
public final String webcamBackgroundURL;
public final Boolean guest;
public final String guestStatus;
public final String clientType;
@ -18,6 +19,7 @@ public class UserJoined implements IMessage {
String name,
String role,
String avatarURL,
String webcamBackgroundURL,
Boolean guest,
String guestStatus,
String clientType) {
@ -27,6 +29,7 @@ public class UserJoined implements IMessage {
this.name = name;
this.role = role;
this.avatarURL = avatarURL;
this.webcamBackgroundURL = webcamBackgroundURL;
this.guest = guest;
this.guestStatus = guestStatus;
this.clientType = clientType;

View File

@ -22,7 +22,7 @@ public interface IPublisherService {
void endMeeting(String meetingId);
void send(String channel, String message);
void registerUser(String meetingID, String internalUserId, String fullname, String role, String externUserID,
String authToken, String avatarURL, Boolean guest, Boolean excludeFromDashboard, Boolean authed);
String authToken, String avatarURL, String webcamBackgroundURL, Boolean guest, Boolean excludeFromDashboard, Boolean authed);
void sendKeepAlive(String system, Long bbbWebTimestamp, Long akkaAppsTimestamp);
void sendStunTurnInfo(String meetingId, String internalUserId, Set<StunServer> stuns, Set<TurnEntry> turns);
}

View File

@ -50,7 +50,7 @@ public interface IBbbWebApiGWApp {
String presentationUploadExternalUrl);
void registerUser(String meetingID, String internalUserId, String fullname, String role,
String externUserID, String authToken, String avatarURL,
String externUserID, String authToken, String avatarURL, String webcamBackgroundURL,
Boolean guest, Boolean authed, String guestStatus, Boolean excludeFromDashboard, Map<String, Object> userCustomData);
void guestWaitingLeft(String meetingID, String internalUserId);

View File

@ -15,7 +15,7 @@ public interface IMeetingService {
void addUserSession(String token, UserSession user);
void registerUser(String meetingID, String internalUserId,
String fullname, String role, String externUserID,
String authToken, String avatarURL, Boolean guest, Boolean authed);
String authToken, String avatarURL, String webcamBackgroundURL, Boolean guest, Boolean authed);
UserSession getUserSession(String token);
UserSession removeUserSession(String token);
void purgeRegisteredUsers();

View File

@ -17,6 +17,7 @@ public class Meeting2 {
public final boolean forciblyEnded;
public final String logoutUrl;
public final String defaultAvatarURL;
public final String defaultWebcamBackgroundURL;
public final Map<String, String> metadata;
public final List<String> breakoutRooms;
@ -30,6 +31,7 @@ public class Meeting2 {
boolean forciblyEnded,
String logoutUrl,
String defaultAvatarURL,
String defaultWebcamBackgroundURL,
Map<String, String> metadata,
List<String> breakoutRooms) {
this.props = props;
@ -42,6 +44,7 @@ public class Meeting2 {
this.forciblyEnded = forciblyEnded;
this.logoutUrl = logoutUrl;
this.defaultAvatarURL = defaultAvatarURL;
this.defaultWebcamBackgroundURL = defaultWebcamBackgroundURL;
this.metadata = metadata;
this.breakoutRooms = breakoutRooms;
}

View File

@ -253,27 +253,23 @@ class BbbWebApiGWApp(
groupsAsVector
)
//meetingManagerActorRef ! new CreateMeetingMsg(defaultProps)
val event = MsgBuilder.buildCreateMeetingRequestToAkkaApps(defaultProps)
msgToAkkaAppsEventBus.publish(MsgToAkkaApps(toAkkaAppsChannel, event))
}
def registerUser(meetingId: String, intUserId: String, name: String,
role: String, extUserId: String, authToken: String, avatarURL: String,
role: String, extUserId: String, authToken: String, avatarURL: String,
webcamBackgroundURL: String,
guest: java.lang.Boolean, authed: java.lang.Boolean,
guestStatus: String, excludeFromDashboard: java.lang.Boolean, userCustomData: java.util.Map[String, AnyRef]): Unit = {
// meetingManagerActorRef ! new RegisterUser(meetingId = meetingId, intUserId = intUserId, name = name,
// role = role, extUserId = extUserId, authToken = authToken, avatarURL = avatarURL,
// guest = guest, authed = authed)
val ucd = userCustomData.asScala.toMap
val regUser = new RegisterUser(meetingId = meetingId, intUserId = intUserId, name = name,
role = role, extUserId = extUserId, authToken = authToken, avatarURL = avatarURL,
guest = guest.booleanValue(), authed = authed.booleanValue(), guestStatus = guestStatus,
webcamBackgroundURL = webcamBackgroundURL, guest = guest.booleanValue(),
authed = authed.booleanValue(), guestStatus = guestStatus,
excludeFromDashboard = excludeFromDashboard, ucd)
val event = MsgBuilder.buildRegisterUserRequestToAkkaApps(regUser)

View File

@ -40,7 +40,8 @@ object MsgBuilder {
val header = BbbCoreHeaderWithMeetingId(RegisterUserReqMsg.NAME, msg.meetingId)
val body = RegisterUserReqMsgBody(meetingId = msg.meetingId, intUserId = msg.intUserId,
name = msg.name, role = msg.role, extUserId = msg.extUserId, authToken = msg.authToken,
avatarURL = msg.avatarURL, guest = msg.guest, authed = msg.authed, guestStatus = msg.guestStatus,
avatarURL = msg.avatarURL, webcamBackgroundURL = msg.webcamBackgroundURL,
guest = msg.guest, authed = msg.authed, guestStatus = msg.guestStatus,
excludeFromDashboard = msg.excludeFromDashboard, msg.userCustomData)
val req = RegisterUserReqMsg(header, body)
BbbCommonEnvCoreMsg(envelope, req)

View File

@ -4,7 +4,8 @@ case class CallerId(name: String, number: String)
case class VoiceUser(id: String, callerId: CallerId, status: String, vid: String, wid: String, callingWith: String)
case class User2(intId: String, extId: String, name: String, role: String, avatarURL: String,
guest: Boolean, waitingForAcceptance: Boolean, status: Vector[String],
webcamBackgroundURL: String,
guest: Boolean, waitingForAcceptance: Boolean, status: Vector[String],
streams: Set[String], customData: UserCustomData, voiceUser: VoiceUser, webcamStreams: Vector[String])
object Users {
@ -40,7 +41,8 @@ class Users {
case class RegisteredUser2(meetingId: String, intId: String, name: String, role: String,
extId: String, authToken: String, avatarURL: String,
guest: Boolean, authed: Boolean)
webcamBackgroundURL: String,
guest: Boolean, authed: Boolean)
object RegisteredUsers {
def findWithId(users: RegisteredUsers, id: String): Option[RegisteredUser2] = {

View File

@ -16,7 +16,7 @@ case class CreateBreakoutRoomMsg(meetingId: String, parentMeetingId: String,
case class AddUserSession(token: String, session: UserSession)
case class RegisterUser(meetingId: String, intUserId: String, name: String, role: String,
extUserId: String, authToken: String, avatarURL: String,
extUserId: String, authToken: String, avatarURL: String, webcamBackgroundURL: String,
guest: Boolean, authed: Boolean, guestStatus: String, excludeFromDashboard: Boolean, userCustomData: Map[String, AnyRef])
case class CreateMeetingMsg(defaultProps: DefaultProps)

View File

@ -122,10 +122,9 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
def handleUserJoinedMeetingEvtMsg(msg: UserJoinedMeetingEvtMsg): Unit = {
olgMsgGW.handle(new UserJoined(msg.header.meetingId, msg.body.intId,
msg.body.extId, msg.body.name, msg.body.role, msg.body.avatar, msg.body.guest,
msg.body.extId, msg.body.name, msg.body.role, msg.body.avatar, msg.body.webcamBackground, msg.body.guest,
msg.body.guestStatus,
msg.body.clientType))
}
def handlePresenterUnassignedEvtMsg(msg: PresenterUnassignedEvtMsg): Unit = {

View File

@ -28,7 +28,8 @@ trait ToAkkaAppsSendersTrait extends SystemConfiguration {
val header = BbbCoreHeaderWithMeetingId(RegisterUserReqMsg.NAME, msg.meetingId)
val body = RegisterUserReqMsgBody(meetingId = msg.meetingId, intUserId = msg.intUserId,
name = msg.name, role = msg.role, extUserId = msg.extUserId, authToken = msg.authToken,
avatarURL = msg.avatarURL, guest = msg.guest, authed = msg.authed, guestStatus = msg.guestStatus,
avatarURL = msg.avatarURL, webcamBackgroundURL = msg.webcamBackgroundURL, guest = msg.guest,
authed = msg.authed, guestStatus = msg.guestStatus,
excludeFromDashboard = msg.excludeFromDashboard, msg.userCustomData)
val req = RegisterUserReqMsg(header, body)
val message = BbbCommonEnvCoreMsg(envelope, req)

View File

@ -5,7 +5,7 @@ case class UserSession2(authToken: String, internalUserId: String, conferencenam
role: String, conference: String, room: String, guest: Boolean = false,
authed: Boolean = false, voicebridge: String, webvoiceconf: String,
mode: String, record: String, welcome: String, logoutUrl: String,
defaultLayout: String = "NOLAYOUT", avatarURL: String)
defaultLayout: String = "NOLAYOUT", avatarURL: String, webcamBackgroundURL: String)
class UserSessions {

View File

@ -29,6 +29,7 @@ export default async function addUserPersistentData(user) {
presenter: Boolean,
locked: Boolean,
avatar: String,
webcamBackground: String,
clientType: String,
left: Boolean,
effectiveConnectionType: null,
@ -42,6 +43,7 @@ export default async function addUserPersistentData(user) {
role,
token,
avatar,
webcamBackground,
guest,
color,
pin,
@ -55,6 +57,7 @@ export default async function addUserPersistentData(user) {
role,
token,
avatar,
webcamBackground,
guest,
color,
pin,

View File

@ -30,6 +30,7 @@ export default async function addUser(meetingId, userData) {
presenter: Boolean,
locked: Boolean,
avatar: String,
webcamBackground: String,
color: String,
pin: Boolean,
clientType: String,

View File

@ -1,3 +1,5 @@
import Auth from '/imports/ui/services/auth';
import Users from '/imports/api/users';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import {
@ -325,6 +327,21 @@ class VideoPreview extends Component {
viewState: VIEW_STATES.found,
});
this.displayPreview();
// Set the custom or default virtual background
const webcamBackground = Users.findOne({
meetingId: Auth.meetingID,
userId: Auth.userID,
}, {
fields: {
webcamBackground: 1,
},
});
const webcamBackgroundURL = webcamBackground?.webcamBackground;
if (webcamBackgroundURL !== '') {
this.handleVirtualBgSelected(EFFECT_TYPES.IMAGE_TYPE, '', { url: webcamBackgroundURL });
}
});
} else {
// There were no webcams coming from enumerateDevices. Throw an error.
@ -428,7 +445,6 @@ class VideoPreview extends Component {
// Resolves into true if the background switch is successful, false otherwise
handleVirtualBgSelected(type, name, customParams) {
const { sharedDevices } = this.props;
const { webcamDeviceId } = this.state;
const shared = this.isAlreadyShared(webcamDeviceId);

View File

@ -15,7 +15,6 @@ import {
getVirtualBgImagePath,
} from '/imports/ui/services/virtual-background/service'
import logger from '/imports/startup/client/logger';
import { simd } from 'wasm-feature-detect/dist/cjs/index';
const blurValue = '25px';
@ -387,10 +386,44 @@ export async function createVirtualBackgroundService(parameters = null) {
parameters.backgroundType = 'blur';
parameters.isVirtualBackground = false;
} else {
parameters.virtualSource = virtualBackgroundImagePath + parameters.backgroundFilename;
if (parameters.customParams) {
parameters.virtualSource = parameters.customParams.file;
} else {
parameters.virtualSource = virtualBackgroundImagePath + parameters.backgroundFilename;
if (parameters.customParams.file) {
parameters.virtualSource = parameters.customParams.file;
} else {
const imageUrl = parameters.customParams.url;
// Function to convert image URL to a File object
async function getFileFromUrl(url) {
try {
const response = await fetch(url, {
credentials: 'omit',
mode: 'cors',
headers: {
'Accept': 'image/*',
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const blob = await response.blob();
const file = new File([blob], 'fetchedWebcamBackground', { type: blob.type });
return file;
} catch (error) {
logger.error('Fetch error:', error);
return null;
}
}
let fetchedWebcamBackground = await getFileFromUrl(imageUrl);
if (fetchedWebcamBackground) {
parameters.virtualSource = URL.createObjectURL(fetchedWebcamBackground);
} else {
logger.error('Failed to fetch custom webcam background image. Using fallback image.');
}
}
}
}

View File

@ -321,6 +321,10 @@ defaultGuestWaitURL=${bigbluebutton.web.serverURL}/html5client/guestWait
useDefaultAvatar=false
defaultAvatarURL=${bigbluebutton.web.serverURL}/html5client/resources/images/avatar.png
# The default webcam background image to display.
useDefaultWebcamBackground=false
defaultWebcamBackgroundURL=${bigbluebutton.web.serverURL}/html5client/resources/images/virtual-backgrounds/board.jpg
apiVersion=2.0
# Salt which is used by 3rd-party apps to authenticate api calls

View File

@ -163,6 +163,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="defaultMaxPinnedCameras" value="${maxPinnedCameras}"/>
<property name="useDefaultAvatar" value="${useDefaultAvatar}"/>
<property name="defaultAvatarURL" value="${defaultAvatarURL}"/>
<property name="useDefaultWebcamBackground" value="${useDefaultWebcamBackground}"/>
<property name="defaultWebcamBackgroundURL" value="${defaultWebcamBackgroundURL}"/>
<property name="defaultGuestPolicy" value="${defaultGuestPolicy}"/>
<property name="authenticatedGuest" value="${authenticatedGuest}"/>
<property name="defaultAllowPromoteGuestToModerator" value="${defaultAllowPromoteGuestToModerator}"/>

View File

@ -405,6 +405,12 @@ class ApiController {
us.avatarURL = meeting.defaultAvatarURL
}
if (!StringUtils.isEmpty(params.webcamBackgroundURL)) {
us.webcamBackgroundURL = params.webcamBackgroundURL;
} else {
us.webcamBackgroundURL = meeting.defaultWebcamBackgroundURL
}
if (!StringUtils.isEmpty(params.excludeFromDashboard)) {
try {
us.excludeFromDashboard = Boolean.parseBoolean(params.excludeFromDashboard)
@ -435,6 +441,7 @@ class ApiController {
us.externUserID,
us.authToken,
us.avatarURL,
us.webcamBackgroundURL,
us.guest,
us.authed,
guestStatusVal,
@ -941,6 +948,7 @@ class ApiController {
logoutUrl us.logoutUrl
defaultLayout us.defaultLayout
avatarURL us.avatarURL
webcamBackgroundURL us.webcamBackgroundURL
if (meeting.breakoutRoomsParams != null) {
breakoutRooms {
record meeting.breakoutRoomsParams.record

View File

@ -55,6 +55,12 @@ const joinEndpointTableData = [
"type": "String",
"description": (<>The link for the users avatar to be displayed (default can be enabled/disabled and set with useDefaultAvatar and defaultAvatarURL in bbb-web.properties).</>)
},
{
"name": "webcamBackgroundURL",
"required": false,
"type": "String",
"description": (<>The link for the user's webcam background to be displayed (default can be enabled/disabled and set with useDefaultWebcamBackground and defaultWebcamBackgroundURL in bigbluebutton.properties). Added in BigBlueButton 2.7.10.</>)
},
{
"name": "redirect",
"required": false,