feat(visitors): Adds showing shared files in the meeting.

This commit is contained in:
damencho
2025-07-10 17:04:41 +03:00
committed by Дамян Минков
parent b511f4b8df
commit 6bd3ed5ae4
5 changed files with 191 additions and 72 deletions

View File

@@ -72,7 +72,7 @@ paths:
properties: properties:
metadata: metadata:
type: string type: string
description: The metadata of the document in JSON format. description: The metadata of the document in JSON format. Must conform to FileMetadata schema.
example: '{"conferenceFullName":"myroomname@conference.tenant.jitsi-meet.example.com","timestamp":1741017572040,"fileSize":1042157,"fileId":"e393a7e5-e790-4f43-836e-d27238201904"}' example: '{"conferenceFullName":"myroomname@conference.tenant.jitsi-meet.example.com","timestamp":1741017572040,"fileSize":1042157,"fileId":"e393a7e5-e790-4f43-836e-d27238201904"}'
file: file:
type: string type: string
@@ -162,6 +162,34 @@ paths:
description: OK description: OK
components: components:
schemas: schemas:
FileMetadata:
type: object
required:
- fileId
- conferenceFullName
- timestamp
- fileSize
properties:
fileId:
type: string
format: uuid
description: Client-generated unique identifier for the file
example: e393a7e5-e790-4f43-836e-d27238201904
conferenceFullName:
type: string
description: Full name of the conference/meeting room
example: myroomname@conference.tenant.jitsi-meet.example.com
timestamp:
type: integer
format: int64
description: Upload timestamp in milliseconds since epoch
example: 1741017572040
fileSize:
type: integer
format: int64
description: Size of the file in bytes
example: 1042157
description: Metadata structure that must be sent as JSON string in the metadata field
Payload: Payload:
type: object type: object
properties: properties:

View File

@@ -13,6 +13,11 @@ local JSON_TYPE_REMOVE_FILE = 'remove';
local JSON_TYPE_LIST_FILES = 'list'; local JSON_TYPE_LIST_FILES = 'list';
local NICK_NS = 'http://jabber.org/protocol/nick'; local NICK_NS = 'http://jabber.org/protocol/nick';
-- this is the main virtual host of the main prosody that this vnode serves
local main_domain = module:get_option_string('main_domain');
-- only the visitor prosody has main_domain setting
local is_visitor_prosody = main_domain ~= nil;
local muc_component_host = module:get_option_string('muc_component'); local muc_component_host = module:get_option_string('muc_component');
if muc_component_host == nil then if muc_component_host == nil then
module:log('error', 'No muc_component specified. No muc to operate on!'); module:log('error', 'No muc_component specified. No muc to operate on!');
@@ -94,34 +99,13 @@ function on_message(event)
end end
msg_obj.conferenceFullName = internal_room_jid_match_rewrite(room.jid); msg_obj.conferenceFullName = internal_room_jid_match_rewrite(room.jid);
if not room.jitsi_shared_files then module:context(muc_domain_base):fire_event('jitsi-filesharing-add', {
room.jitsi_shared_files = {}; room = room; file = msg_obj; actor = occupant.nick;
end
room.jitsi_shared_files[msg_obj.fileId] = msg_obj;
local json_msg, error = json.encode({
type = FILE_SHARING_IDENTITY_TYPE,
event = JSON_TYPE_ADD_FILE,
file = msg_obj
}); });
if not json_msg then module:context(muc_domain_base):fire_event('jitsi-filesharing-updated', {
module:log('error', 'skip sending add request room:%s error:%s', room.jid, error); room = room;
return false });
end
local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
:text(json_msg):up();
-- send add file to all occupants except jicofo and sender
for _, room_occupant in room:each_occupant() do
if not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= occupant.nick then
local to_send = st.clone(stanza);
to_send.attr.to = room_occupant.jid;
module:send(to_send);
end
end
return true; return true;
elseif message.attr.type == JSON_TYPE_REMOVE_FILE then elseif message.attr.type == JSON_TYPE_REMOVE_FILE then
@@ -130,34 +114,13 @@ function on_message(event)
return true; return true;
end end
if not room.jitsi_shared_files then module:context(muc_domain_base):fire_event('jitsi-filesharing-remove', {
return; room = room; id = message.attr.fileId; actor = occupant.nick;
end
room.jitsi_shared_files[message.attr.fileId] = nil;
local json_msg, error = json.encode({
type = FILE_SHARING_IDENTITY_TYPE,
event = JSON_TYPE_REMOVE_FILE,
fileId = message.attr.fileId
}); });
if not json_msg then module:context(muc_domain_base):fire_event('jitsi-filesharing-updated', {
module:log('error', 'skip sending remove request room:%s error:%s', room.jid, error); room = room;
return false });
end
local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
:text(json_msg):up();
-- send remove file to all occupants except jicofo and sender
for _, room_occupant in room:each_occupant() do
if not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= occupant.nick then
local to_send = st.clone(stanza);
to_send.attr.to = room_occupant.jid;
module:send(to_send);
end
end
return true; return true;
else else
@@ -202,7 +165,81 @@ end);
module:hook('message/host', on_message); module:hook('message/host', on_message);
process_host_module(muc_domain_base, function(host_module, host) process_host_module(muc_domain_base, function(host_module, host)
module:context(host_module.host):fire_event('jitsi-add-identity', { module:context(muc_domain_base):fire_event('jitsi-add-identity', {
name = FILE_SHARING_IDENTITY_TYPE; host = module.host; name = FILE_SHARING_IDENTITY_TYPE; host = module.host;
}); });
module:context(muc_domain_base):hook('jitsi-filesharing-add', function(event)
local actor, file, room = event.actor, event.file, event.room;
if not room.jitsi_shared_files then
room.jitsi_shared_files = {};
end
room.jitsi_shared_files[file.fileId] = file;
local json_msg, error = json.encode({
type = FILE_SHARING_IDENTITY_TYPE,
event = JSON_TYPE_ADD_FILE,
file = file
});
if not json_msg then
module:log('error', 'skip sending add request room:%s error:%s', room.jid, error);
return false
end
local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
:text(json_msg):up();
-- send add file to all occupants except jicofo and sender
-- if this is visitor prosody send it only to visitors
for _, room_occupant in room:each_occupant() do
local send_event = not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= actor;
if is_visitor_prosody then
send_event = room_occupant.role == 'visitor';
end
if send_event then
local to_send = st.clone(stanza);
to_send.attr.to = room_occupant.jid;
module:send(to_send);
end
end
end);
module:context(muc_domain_base):hook('jitsi-filesharing-remove', function(event)
local actor, id, room = event.actor, event.id, event.room;
if not room.jitsi_shared_files then
return;
end
room.jitsi_shared_files[id] = nil;
local json_msg, error = json.encode({
type = FILE_SHARING_IDENTITY_TYPE,
event = JSON_TYPE_REMOVE_FILE,
fileId = id
});
if not json_msg then
module:log('error', 'skip sending remove request room:%s error:%s', room.jid, error);
return false
end
local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
:text(json_msg):up();
-- send remove file to all occupants except jicofo and sender
-- if this is visitor prosody send it only to visitors
for _, room_occupant in room:each_occupant() do
local send_event = not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= actor;
if is_visitor_prosody then
send_event = room_occupant.role == 'visitor';
end
if send_event then
local to_send = st.clone(stanza);
to_send.attr.to = room_occupant.jid;
module:send(to_send);
end
end
end);
end); end);

View File

@@ -16,6 +16,7 @@ local new_id = require 'util.id'.medium;
local filters = require 'util.filters'; local filters = require 'util.filters';
local array = require 'util.array'; local array = require 'util.array';
local set = require 'util.set'; local set = require 'util.set';
local json = require 'cjson.safe';
local util = module:require 'util'; local util = module:require 'util';
local is_admin = util.is_admin; local is_admin = util.is_admin;
@@ -27,6 +28,7 @@ local get_focus_occupant = util.get_focus_occupant;
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite; local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
local presence_check_status = util.presence_check_status; local presence_check_status = util.presence_check_status;
local respond_iq_result = util.respond_iq_result; local respond_iq_result = util.respond_iq_result;
local table_compare = util.table_compare;
local PARTICIPANT_PROP_RAISE_HAND = 'jitsi_participant_raisedHand'; local PARTICIPANT_PROP_RAISE_HAND = 'jitsi_participant_raisedHand';
local PARTICIPANT_PROP_REQUEST_TRANSCRIPTION = 'jitsi_participant_requestingTranscription'; local PARTICIPANT_PROP_REQUEST_TRANSCRIPTION = 'jitsi_participant_requestingTranscription';
@@ -672,6 +674,29 @@ local function iq_from_main_handler(event)
end end
end end
local files = node:get_child('files');
if files then
local received_files = {};
for _, child in ipairs(files.tags) do
if child.name == 'file' then
received_files[child.attr.id] = json.decode(child:get_text());
end
end
-- fire events so file sharing component will add/remove files and will notify clients
local removed, added = table_compare(room.jitsi_shared_files or {}, received_files)
for _, id in ipairs(removed) do
module:context(local_domain):fire_event('jitsi-filesharing-remove', {
room = room; id = id;
});
end
for _, id in ipairs(added) do
module:context(local_domain):fire_event('jitsi-filesharing-add', {
room = room; file = received_files[id];
});
end
end
if fire_jicofo_unlock then if fire_jicofo_unlock then
-- everything is connected allow participants to join -- everything is connected allow participants to join
module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; }); module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; });

View File

@@ -16,6 +16,7 @@ local is_admin = util.is_admin;
local presence_check_status = util.presence_check_status; local presence_check_status = util.presence_check_status;
local process_host_module = util.process_host_module; local process_host_module = util.process_host_module;
local is_transcriber_jigasi = util.is_transcriber_jigasi; local is_transcriber_jigasi = util.is_transcriber_jigasi;
local json = require 'cjson.safe';
local MUC_NS = 'http://jabber.org/protocol/muc'; local MUC_NS = 'http://jabber.org/protocol/muc';
@@ -78,6 +79,17 @@ local function send_visitors_iq(conference_service, room, type)
end end
visitors_iq:up(); visitors_iq:up();
-- files that are shared in the room
if room.jitsi_shared_files then
visitors_iq:tag('files', { xmlns = 'jitsi:visitors' });
for k, v in pairs(room.jitsi_shared_files) do
visitors_iq:tag('file', {
id = k
}):text(json.encode(v)):up();
end
visitors_iq:up();
end
end end
visitors_iq:up(); visitors_iq:up();
@@ -370,23 +382,17 @@ process_host_module(main_muc_component_config, function(host_module, host)
end, -2); end, -2);
end); end);
module:hook('jitsi-lobby-enabled', function(event) local function update_vnodes_for_room(event)
local room = event.room; local room = event.room;
if visitors_nodes[room.jid] then if visitors_nodes[room.jid] then
-- we need to update all vnodes -- we need to update all vnodes
local vnodes = visitors_nodes[room.jid].nodes; local vnodes = visitors_nodes[room.jid].nodes;
for conference_service in pairs(vnodes) do for conference_service in pairs(vnodes) do
send_visitors_iq(conference_service, room, 'update'); send_visitors_iq(conference_service, room, 'update');
end
end end
end end
end);
module:hook('jitsi-lobby-disabled', function(event) module:hook('jitsi-lobby-enabled', update_vnodes_for_room);
local room = event.room; module:hook('jitsi-lobby-disabled', update_vnodes_for_room);
if visitors_nodes[room.jid] then module:hook('jitsi-filesharing-updated', update_vnodes_for_room);
-- we need to update all vnodes
local vnodes = visitors_nodes[room.jid].nodes;
for conference_service in pairs(vnodes) do
send_visitors_iq(conference_service, room, 'update');
end
end
end);

View File

@@ -616,6 +616,28 @@ local function table_add(t1, t2)
end end
end end
-- Returns as a first result the removed items and as a second the added items
local function table_compare(old_table, new_table)
local removed = {}
local added = {}
-- Find removed items (in old but not in new)
for id, _ in pairs(old_table) do
if new_table[id] == nil then
table.insert(removed, id)
end
end
-- Find added items (in new but not in old)
for id, _ in pairs(new_table) do
if old_table[id] == nil then
table.insert(added, id)
end
end
return removed, added
end
-- Splits a string using delimiter -- Splits a string using delimiter
function split_string(str, delimiter) function split_string(str, delimiter)
str = str .. delimiter; str = str .. delimiter;
@@ -697,6 +719,7 @@ return {
starts_with = starts_with; starts_with = starts_with;
starts_with_one_of = starts_with_one_of; starts_with_one_of = starts_with_one_of;
table_add = table_add; table_add = table_add;
table_compare = table_compare;
table_shallow_copy = table_shallow_copy; table_shallow_copy = table_shallow_copy;
table_find = table_find; table_find = table_find;
}; };