Files
Cloud-CMS/lib/Service/PlayerActionService.php
Matt Batchelder 05ce0da296 Initial Upload
2025-12-02 10:32:59 -05:00

196 lines
5.8 KiB
PHP

<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Service;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Xibo\Entity\Display;
use Xibo\Support\Exception\ConfigurationException;
use Xibo\Support\Exception\InvalidArgumentException;
use Xibo\XMR\PlayerAction;
use Xibo\XMR\PlayerActionException;
/**
* Class PlayerActionService
* @package Xibo\Service
*/
class PlayerActionService implements PlayerActionServiceInterface
{
private ?string $xmrAddress;
/** @var PlayerAction[] */
private array $actions = [];
/**
* @inheritdoc
*/
public function __construct(
private readonly ConfigServiceInterface $config,
private readonly LogServiceInterface $log,
private readonly bool $triggerPlayerActions
) {
$this->xmrAddress = null;
}
/**
* Get Config
* @return ConfigServiceInterface
*/
private function getConfig(): ConfigServiceInterface
{
return $this->config;
}
/**
* @inheritdoc
*/
public function sendAction($displays, $action): void
{
if (!$this->triggerPlayerActions) {
return;
}
// XMR network address
if ($this->xmrAddress == null) {
$this->xmrAddress = $this->getConfig()->getSetting('XMR_ADDRESS');
}
if (empty($this->xmrAddress)) {
throw new InvalidArgumentException(__('XMR address is not set'), 'xmrAddress');
}
if (!is_array($displays)) {
$displays = [$displays];
}
// Send a message to all displays
foreach ($displays as $display) {
/* @var Display $display */
$isEncrypt = false;
if ($display->xmrChannel == '') {
throw new InvalidArgumentException(
sprintf(
__('%s is not configured or ready to receive push commands over XMR. Please contact your administrator.'),//phpcs:ignore
$display->display
),
'xmrChannel'
);
}
// If we are using the old ZMQ XMR service, we also need to encrypt the message
if (!$display->isWebSocketXmrSupported()) {
// We also need a xmrPubKey
$isEncrypt = true;
if ($display->xmrPubKey == '') {
throw new InvalidArgumentException(
sprintf(
__('%s is not configured or ready to receive push commands over XMR. Please contact your administrator.'),//phpcs:ignore
$display->display
),
'xmrPubKey'
);
}
}
// Do not send anything if XMR is disabled.
if (($isEncrypt && $this->getConfig()->getSetting('XMR_WS_ADDRESS') === 'DISABLED')
|| (!$isEncrypt && $this->getConfig()->getSetting('XMR_PUB_ADDRESS') === 'DISABLED')
) {
continue;
}
$displayAction = clone $action;
try {
$displayAction->setIdentity($display->xmrChannel, $isEncrypt, $display->xmrPubKey ?? null);
} catch (\Exception $exception) {
throw new InvalidArgumentException(
sprintf(
__('%s Invalid XMR registration'),
$display->display
),
'xmrPubKey'
);
}
// Add to collection
$this->actions[] = $displayAction;
}
}
/** @inheritDoc */
public function getQueue(): array
{
return $this->actions;
}
/**
* @inheritdoc
*/
public function processQueue(): void
{
if (count($this->actions) > 0) {
$this->log->debug('Player Action Service is looking to send %d actions', count($this->actions));
} else {
return;
}
// XMR network address
if ($this->xmrAddress == null) {
$this->xmrAddress = $this->getConfig()->getSetting('XMR_ADDRESS');
}
$client = new Client($this->config->getGuzzleProxy([
'base_uri' => $this->getConfig()->getSetting('XMR_ADDRESS'),
]));
$failures = 0;
// TODO: could I send them all in one request instead?
foreach ($this->actions as $action) {
/** @var PlayerAction $action */
try {
// Send each action
$client->post('/', [
'json' => $action->finaliseMessage(),
]);
} catch (GuzzleException | PlayerActionException $e) {
$this->log->error('Player action connection failed. E = ' . $e->getMessage());
$failures++;
}
}
if ($failures > 0) {
throw new ConfigurationException(
sprintf(
__('%d of %d player actions failed'),
$failures,
count($this->actions)
)
);
}
}
}