This commit renames the isMuted variable into isSelfMuted to reflect the user action of self muting, as opposed to the state of being mute, which will be distinct in an upcoming commit. part of task-2720026 closes odoo/odoo#82199 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
131 lines
5.8 KiB
Python
131 lines
5.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from collections import defaultdict
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import api, fields, models
|
|
|
|
|
|
class MailRtcSession(models.Model):
|
|
_name = 'mail.channel.rtc.session'
|
|
_description = 'Mail RTC session'
|
|
|
|
channel_partner_id = fields.Many2one('mail.channel.partner', index=True, required=True, ondelete='cascade')
|
|
channel_id = fields.Many2one('mail.channel', related='channel_partner_id.channel_id', store=True, readonly=True)
|
|
partner_id = fields.Many2one('res.partner', related='channel_partner_id.partner_id', string="Partner")
|
|
guest_id = fields.Many2one('mail.guest', related='channel_partner_id.guest_id')
|
|
|
|
write_date = fields.Datetime("Last Updated On", index=True)
|
|
|
|
is_screen_sharing_on = fields.Boolean(string="Is sharing the screen")
|
|
is_camera_on = fields.Boolean(string="Is sending user video")
|
|
is_muted = fields.Boolean(string="Is microphone muted")
|
|
is_deaf = fields.Boolean(string="Has disabled incoming sound")
|
|
|
|
_sql_constraints = [
|
|
('channel_partner_unique', 'UNIQUE(channel_partner_id)',
|
|
'There can only be one rtc session per channel partner')
|
|
]
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
rtc_sessions = super().create(vals_list)
|
|
self.env['bus.bus']._sendmany([(channel, 'mail.channel/rtc_sessions_update', {
|
|
'id': channel.id,
|
|
'rtcSessions': [('insert', sessions_data)],
|
|
}) for channel, sessions_data in rtc_sessions._mail_rtc_session_format_by_channel().items()])
|
|
return rtc_sessions
|
|
|
|
def unlink(self):
|
|
channels = self.channel_id
|
|
for channel in channels:
|
|
if channel.rtc_session_ids and len(channel.rtc_session_ids - self) == 0:
|
|
# If there is no member left in the RTC call, all invitations are cancelled.
|
|
# Note: invitation depends on field `rtc_inviting_session_id` so the cancel must be
|
|
# done before the delete to be able to know who was invited.
|
|
channel._rtc_cancel_invitations()
|
|
notifications = [(channel, 'mail.channel/rtc_sessions_update', {
|
|
'id': channel.id,
|
|
'rtcSessions': [('insert-and-unlink', [{'id': session_data['id']} for session_data in sessions_data])],
|
|
}) for channel, sessions_data in self._mail_rtc_session_format_by_channel().items()]
|
|
for rtc_session in self:
|
|
target = rtc_session.guest_id or rtc_session.partner_id
|
|
notifications.append((target, 'mail.channel.rtc.session/ended', {'sessionId': rtc_session.id}))
|
|
self.env['bus.bus']._sendmany(notifications)
|
|
return super().unlink()
|
|
|
|
def _update_and_broadcast(self, values):
|
|
""" Updates the session and notifies all members of the channel
|
|
of the change.
|
|
"""
|
|
valid_values = {'is_screen_sharing_on', 'is_camera_on', 'is_muted', 'is_deaf'}
|
|
self.write({key: values[key] for key in valid_values if key in valid_values})
|
|
session_data = self._mail_rtc_session_format()
|
|
self.env['bus.bus']._sendone(self.channel_id, 'mail.channel.rtc.session/insert', session_data)
|
|
|
|
@api.autovacuum
|
|
def _gc_inactive_sessions(self):
|
|
""" Garbage collect sessions that aren't active anymore,
|
|
this can happen when the server or the user's browser crash
|
|
or when the user's odoo session ends.
|
|
"""
|
|
self.search(self._inactive_rtc_session_domain()).unlink()
|
|
|
|
def action_disconnect(self):
|
|
self.unlink()
|
|
|
|
def _delete_inactive_rtc_sessions(self):
|
|
"""Deletes the inactive sessions from self."""
|
|
self.filtered_domain(self._inactive_rtc_session_domain()).unlink()
|
|
|
|
def _notify_peers(self, notifications):
|
|
""" Used for peer-to-peer communication,
|
|
guarantees that the sender is the current guest or partner.
|
|
|
|
:param notifications: list of tuple with the following elements:
|
|
- target_session_ids: a list of mail.channel.rtc.session ids
|
|
- content: a string with the content to be sent to the targets
|
|
"""
|
|
self.ensure_one()
|
|
payload_by_target = defaultdict(lambda: {'sender': self.id, 'notifications': []})
|
|
for target_session_ids, content in notifications:
|
|
for target_session in self.env['mail.channel.rtc.session'].browse(target_session_ids).exists():
|
|
target = target_session.guest_id or target_session.partner_id
|
|
payload_by_target[target]['notifications'].append(content)
|
|
return self.env['bus.bus']._sendmany([(target, 'mail.channel.rtc.session/peer_notification', payload) for target, payload in payload_by_target.items()])
|
|
|
|
def _mail_rtc_session_format(self, complete_info=True):
|
|
self.ensure_one()
|
|
vals = {
|
|
'id': self.id,
|
|
}
|
|
if complete_info:
|
|
vals.update({
|
|
'isCameraOn': self.is_camera_on,
|
|
'isDeaf': self.is_deaf,
|
|
'isSelfMuted': self.is_muted,
|
|
'isScreenSharingOn': self.is_screen_sharing_on,
|
|
})
|
|
if self.guest_id:
|
|
vals['guest'] = [('insert', {
|
|
'id': self.guest_id.id,
|
|
'name': self.guest_id.name,
|
|
})]
|
|
else:
|
|
vals['partner'] = [('insert', {
|
|
'id': self.partner_id.id,
|
|
'name': self.partner_id.name,
|
|
})]
|
|
return vals
|
|
|
|
def _mail_rtc_session_format_by_channel(self):
|
|
data = {}
|
|
for rtc_session in self:
|
|
data.setdefault(rtc_session.channel_id, []).append(rtc_session._mail_rtc_session_format())
|
|
return data
|
|
|
|
@api.model
|
|
def _inactive_rtc_session_domain(self):
|
|
return [('write_date', '<', fields.Datetime.now() - relativedelta(minutes=1))]
|