diff --git a/recording/server.conf.in b/recording/server.conf.in index 46b5be7628b..10a61315f80 100644 --- a/recording/server.conf.in +++ b/recording/server.conf.in @@ -109,3 +109,11 @@ # The extension of the file for audio and video recordings. #extensionvideo = .webm + +[recording] +# Browser to use for recordings. Please note that the "chrome" value does not +# refer to the web browser, but to the Selenium WebDriver. In practice, "chrome" +# will use Google Chrome, or Chromium if Google Chrome is not installed. +# Allowed values: firefox, chrome +# Defaults to firefox +# browser = firefox diff --git a/recording/src/nextcloud/talk/recording/Config.py b/recording/src/nextcloud/talk/recording/Config.py index e0e85beb8d1..1c7a85ab29c 100644 --- a/recording/src/nextcloud/talk/recording/Config.py +++ b/recording/src/nextcloud/talk/recording/Config.py @@ -1,5 +1,6 @@ # # @copyright Copyright (c) 2023, Daniel Calviño Sánchez (danxuliu@gmail.com) +# @copyright Copyright (c) 2023, Elmer Miroslav Mosher Golovin (miroslav@mishamosher.com) # # @license GNU AGPL version 3 or any later version # @@ -236,4 +237,12 @@ def getFfmpegExtensionVideo(self): """ return self._configParser.get('ffmpeg', 'extensionvideo', fallback='.webm') + def getBrowserForRecording(self): + """ + Returns the browser identifier that will be used for recordings. + + Defaults to "firefox". + """ + return self._configParser.get('recording', 'browser', fallback='firefox') + config = Config() diff --git a/recording/src/nextcloud/talk/recording/Participant.py b/recording/src/nextcloud/talk/recording/Participant.py index 2bf0a5f4f21..ba357b09edd 100644 --- a/recording/src/nextcloud/talk/recording/Participant.py +++ b/recording/src/nextcloud/talk/recording/Participant.py @@ -1,5 +1,6 @@ # # @copyright Copyright (c) 2023, Daniel Calviño Sánchez (danxuliu@gmail.com) +# @copyright Copyright (c) 2023, Elmer Miroslav Mosher Golovin (miroslav@mishamosher.com) # # @license GNU AGPL version 3 or any later version # @@ -33,7 +34,12 @@ from secrets import token_urlsafe from selenium import webdriver from selenium.webdriver.common.by import By +from selenium.webdriver.chrome.options import Options as ChromeOptions +from selenium.webdriver.chrome.service import Service as ChromeService +from selenium.webdriver.chrome.webdriver import WebDriver as ChromeDriver +from selenium.webdriver.firefox.options import Options as FirefoxOptions from selenium.webdriver.firefox.service import Service as FirefoxService +from selenium.webdriver.firefox.webdriver import WebDriver as FirefoxDriver from selenium.webdriver.support.wait import WebDriverWait from shutil import disk_usage from time import sleep @@ -202,6 +208,51 @@ def __del__(self): # created in "/tmp". self.driver.quit() + def startChrome(self, width, height, env): + """ + Starts a Chrome instance. + + Will use Chromium if Google Chrome is not installed. + + :param width: the width of the browser window. + :param height: the height of the browser window. + :param env: the environment variables, including the display to start + the browser in. + """ + + options = ChromeOptions() + + # "webSocketUrl" is needed for BiDi. + options.set_capability('webSocketUrl', True) + + options.add_argument('--use-fake-ui-for-media-stream') + + # Allow to play media without user interaction. + options.add_argument('--autoplay-policy=no-user-gesture-required') + + options.add_argument('--kiosk') + options.add_argument(f'--window-size={width},{height}') + options.add_argument('--disable-infobars') + options.add_experimental_option("excludeSwitches", ["enable-automation"]) + + if disk_usage('/dev/shm').free < 2147483648: + self._logger.info('Less than 2 GiB available in "/dev/shm", usage disabled') + options.add_argument("--disable-dev-shm-usage") + + if disk_usage('/tmp').free < 134217728: + self._logger.warning('Less than 128 MiB available in "/tmp", strange failures may occur') + + service = ChromeService( + env=env, + ) + + self.driver = ChromeDriver( + options=options, + service=service, + ) + + self.bidiLogsHelper = BiDiLogsHelper(self.driver, self._parentLogger) + def startFirefox(self, width, height, env): """ Starts a Firefox instance. @@ -212,7 +263,7 @@ def startFirefox(self, width, height, env): the browser in. """ - options = webdriver.FirefoxOptions() + options = FirefoxOptions() # "webSocketUrl" is needed for BiDi; this should be set already by # default, but just in case. @@ -239,7 +290,7 @@ def startFirefox(self, width, height, env): env=env, ) - self.driver = webdriver.Firefox( + self.driver = FirefoxDriver( options=options, service=service, ) @@ -418,7 +469,9 @@ def __init__(self, browser, nextcloudUrl, width, height, env, parentLogger): self.seleniumHelper = SeleniumHelper(parentLogger) - if browser == 'firefox': + if browser == 'chrome': + self.seleniumHelper.startChrome(width, height, env) + elif browser == 'firefox': self.seleniumHelper.startFirefox(width, height, env) else: raise Exception('Invalid browser: ' + browser) diff --git a/recording/src/nextcloud/talk/recording/Service.py b/recording/src/nextcloud/talk/recording/Service.py index 530d5c7f073..ced940fed37 100644 --- a/recording/src/nextcloud/talk/recording/Service.py +++ b/recording/src/nextcloud/talk/recording/Service.py @@ -1,5 +1,6 @@ # # @copyright Copyright (c) 2023, Daniel Calviño Sánchez (danxuliu@gmail.com) +# @copyright Copyright (c) 2023, Elmer Miroslav Mosher Golovin (miroslav@mishamosher.com) # # @license GNU AGPL version 3 or any later version # @@ -188,8 +189,10 @@ def start(self, actorType, actorId): env = self._display.env() env['PULSE_SINK'] = audioSinkIndex + browser = config.getBrowserForRecording() + self._logger.debug("Starting participant") - self._participant = Participant('firefox', self.backend, width, height, env, self._logger) + self._participant = Participant(browser, self.backend, width, height, env, self._logger) self._logger.debug("Joining call") self._participant.joinCall(self.token)