Merge commit '22451f4a7078faf579e8b4f67d0b51e03981f135'
This commit is contained in:
parent
31f0c3619a
commit
501ab48dcb
|
@ -53,7 +53,6 @@
|
|||
"matomo/device-detector": "^5.0",
|
||||
"mezzio/mezzio-session": "^1.3",
|
||||
"mezzio/mezzio-session-cache": "^1.4",
|
||||
"mnapoli/silly-php-di": "^1.2",
|
||||
"monolog/monolog": "^2",
|
||||
"myclabs/deep-copy": "^1.10",
|
||||
"nesbot/carbon": "^2.36",
|
||||
|
@ -100,7 +99,8 @@
|
|||
"symfony/polyfill-php72": "1.99",
|
||||
"symfony/polyfill-php73": "1.99",
|
||||
"symfony/polyfill-php74": "1.99",
|
||||
"symfony/polyfill-php80": "1.99"
|
||||
"symfony/polyfill-php80": "1.99",
|
||||
"symfony/polyfill-php81": "1.99"
|
||||
},
|
||||
"conflict": {
|
||||
"gettext/gettext": ">=5"
|
||||
|
@ -132,7 +132,15 @@
|
|||
"config": {
|
||||
"discard-changes": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"ergebnis/composer-normalize": true,
|
||||
"pyrech/composer-changelogs": true,
|
||||
"ramsey/composer-repl": true,
|
||||
"wikimedia/composer-merge-plugin": true
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"merge-plugin": {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "fd1e38effd0adbef62a54c83b3a4f856",
|
||||
"content-hash": "46cce91d9427c4a7e2297a993606ed14",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
|
@ -486,19 +486,19 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/AzuraCast/slim-callable-eventdispatcher.git",
|
||||
"reference": "0c1a17c81573fbdbcb65b04aea5ddf1743889e91"
|
||||
"reference": "0260f552c84f312819dca7556f03f0386f40b14c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/AzuraCast/slim-callable-eventdispatcher/zipball/0c1a17c81573fbdbcb65b04aea5ddf1743889e91",
|
||||
"reference": "0c1a17c81573fbdbcb65b04aea5ddf1743889e91",
|
||||
"url": "https://api.github.com/repos/AzuraCast/slim-callable-eventdispatcher/zipball/0260f552c84f312819dca7556f03f0386f40b14c",
|
||||
"reference": "0260f552c84f312819dca7556f03f0386f40b14c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": ">=8.0",
|
||||
"slim/slim": "^4",
|
||||
"symfony/event-dispatcher": "^5"
|
||||
"symfony/event-dispatcher": "^5|^6"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-parallel-lint/php-console-highlighter": "^0.5.0",
|
||||
|
@ -543,7 +543,7 @@
|
|||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-05T00:22:23+00:00"
|
||||
"time": "2021-12-22T17:29:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
|
@ -4158,104 +4158,6 @@
|
|||
],
|
||||
"time": "2021-09-15T08:19:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mnapoli/silly",
|
||||
"version": "1.7.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mnapoli/silly.git",
|
||||
"reference": "e437baa502c3e1691d342374e71446d4bac1cad5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mnapoli/silly/zipball/e437baa502c3e1691d342374e71446d4bac1cad5",
|
||||
"reference": "e437baa502c3e1691d342374e71446d4bac1cad5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"php-di/invoker": "~2.0",
|
||||
"psr/container": "^1.0|^2.0",
|
||||
"symfony/console": "~3.0|~4.0|~5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.12",
|
||||
"mnapoli/phpunit-easymock": "~1.0",
|
||||
"phpunit/phpunit": "~6.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Silly\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Silly CLI micro-framework based on Symfony Console",
|
||||
"keywords": [
|
||||
"PSR-11",
|
||||
"cli",
|
||||
"console",
|
||||
"framework",
|
||||
"micro-framework",
|
||||
"silly"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mnapoli/silly/issues",
|
||||
"source": "https://github.com/mnapoli/silly/tree/1.7.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/mnapoli",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/mnapoli/silly",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-12-13T09:21:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mnapoli/silly-php-di",
|
||||
"version": "1.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mnapoli/silly-php-di.git",
|
||||
"reference": "bd4af1c2cdf6141dc941a9a4728d1db4d4b01ef0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mnapoli/silly-php-di/zipball/bd4af1c2cdf6141dc941a9a4728d1db4d4b01ef0",
|
||||
"reference": "bd4af1c2cdf6141dc941a9a4728d1db4d4b01ef0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"mnapoli/silly": "~1.1",
|
||||
"php-di/php-di": "~4.4 || ^5.0 || ^6.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Silly\\Edition\\PhpDi\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "Silly framework edition configured with PHP-DI",
|
||||
"support": {
|
||||
"issues": "https://github.com/mnapoli/silly-php-di/issues",
|
||||
"source": "https://github.com/mnapoli/silly-php-di/tree/master"
|
||||
},
|
||||
"time": "2018-04-05T08:53:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
"version": "2.3.5",
|
||||
|
@ -4483,9 +4385,6 @@
|
|||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"replace": {
|
||||
"myclabs/deep-copy": "self.version"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.0",
|
||||
"doctrine/common": "^2.6",
|
||||
|
@ -7028,25 +6927,25 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.0.0",
|
||||
"version": "v2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced"
|
||||
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced",
|
||||
"reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8",
|
||||
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0.2"
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.0-dev"
|
||||
"dev-main": "2.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
|
@ -7075,7 +6974,7 @@
|
|||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -7091,7 +6990,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-01T23:48:49+00:00"
|
||||
"time": "2021-07-12T14:48:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/doctrine-messenger",
|
||||
|
@ -8102,85 +8001,6 @@
|
|||
],
|
||||
"time": "2021-05-27T09:27:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php81",
|
||||
"version": "v1.23.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php81.git",
|
||||
"reference": "e66119f3de95efc359483f810c4c3e6436279436"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/e66119f3de95efc359483f810c4c3e6436279436",
|
||||
"reference": "e66119f3de95efc359483f810c4c3e6436279436",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.23-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php81\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php81/tree/v1.23.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-21T13:25:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v5.4.0",
|
||||
|
@ -8654,21 +8474,22 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
"version": "v2.4.1",
|
||||
"version": "v2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/service-contracts.git",
|
||||
"reference": "d664541b99d6fb0247ec5ff32e87238582236204"
|
||||
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/d664541b99d6fb0247ec5ff32e87238582236204",
|
||||
"reference": "d664541b99d6fb0247ec5ff32e87238582236204",
|
||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
|
||||
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"psr/container": "^1.1"
|
||||
"psr/container": "^1.1",
|
||||
"symfony/deprecation-contracts": "^2.1"
|
||||
},
|
||||
"conflict": {
|
||||
"ext-psr": "<1.1|>=2"
|
||||
|
@ -8679,7 +8500,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.4-dev"
|
||||
"dev-main": "2.5-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
|
@ -8716,7 +8537,7 @@
|
|||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v2.4.1"
|
||||
"source": "https://github.com/symfony/service-contracts/tree/v2.5.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -8732,7 +8553,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-04T16:37:19+00:00"
|
||||
"time": "2021-11-04T16:48:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/stopwatch",
|
||||
|
@ -12511,12 +12332,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Roave/SecurityAdvisories.git",
|
||||
"reference": "fff53639bf1fa25f311c3e54932ac8c827f9a343"
|
||||
"reference": "31d9d9e2977ae7d796d82271be09e46f4bdf41b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/fff53639bf1fa25f311c3e54932ac8c827f9a343",
|
||||
"reference": "fff53639bf1fa25f311c3e54932ac8c827f9a343",
|
||||
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/31d9d9e2977ae7d796d82271be09e46f4bdf41b3",
|
||||
"reference": "31d9d9e2977ae7d796d82271be09e46f4bdf41b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"conflict": {
|
||||
|
@ -12759,7 +12580,7 @@
|
|||
"shopware/production": "<=6.3.5.2",
|
||||
"shopware/shopware": "<5.7.6",
|
||||
"showdoc/showdoc": "<=2.9.13",
|
||||
"silverstripe/admin": "<4.8.1",
|
||||
"silverstripe/admin": ">=1,<1.8.1",
|
||||
"silverstripe/assets": ">=1,<1.4.7|>=1.5,<1.5.2",
|
||||
"silverstripe/cms": "<4.3.6|>=4.4,<4.4.4",
|
||||
"silverstripe/comments": ">=1.3,<1.9.99|>=2,<2.9.99|>=3,<3.1.1",
|
||||
|
@ -12936,7 +12757,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-12-17T20:13:17+00:00"
|
||||
"time": "2021-12-22T21:13:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
|
@ -14525,5 +14346,5 @@
|
|||
"ext-xmlwriter": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.1.0"
|
||||
"plugin-api-version": "2.2.0"
|
||||
}
|
||||
|
|
187
config/cli.php
187
config/cli.php
|
@ -1,160 +1,37 @@
|
|||
<?php
|
||||
|
||||
use App\Console\Application;
|
||||
use App\Console\Command;
|
||||
|
||||
return function (Application $console) {
|
||||
// Liquidsoap Internal CLI commands
|
||||
$console->command(
|
||||
'azuracast:internal:auth station-id [--dj-user=] [--dj-password=]',
|
||||
Command\Internal\DjAuthCommand::class
|
||||
)->setDescription('Authorize a streamer to connect as a source for the radio service.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:internal:djoff station-id [--dj-user=]',
|
||||
Command\Internal\DjOffCommand::class
|
||||
)->setDescription('Indicate that a DJ has finished streaming to a station.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:internal:djon station-id [--dj-user=]',
|
||||
Command\Internal\DjOnCommand::class
|
||||
)->setDescription('Indicate that a DJ has begun streaming to a station.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:internal:feedback station-id [-s|--song=] [-m|--media=] [-p|--playlist=]',
|
||||
Command\Internal\FeedbackCommand::class
|
||||
)->setDescription('Send upcoming song feedback from the AutoDJ back to AzuraCast.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:internal:sftp-event action username path [target-path] [ssh-cmd]',
|
||||
Command\Internal\SftpEventCommand::class
|
||||
)->setDescription('Process an event triggered via SFTP');
|
||||
|
||||
$console->command(
|
||||
'azuracast:internal:sftp-auth',
|
||||
Command\Internal\SftpAuthCommand::class
|
||||
)->setDescription('Attempt SFTP authentication');
|
||||
|
||||
$console->command(
|
||||
'azuracast:internal:nextsong station-id [as-autodj]',
|
||||
Command\Internal\NextSongCommand::class
|
||||
)->defaults(
|
||||
[
|
||||
'as-autodj' => true,
|
||||
]
|
||||
)->setDescription('Return the next song to the AutoDJ.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:internal:ip',
|
||||
Command\Internal\GetIpCommand::class
|
||||
)->setDescription('Get the external IP address for this instance.');
|
||||
|
||||
// Locales
|
||||
$console->command(
|
||||
'locale:generate',
|
||||
Command\Locale\GenerateCommand::class
|
||||
)->setDescription(__('Generate the translation locale file.'));
|
||||
|
||||
$console->command(
|
||||
'locale:import',
|
||||
Command\Locale\ImportCommand::class
|
||||
)->setDescription(__('Convert translated locale files into PHP arrays.'));
|
||||
|
||||
// Setup
|
||||
$console->command(
|
||||
'azuracast:setup:initialize',
|
||||
Command\InitializeCommand::class
|
||||
)->setDescription(__('Ensure key settings are initialized within AzuraCast.'));
|
||||
|
||||
$console->command(
|
||||
'azuracast:config:migrate',
|
||||
Command\MigrateConfigCommand::class
|
||||
)->setDescription(__('Migrate existing configuration to new INI format if any exists.'));
|
||||
|
||||
$console->command(
|
||||
'azuracast:setup:fixtures',
|
||||
Command\SetupFixturesCommand::class
|
||||
)->setDescription(__('Install fixtures for demo / local development.'));
|
||||
|
||||
$console->command(
|
||||
'azuracast:setup [--update] [--load-fixtures] [--release]',
|
||||
Command\SetupCommand::class
|
||||
)->setDescription(__('Run all general AzuraCast setup steps.'));
|
||||
|
||||
// Maintenance
|
||||
$console->command(
|
||||
'azuracast:radio:restart [station-name]',
|
||||
Command\RestartRadioCommand::class
|
||||
)->setDescription('Restart all radio stations, or a single one if specified.');
|
||||
|
||||
$console->command(
|
||||
'sync:run [--force] [task]',
|
||||
Command\SyncCommand::class
|
||||
)->setDescription(__('Run one or more scheduled synchronization tasks.'));
|
||||
|
||||
$console->command(
|
||||
'queue:process [runtime] [--worker-name=]',
|
||||
Command\MessageQueue\ProcessCommand::class
|
||||
)->setDescription(__('Process the message queue.'));
|
||||
|
||||
$console->command(
|
||||
'queue:clear [queue]',
|
||||
Command\MessageQueue\ClearCommand::class
|
||||
)->setDescription(__('Clear the contents of the message queue.'));
|
||||
|
||||
$console->command(
|
||||
'azuracast:media:reprocess [station-name]',
|
||||
Command\ReprocessMediaCommand::class
|
||||
)->setDescription('Manually reload all media metadata from file.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:api:docs',
|
||||
Command\GenerateApiDocsCommand::class
|
||||
)->setDescription('Trigger regeneration of AzuraCast API documentation.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:debug:optimize-tables',
|
||||
Command\Debug\OptimizeTablesCommand::class
|
||||
)->setDescription('Optimize all tables in the database.');
|
||||
|
||||
// User-side tools
|
||||
$console->command(
|
||||
'azuracast:account:list',
|
||||
Command\Users\ListCommand::class
|
||||
)->setDescription('List all accounts in the system.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:account:login-token email',
|
||||
Command\Users\LoginTokenCommand::class
|
||||
)->setDescription('Create a unique login recovery URL for the specified account.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:account:reset-password email',
|
||||
Command\Users\ResetPasswordCommand::class
|
||||
)->setDescription('Reset the password of the specified account.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:account:set-administrator email',
|
||||
Command\Users\SetAdministratorCommand::class
|
||||
)->setDescription('Set the account specified as a global administrator.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:settings:list',
|
||||
Command\Settings\ListCommand::class
|
||||
)->setDescription(__('List all settings in the AzuraCast settings database.'));
|
||||
|
||||
$console->command(
|
||||
'azuracast:settings:set setting-key setting-value',
|
||||
Command\Settings\SetCommand::class
|
||||
)->setDescription('Set the value of a setting in the AzuraCast settings database.');
|
||||
|
||||
$console->command(
|
||||
'azuracast:backup [path] [--storage-location-id=] [--exclude-media]',
|
||||
Command\Backup\BackupCommand::class
|
||||
)->setDescription(__('Back up the AzuraCast database and statistics (and optionally media).'));
|
||||
|
||||
$console->command(
|
||||
'azuracast:restore path [--restore] [--release]',
|
||||
Command\Backup\RestoreCommand::class
|
||||
)->setDescription('Restore a backup previously generated by AzuraCast.');
|
||||
return function (App\Event\BuildConsoleCommands $event) {
|
||||
$event->addAliases([
|
||||
'azuracast:backup' => Command\Backup\BackupCommand::class,
|
||||
'azuracast:restore' => Command\Backup\RestoreCommand::class,
|
||||
'azuracast:debug:optimize-tables' => Command\Debug\OptimizeTablesCommand::class,
|
||||
'azuracast:internal:auth' => Command\Internal\DjAuthCommand::class,
|
||||
'azuracast:internal:djoff' => Command\Internal\DjOffCommand::class,
|
||||
'azuracast:internal:djon' => Command\Internal\DjOnCommand::class,
|
||||
'azuracast:internal:feedback' => Command\Internal\FeedbackCommand::class,
|
||||
'azuracast:internal:sftp-event' => Command\Internal\SftpEventCommand::class,
|
||||
'azuracast:internal:sftp-auth' => Command\Internal\SftpAuthCommand::class,
|
||||
'azuracast:internal:nextsong' => Command\Internal\NextSongCommand::class,
|
||||
'azuracast:internal:ip' => Command\Internal\GetIpCommand::class,
|
||||
'locale:generate' => Command\Locale\GenerateCommand::class,
|
||||
'locale:import' => Command\Locale\ImportCommand::class,
|
||||
'queue:process' => Command\MessageQueue\ProcessCommand::class,
|
||||
'queue:clear' => Command\MessageQueue\ClearCommand::class,
|
||||
'azuracast:settings:list' => Command\Settings\ListCommand::class,
|
||||
'azuracast:settings:set' => Command\Settings\SetCommand::class,
|
||||
'azuracast:account:list' => Command\Users\ListCommand::class,
|
||||
'azuracast:account:login-token' => Command\Users\LoginTokenCommand::class,
|
||||
'azuracast:account:reset-password' => Command\Users\ResetPasswordCommand::class,
|
||||
'azuracast:account:set-administrator' => Command\Users\SetAdministratorCommand::class,
|
||||
'azuracast:setup:initialize' => Command\InitializeCommand::class,
|
||||
'azuracast:config:migrate' => Command\MigrateConfigCommand::class,
|
||||
'azuracast:setup:fixtures' => Command\SetupFixturesCommand::class,
|
||||
'azuracast:setup' => Command\SetupCommand::class,
|
||||
'azuracast:radio:restart' => Command\RestartRadioCommand::class,
|
||||
'sync:run' => Command\SyncCommand::class,
|
||||
'azuracast:media:reprocess' => Command\ReprocessMediaCommand::class,
|
||||
'azuracast:api:docs' => Command\GenerateApiDocsCommand::class,
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -11,13 +11,11 @@ return function (CallableEventDispatcherInterface $dispatcher) {
|
|||
Event\BuildConsoleCommands::class,
|
||||
function (Event\BuildConsoleCommands $event) use ($dispatcher) {
|
||||
$console = $event->getConsole();
|
||||
$di = $console->getContainer();
|
||||
$di = $event->getContainer();
|
||||
|
||||
/** @var Environment $environment */
|
||||
$environment = $di->get(Environment::class);
|
||||
|
||||
$console->command('cache:clear', Command\ClearCacheCommand::class)
|
||||
->setDescription('Clear all application caches.');
|
||||
$event->addAliases([
|
||||
'cache:clear' => Command\ClearCacheCommand::class,
|
||||
]);
|
||||
|
||||
// Doctrine ORM/DBAL
|
||||
Doctrine\ORM\Tools\Console\ConsoleRunner::addCommands($console);
|
||||
|
@ -26,6 +24,9 @@ return function (CallableEventDispatcherInterface $dispatcher) {
|
|||
/** @var Doctrine\ORM\EntityManagerInterface $em */
|
||||
$em = $di->get(Doctrine\ORM\EntityManagerInterface::class);
|
||||
|
||||
/** @var Environment $environment */
|
||||
$environment = $di->get(Environment::class);
|
||||
|
||||
$helper_set = $console->getHelperSet();
|
||||
$doctrine_helpers = Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($em);
|
||||
$helper_set->set($doctrine_helpers->get('db'), 'db');
|
||||
|
@ -59,7 +60,7 @@ return function (CallableEventDispatcherInterface $dispatcher) {
|
|||
);
|
||||
Doctrine\Migrations\Tools\Console\ConsoleRunner::addCommands($console, $migrateFactory);
|
||||
|
||||
call_user_func(include(__DIR__ . '/cli.php'), $console);
|
||||
call_user_func(include(__DIR__ . '/cli.php'), $event);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -232,15 +232,20 @@ return [
|
|||
) {
|
||||
$console = new App\Console\Application(
|
||||
$environment->getAppName() . ' Command Line Tools (' . $environment->getAppEnvironment() . ')',
|
||||
$version->getVersion(),
|
||||
$di
|
||||
$version->getVersion()
|
||||
);
|
||||
$console->setDispatcher($dispatcher);
|
||||
|
||||
// Trigger an event for the core app and all plugins to build their CLI commands.
|
||||
$event = new App\Event\BuildConsoleCommands($console);
|
||||
$event = new Event\BuildConsoleCommands($console, $di);
|
||||
$dispatcher->dispatch($event);
|
||||
|
||||
$commandLoader = new Symfony\Component\Console\CommandLoader\ContainerCommandLoader(
|
||||
$di,
|
||||
$event->getAliases()
|
||||
);
|
||||
$console->setCommandLoader($commandLoader);
|
||||
|
||||
return $console;
|
||||
},
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use RuntimeException;
|
|||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\StreamOutput;
|
||||
|
||||
class Application extends \Silly\Edition\PhpDi\Application
|
||||
class Application extends \Symfony\Component\Console\Application
|
||||
{
|
||||
/**
|
||||
* Run a one-off command from elsewhere in the application, and pass through the results.
|
||||
|
|
|
@ -9,22 +9,45 @@ use App\Console\Command\Traits;
|
|||
use App\Entity;
|
||||
use App\Utilities;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
use const PATHINFO_EXTENSION;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:backup',
|
||||
description: 'Back up the AzuraCast database and statistics (and optionally media).',
|
||||
)]
|
||||
class BackupCommand extends CommandAbstract
|
||||
{
|
||||
use Traits\PassThruProcess;
|
||||
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
Entity\Repository\StorageLocationRepository $storageLocationRepo,
|
||||
?string $path = '',
|
||||
bool $excludeMedia = false,
|
||||
?int $storageLocationId = null
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
protected Entity\Repository\StorageLocationRepository $storageLocationRepo,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('path', InputArgument::REQUIRED)
|
||||
->addOption('storage-location-id', null, InputOption::VALUE_OPTIONAL, '', '')
|
||||
->addOption('exclude-media', null, InputOption::VALUE_NONE);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$path = $input->getArgument('path');
|
||||
$excludeMedia = (bool)$input->getOption('exclude-media');
|
||||
$storageLocationId = $input->getOption('storage-location-id');
|
||||
|
||||
$start_time = microtime(true);
|
||||
|
||||
if (empty($path)) {
|
||||
|
@ -44,7 +67,7 @@ class BackupCommand extends CommandAbstract
|
|||
return 1;
|
||||
}
|
||||
|
||||
$storageLocation = $storageLocationRepo->findByType(
|
||||
$storageLocation = $this->storageLocationRepo->findByType(
|
||||
Entity\StorageLocation::TYPE_BACKUP,
|
||||
$storageLocationId
|
||||
);
|
||||
|
@ -81,7 +104,7 @@ class BackupCommand extends CommandAbstract
|
|||
|
||||
$path_db_dump = $tmp_dir_mariadb . '/db.sql';
|
||||
|
||||
$conn = $em->getConnection();
|
||||
$conn = $this->em->getConnection();
|
||||
$connParams = $conn->getParams();
|
||||
|
||||
// phpcs:disable Generic.Files.LineLength
|
||||
|
@ -104,7 +127,7 @@ class BackupCommand extends CommandAbstract
|
|||
|
||||
// Include station media if specified.
|
||||
if ($includeMedia) {
|
||||
$stations = $em->createQuery(
|
||||
$stations = $this->em->createQuery(
|
||||
<<<'DQL'
|
||||
SELECT s FROM App\Entity\Station s
|
||||
DQL
|
||||
|
|
|
@ -8,21 +8,42 @@ use App\Console\Command\CommandAbstract;
|
|||
use App\Console\Command\Traits;
|
||||
use App\Utilities;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
use const PATHINFO_EXTENSION;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:restore',
|
||||
description: 'Restore a backup previously generated by AzuraCast.',
|
||||
)]
|
||||
class RestoreCommand extends CommandAbstract
|
||||
{
|
||||
use Traits\PassThruProcess;
|
||||
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
OutputInterface $output,
|
||||
EntityManagerInterface $em,
|
||||
string $path
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('path', InputArgument::REQUIRED)
|
||||
->addOption('restore', null, InputOption::VALUE_NONE)
|
||||
->addOption('release', null, InputOption::VALUE_NONE);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$path = $input->getArgument('path');
|
||||
|
||||
$start_time = microtime(true);
|
||||
|
||||
$io->title('AzuraCast Restore');
|
||||
|
@ -96,7 +117,7 @@ class RestoreCommand extends CommandAbstract
|
|||
return 1;
|
||||
}
|
||||
|
||||
$conn = $em->getConnection();
|
||||
$conn = $this->em->getConnection();
|
||||
$connParams = $conn->getParams();
|
||||
|
||||
// Drop all preloaded tables prior to running a DB dump backup.
|
||||
|
|
|
@ -7,28 +7,41 @@ namespace App\Console\Command;
|
|||
use App\Entity\Repository\SettingsRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'cache:clear',
|
||||
description: 'Clear all application caches.',
|
||||
)]
|
||||
class ClearCacheCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
AdapterInterface $cache,
|
||||
EntityManagerInterface $em,
|
||||
SettingsRepository $settingsRepo,
|
||||
): int {
|
||||
public function __construct(
|
||||
protected AdapterInterface $cache,
|
||||
protected EntityManagerInterface $em,
|
||||
protected SettingsRepository $settingsRepo,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
// Flush all Redis entries.
|
||||
$cache->clear();
|
||||
$this->cache->clear();
|
||||
|
||||
// Clear "Now Playing" cache on all station rows.
|
||||
$em->createQuery(
|
||||
$this->em->createQuery(
|
||||
<<<'DQL'
|
||||
UPDATE App\Entity\Station s SET s.nowplaying=null
|
||||
DQL
|
||||
)->execute();
|
||||
|
||||
// Clear cached system settings.
|
||||
$settings = $settingsRepo->readSettings();
|
||||
$settings = $this->settingsRepo->readSettings();
|
||||
$settings->setNowplaying(null);
|
||||
$settings->updateUpdateLastRun();
|
||||
$settings->setUpdateResults(null);
|
||||
|
@ -37,7 +50,7 @@ class ClearCacheCommand extends CommandAbstract
|
|||
$settings->setExternalIp(null);
|
||||
}
|
||||
|
||||
$settingsRepo->writeSettings($settings);
|
||||
$this->settingsRepo->writeSettings($settings);
|
||||
|
||||
$io->success('Local cache flushed.');
|
||||
return 0;
|
||||
|
|
|
@ -4,27 +4,18 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Console\Command;
|
||||
|
||||
use App\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
abstract class CommandAbstract
|
||||
abstract class CommandAbstract extends Command
|
||||
{
|
||||
protected Application $application;
|
||||
|
||||
public function __construct(Application $application)
|
||||
{
|
||||
$this->application = $application;
|
||||
}
|
||||
|
||||
public function getApplication(): Application
|
||||
{
|
||||
return $this->application;
|
||||
}
|
||||
|
||||
protected function runCommand(OutputInterface $output, string $command_name, array $command_args = []): void
|
||||
{
|
||||
$command = $this->getApplication()->find($command_name);
|
||||
$command = $this->getApplication()?->find($command_name);
|
||||
if (null === $command) {
|
||||
return;
|
||||
}
|
||||
|
||||
$input = new ArrayInput(['command' => $command_name] + $command_args);
|
||||
$input->setInteractive(false);
|
||||
|
|
|
@ -6,19 +6,34 @@ namespace App\Console\Command\Debug;
|
|||
|
||||
use App\Console\Command\CommandAbstract;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:debug:optimize-tables',
|
||||
description: 'Optimize all tables in the database.',
|
||||
)]
|
||||
class OptimizeTablesCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(SymfonyStyle $io, Connection $db): int
|
||||
public function __construct(
|
||||
protected Connection $db
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->title('Optimizing Database Tables...');
|
||||
|
||||
foreach ($db->fetchAllAssociative('SHOW TABLES') as $tableRow) {
|
||||
foreach ($this->db->fetchAllAssociative('SHOW TABLES') as $tableRow) {
|
||||
$table = reset($tableRow);
|
||||
|
||||
$io->listing([$table]);
|
||||
$db->executeQuery('OPTIMIZE TABLE ' . $db->quoteIdentifier($table));
|
||||
$this->db->executeQuery('OPTIMIZE TABLE ' . $this->db->quoteIdentifier($table));
|
||||
}
|
||||
|
||||
$io->success('All tables optimized.');
|
||||
|
|
|
@ -4,28 +4,35 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Console\Command;
|
||||
|
||||
use App\Console\Application;
|
||||
use App\Environment;
|
||||
use App\Version;
|
||||
use OpenApi\Annotations\OpenApi;
|
||||
use OpenApi\Generator;
|
||||
use OpenApi\Util;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:api:docs',
|
||||
description: 'Trigger regeneration of AzuraCast API documentation.',
|
||||
)]
|
||||
class GenerateApiDocsCommand extends CommandAbstract
|
||||
{
|
||||
public function __construct(
|
||||
Application $application,
|
||||
protected Environment $environment,
|
||||
protected Version $version,
|
||||
protected LoggerInterface $logger
|
||||
) {
|
||||
parent::__construct($application);
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function __invoke(SymfonyStyle $io): int
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$yaml = $this->generate()?->toYaml();
|
||||
$yaml_path = $this->environment->getBaseDirectory() . '/web/static/api/openapi.yml';
|
||||
|
||||
|
|
|
@ -6,24 +6,35 @@ namespace App\Console\Command;
|
|||
|
||||
use App\Entity;
|
||||
use App\Environment;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:setup:initialize',
|
||||
description: 'Ensure key settings are initialized within AzuraCast.',
|
||||
)]
|
||||
class InitializeCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
OutputInterface $output,
|
||||
Environment $environment,
|
||||
Entity\Repository\StorageLocationRepository $storageLocationRepo
|
||||
): int {
|
||||
public function __construct(
|
||||
protected Environment $environment,
|
||||
protected Entity\Repository\StorageLocationRepository $storageLocationRepo,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->title(__('Initialize AzuraCast'));
|
||||
$io->writeln(__('Initializing essential settings...'));
|
||||
|
||||
$io->listing(
|
||||
[
|
||||
__('Environment: %s', ucfirst($environment->getAppEnvironment())),
|
||||
__('Installation Method: %s', $environment->isDocker() ? 'Docker' : 'Ansible'),
|
||||
__('Environment: %s', ucfirst($this->environment->getAppEnvironment())),
|
||||
__('Installation Method: %s', $this->environment->isDocker() ? 'Docker' : 'Ansible'),
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -49,7 +60,7 @@ class InitializeCommand extends CommandAbstract
|
|||
$this->runCommand($output, 'cache:clear');
|
||||
|
||||
// Ensure default storage locations exist.
|
||||
$storageLocationRepo->createDefaultStorageLocations();
|
||||
$this->storageLocationRepo->createDefaultStorageLocations();
|
||||
|
||||
$io->newLine();
|
||||
$io->success(
|
||||
|
|
|
@ -6,29 +6,54 @@ namespace App\Console\Command\Internal;
|
|||
|
||||
use App\Console\Command\CommandAbstract;
|
||||
use App\Entity;
|
||||
use App\Entity\Repository\SettingsRepository;
|
||||
use App\Radio\Adapters;
|
||||
use App\Radio\Backend\Liquidsoap;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:internal:auth',
|
||||
description: 'Authorize a streamer to connect as a source for the radio service.',
|
||||
)]
|
||||
class DjAuthCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
Adapters $adapters,
|
||||
int $stationId,
|
||||
string $djUser = '',
|
||||
string $djPassword = ''
|
||||
): int {
|
||||
$station = $em->getRepository(Entity\Station::class)->find($stationId);
|
||||
public function __construct(
|
||||
protected Adapters $adapters,
|
||||
protected EntityManagerInterface $em,
|
||||
protected SettingsRepository $settingsRepo,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('station-id', InputArgument::REQUIRED)
|
||||
->addOption('dj-user', null, InputOption::VALUE_REQUIRED, '', '')
|
||||
->addOption('dj-password', null, InputOption::VALUE_REQUIRED, '', '');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$stationId = (int)$input->getArgument('station-id');
|
||||
$djUser = $input->getOption('dj-user');
|
||||
$djPassword = $input->getOption('dj-password');
|
||||
|
||||
$station = $this->em->getRepository(Entity\Station::class)->find($stationId);
|
||||
|
||||
if (!($station instanceof Entity\Station) || !$station->getEnableStreamers()) {
|
||||
$io->write('false');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$adapter = $adapters->getBackendAdapter($station);
|
||||
$adapter = $this->adapters->getBackendAdapter($station);
|
||||
|
||||
if ($adapter instanceof Liquidsoap) {
|
||||
$response = $adapter->authenticateStreamer($station, $djUser, $djPassword);
|
||||
|
|
|
@ -9,24 +9,46 @@ use App\Entity;
|
|||
use App\Radio\Adapters;
|
||||
use App\Radio\Backend\Liquidsoap;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:internal:djoff',
|
||||
description: 'Indicate that a DJ has finished streaming to a station.',
|
||||
)]
|
||||
class DjOffCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
Adapters $adapters,
|
||||
int $stationId,
|
||||
string $djUser = ''
|
||||
): int {
|
||||
$station = $em->find(Entity\Station::class, $stationId);
|
||||
public function __construct(
|
||||
protected Adapters $adapters,
|
||||
protected EntityManagerInterface $em
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('station-id', InputArgument::REQUIRED)
|
||||
->addOption('dj-user', null, InputOption::VALUE_REQUIRED, '', '');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$stationId = (int)$input->getArgument('station-id');
|
||||
$djUser = $input->getOption('dj-user');
|
||||
|
||||
$station = $this->em->find(Entity\Station::class, $stationId);
|
||||
|
||||
if (!($station instanceof Entity\Station) || !$station->getEnableStreamers()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$adapter = $adapters->getBackendAdapter($station);
|
||||
$adapter = $this->adapters->getBackendAdapter($station);
|
||||
|
||||
if ($adapter instanceof Liquidsoap) {
|
||||
$io->write($adapter->onDisconnect($station, $djUser));
|
||||
|
|
|
@ -9,24 +9,46 @@ use App\Entity;
|
|||
use App\Radio\Adapters;
|
||||
use App\Radio\Backend\Liquidsoap;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:internal:djon',
|
||||
description: 'Indicate that a DJ has begun streaming to a station.',
|
||||
)]
|
||||
class DjOnCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
Adapters $adapters,
|
||||
int $stationId,
|
||||
string $djUser = ''
|
||||
): int {
|
||||
$station = $em->find(Entity\Station::class, $stationId);
|
||||
public function __construct(
|
||||
protected Adapters $adapters,
|
||||
protected EntityManagerInterface $em
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('station-id', InputArgument::REQUIRED)
|
||||
->addOption('dj-user', null, InputOption::VALUE_REQUIRED, '', '');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$stationId = (int)$input->getArgument('station-id');
|
||||
$djUser = $input->getOption('dj-user');
|
||||
|
||||
$station = $this->em->find(Entity\Station::class, $stationId);
|
||||
|
||||
if (!($station instanceof Entity\Station) || !$station->getEnableStreamers()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$adapter = $adapters->getBackendAdapter($station);
|
||||
$adapter = $this->adapters->getBackendAdapter($station);
|
||||
|
||||
if ($adapter instanceof Liquidsoap) {
|
||||
$io->write($adapter->onConnect($station, $djUser));
|
||||
|
|
|
@ -9,20 +9,44 @@ use App\Entity;
|
|||
use App\Sync\Task\NowPlayingTask;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:internal:feedback',
|
||||
description: 'Send upcoming song feedback from the AutoDJ back to AzuraCast.',
|
||||
)]
|
||||
class FeedbackCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
NowPlayingTask $nowPlaying,
|
||||
int $stationId,
|
||||
string $song = null,
|
||||
string $media = null,
|
||||
string $playlist = null
|
||||
): int {
|
||||
$station = $em->find(Entity\Station::class, $stationId);
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
protected NowPlayingTask $nowPlaying,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('station-id', InputArgument::REQUIRED)
|
||||
->addOption('song', 's', InputOption::VALUE_OPTIONAL, '', '')
|
||||
->addOption('media', 'm', InputOption::VALUE_OPTIONAL, '', '')
|
||||
->addOption('playlist', 'p', InputOption::VALUE_OPTIONAL, '', '');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$stationId = (int)$input->getArgument('station-id');
|
||||
$song = $input->getOption('song');
|
||||
$media = $input->getOption('media');
|
||||
$playlist = $input->getOption('playlist');
|
||||
|
||||
$station = $this->em->find(Entity\Station::class, $stationId);
|
||||
|
||||
if (!($station instanceof Entity\Station)) {
|
||||
$io->write('false');
|
||||
|
@ -30,9 +54,9 @@ class FeedbackCommand extends CommandAbstract
|
|||
}
|
||||
|
||||
try {
|
||||
$nowPlaying->queueStation($station, [
|
||||
'song_id' => $song,
|
||||
'media_id' => $media,
|
||||
$this->nowPlaying->queueStation($station, [
|
||||
'song_id' => $song,
|
||||
'media_id' => $media,
|
||||
'playlist_id' => $playlist,
|
||||
]);
|
||||
|
||||
|
|
|
@ -6,15 +6,28 @@ namespace App\Console\Command\Internal;
|
|||
|
||||
use App\Console\Command\CommandAbstract;
|
||||
use App\Service\AzuraCastCentral;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:internal:ip',
|
||||
description: 'Get the external IP address for this instance.',
|
||||
)]
|
||||
class GetIpCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
AzuraCastCentral $acCentral
|
||||
): int {
|
||||
$io->write($acCentral->getIp() ?? 'Unknown');
|
||||
public function __construct(
|
||||
protected AzuraCastCentral $acCentral,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->write($this->acCentral->getIp() ?? 'Unknown');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,25 +8,47 @@ use App\Console\Command\CommandAbstract;
|
|||
use App\Entity;
|
||||
use App\Radio\AutoDJ;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:internal:nextsong',
|
||||
description: 'Return the next song to the AutoDJ.',
|
||||
)]
|
||||
class NextSongCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
AutoDJ $autoDJ,
|
||||
int $stationId,
|
||||
bool $asAutodj = false
|
||||
): int {
|
||||
$station = $em->find(Entity\Station::class, $stationId);
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
protected AutoDJ $autoDJ,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('station-id', InputArgument::REQUIRED)
|
||||
->addOption('as-autodj', null, InputOption::VALUE_NONE);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$stationId = (int)$input->getArgument('station-id');
|
||||
$asAutodj = (bool)$input->getOption('as-autodj');
|
||||
|
||||
$station = $this->em->find(Entity\Station::class, $stationId);
|
||||
|
||||
if (!($station instanceof Entity\Station)) {
|
||||
$io->write('false');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$io->write($autoDJ->annotateNextSong($station, $asAutodj));
|
||||
$io->write($this->autoDJ->annotateNextSong($station, $asAutodj));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,23 +8,36 @@ use App\Console\Command\CommandAbstract;
|
|||
use App\Entity\SftpUser;
|
||||
use Brick\Math\BigInteger;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
use const JSON_NUMERIC_CHECK;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:internal:sftp-auth',
|
||||
description: 'Attempt SFTP authentication.',
|
||||
)]
|
||||
class SftpAuthCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$username = getenv('SFTPGO_AUTHD_USERNAME') ?: null;
|
||||
$password = getenv('SFTPGO_AUTHD_PASSWORD') ?: null;
|
||||
$pubKey = getenv('SFTPGO_AUTHD_PUBLIC_KEY') ?: null;
|
||||
|
||||
$sftpUser = $em->getRepository(SftpUser::class)->findOneBy(['username' => $username]);
|
||||
$sftpUser = $this->em->getRepository(SftpUser::class)->findOneBy(['username' => $username]);
|
||||
|
||||
if ($sftpUser instanceof SftpUser && $sftpUser->authenticate($password, $pubKey)) {
|
||||
$storageLocation = $sftpUser->getStation()->getMediaStorageLocation();
|
||||
|
@ -35,14 +48,14 @@ class SftpAuthCommand extends CommandAbstract
|
|||
: 0;
|
||||
|
||||
$row = [
|
||||
'status' => 1,
|
||||
'username' => $sftpUser->getUsername(),
|
||||
'status' => 1,
|
||||
'username' => $sftpUser->getUsername(),
|
||||
'expiration_date' => 0,
|
||||
'home_dir' => $storageLocation->getPath(),
|
||||
'uid' => 0,
|
||||
'gid' => 0,
|
||||
'quota_size' => $quota,
|
||||
'permissions' => [
|
||||
'home_dir' => $storageLocation->getPath(),
|
||||
'uid' => 0,
|
||||
'gid' => 0,
|
||||
'quota_size' => $quota,
|
||||
'permissions' => [
|
||||
'/' => ['*'],
|
||||
],
|
||||
];
|
||||
|
|
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Console\Command\Internal;
|
||||
|
||||
use App\Console\Application;
|
||||
use App\Console\Command\CommandAbstract;
|
||||
use App\Entity;
|
||||
use App\Media\BatchUtilities;
|
||||
|
@ -12,42 +11,57 @@ use App\Message;
|
|||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use League\Flysystem\PathPrefixer;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Messenger\MessageBus;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:internal:sftp-event',
|
||||
description: 'Send upcoming song feedback from the AutoDJ back to AzuraCast.',
|
||||
)]
|
||||
class SftpEventCommand extends CommandAbstract
|
||||
{
|
||||
public function __construct(
|
||||
Application $application,
|
||||
protected EntityManagerInterface $em,
|
||||
protected MessageBus $messageBus,
|
||||
protected LoggerInterface $logger,
|
||||
protected BatchUtilities $batchUtilities
|
||||
protected BatchUtilities $batchUtilities,
|
||||
) {
|
||||
parent::__construct($application);
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
string $action = null,
|
||||
string $username = null,
|
||||
string $path = null,
|
||||
string $targetPath = null,
|
||||
string $sshCmd = null
|
||||
): int {
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('action', InputArgument::REQUIRED)
|
||||
->addArgument('username', InputArgument::REQUIRED)
|
||||
->addArgument('path', InputArgument::REQUIRED)
|
||||
->addArgument('target-path', InputArgument::OPTIONAL)
|
||||
->addArgument('ssh-cmd', InputArgument::OPTIONAL);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$action = $input->getArgument('action');
|
||||
$username = $input->getArgument('username');
|
||||
$path = $input->getArgument('path');
|
||||
$targetPath = $input->getArgument('target-path');
|
||||
$sshCmd = $input->getArgument('ssh-cmd');
|
||||
|
||||
$this->logger->notice(
|
||||
'SFTP file event triggered',
|
||||
[
|
||||
'action' => $action,
|
||||
'username' => $username,
|
||||
'path' => $path,
|
||||
'action' => $action,
|
||||
'username' => $username,
|
||||
'path' => $path,
|
||||
'targetPath' => $targetPath,
|
||||
'sshCmd' => $sshCmd,
|
||||
'sshCmd' => $sshCmd,
|
||||
]
|
||||
);
|
||||
|
||||
// Determine which station the username belongs to.
|
||||
$sftpUser = $em->getRepository(Entity\SftpUser::class)->findOneBy(
|
||||
$sftpUser = $this->em->getRepository(Entity\SftpUser::class)->findOneBy(
|
||||
[
|
||||
'username' => $username,
|
||||
]
|
||||
|
@ -101,7 +115,7 @@ class SftpEventCommand extends CommandAbstract
|
|||
'Processing new SFTP upload.',
|
||||
[
|
||||
'storageLocation' => (string)$storageLocation,
|
||||
'path' => $relativePath,
|
||||
'path' => $relativePath,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -125,7 +139,7 @@ class SftpEventCommand extends CommandAbstract
|
|||
'Processing SFTP file/folder deletion.',
|
||||
[
|
||||
'storageLocation' => (string)$storageLocation,
|
||||
'path' => $relativePath,
|
||||
'path' => $relativePath,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -182,8 +196,8 @@ class SftpEventCommand extends CommandAbstract
|
|||
'Processing SFTP file/folder rename.',
|
||||
[
|
||||
'storageLocation' => (string)$storageLocation,
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -9,26 +9,37 @@ use App\Environment;
|
|||
use Gettext\Translations;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use RecursiveRegexIterator;
|
||||
use RegexIterator;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'locale:generate',
|
||||
description: 'Generate the translation locale file.',
|
||||
)]
|
||||
class GenerateCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
Environment $environment
|
||||
): int {
|
||||
public function __construct(
|
||||
protected Environment $environment
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->title('Generate Locales');
|
||||
|
||||
$exportDir = $environment->getBaseDirectory() . '/resources/locale';
|
||||
$exportDir = $this->environment->getBaseDirectory() . '/resources/locale';
|
||||
|
||||
$dest_file = $exportDir . '/default.pot';
|
||||
|
||||
$translations = new Translations();
|
||||
|
||||
// Find all JS/Vue file translations.
|
||||
$directory = new RecursiveDirectoryIterator($environment->getBaseDirectory() . '/frontend/vue');
|
||||
$directory = new RecursiveDirectoryIterator($this->environment->getBaseDirectory() . '/frontend/vue');
|
||||
$iterator = new RecursiveIteratorIterator($directory);
|
||||
|
||||
$vueRegex = new RegexIterator($iterator, '/^.+\.(vue)$/i', RegexIterator::GET_MATCH);
|
||||
|
@ -43,9 +54,9 @@ class GenerateCommand extends CommandAbstract
|
|||
|
||||
// Find all PHP/PHTML files in the application's code.
|
||||
$translatable_folders = [
|
||||
$environment->getBaseDirectory() . '/src',
|
||||
$environment->getBaseDirectory() . '/config',
|
||||
$environment->getViewsDirectory(),
|
||||
$this->environment->getBaseDirectory() . '/src',
|
||||
$this->environment->getBaseDirectory() . '/config',
|
||||
$this->environment->getViewsDirectory(),
|
||||
];
|
||||
|
||||
foreach ($translatable_folders as $folder) {
|
||||
|
|
|
@ -9,18 +9,30 @@ use App\Environment;
|
|||
use App\Locale;
|
||||
use Gettext\Translation;
|
||||
use Gettext\Translations;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'locale:import',
|
||||
description: 'Convert translated locale files into PHP arrays.',
|
||||
)]
|
||||
class ImportCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
Environment $environment
|
||||
): int {
|
||||
public function __construct(
|
||||
protected Environment $environment
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->title('Import Locales');
|
||||
|
||||
$locales = Locale::SUPPORTED_LOCALES;
|
||||
$locale_base = $environment->getBaseDirectory() . '/resources/locale';
|
||||
$locale_base = $this->environment->getBaseDirectory() . '/resources/locale';
|
||||
|
||||
$jsTranslations = [];
|
||||
|
||||
|
|
|
@ -7,20 +7,39 @@ namespace App\Console\Command\MessageQueue;
|
|||
use App\Console\Command\CommandAbstract;
|
||||
use App\MessageQueue\AbstractQueueManager;
|
||||
use App\MessageQueue\QueueManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'queue:clear',
|
||||
description: 'Clear the contents of the message queue.',
|
||||
)]
|
||||
class ClearCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
QueueManagerInterface $queueManager,
|
||||
?string $queue = null
|
||||
): int {
|
||||
public function __construct(
|
||||
protected QueueManagerInterface $queueManager,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('queue', InputArgument::OPTIONAL);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$queue = $input->getArgument('queue');
|
||||
$allQueues = AbstractQueueManager::getAllQueues();
|
||||
|
||||
if (!empty($queue)) {
|
||||
if (in_array($queue, $allQueues, true)) {
|
||||
$queueManager->clearQueue($queue);
|
||||
$this->queueManager->clearQueue($queue);
|
||||
|
||||
$io->success(sprintf('Message queue "%s" cleared.', $queue));
|
||||
} else {
|
||||
|
@ -29,7 +48,7 @@ class ClearCommand extends CommandAbstract
|
|||
}
|
||||
} else {
|
||||
foreach ($allQueues as $queueName) {
|
||||
$queueManager->clearQueue($queueName);
|
||||
$this->queueManager->clearQueue($queueName);
|
||||
}
|
||||
|
||||
$io->success('All message queues cleared.');
|
||||
|
|
|
@ -12,23 +12,44 @@ use App\MessageQueue\QueueManagerInterface;
|
|||
use App\MessageQueue\ResetArrayCacheMiddleware;
|
||||
use Azura\SlimCallableEventDispatcher\CallableEventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Messenger\EventListener\StopWorkerOnTimeLimitListener;
|
||||
use Symfony\Component\Messenger\MessageBus;
|
||||
use Symfony\Component\Messenger\Worker;
|
||||
use Throwable;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'queue:process',
|
||||
description: 'Process the message queue.',
|
||||
)]
|
||||
class ProcessCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
MessageBus $messageBus,
|
||||
CallableEventDispatcherInterface $eventDispatcher,
|
||||
QueueManagerInterface $queueManager,
|
||||
LoggerInterface $logger,
|
||||
Environment $environment,
|
||||
?int $runtime = 0,
|
||||
?string $workerName = null
|
||||
): int {
|
||||
$logger->notice(
|
||||
public function __construct(
|
||||
protected MessageBus $messageBus,
|
||||
protected CallableEventDispatcherInterface $eventDispatcher,
|
||||
protected QueueManagerInterface $queueManager,
|
||||
protected LoggerInterface $logger,
|
||||
protected Environment $environment,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('runtime', InputArgument::OPTIONAL)
|
||||
->addOption('worker-name', null, InputOption::VALUE_OPTIONAL);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$runtime = (int)$input->getArgument('runtime');
|
||||
$workerName = $input->getOption('worker-name');
|
||||
|
||||
$this->logger->notice(
|
||||
'Starting new Message Queue worker process.',
|
||||
[
|
||||
'runtime' => $runtime,
|
||||
|
@ -37,38 +58,36 @@ class ProcessCommand extends CommandAbstract
|
|||
);
|
||||
|
||||
if (null !== $workerName) {
|
||||
$queueManager->setWorkerName($workerName);
|
||||
$this->queueManager->setWorkerName($workerName);
|
||||
}
|
||||
|
||||
$receivers = $queueManager->getTransports();
|
||||
$receivers = $this->queueManager->getTransports();
|
||||
|
||||
$eventDispatcher->addServiceSubscriber(ClearEntityManagerSubscriber::class);
|
||||
$eventDispatcher->addServiceSubscriber(LogWorkerExceptionSubscriber::class);
|
||||
$eventDispatcher->addServiceSubscriber(ResetArrayCacheMiddleware::class);
|
||||
$this->eventDispatcher->addServiceSubscriber(ClearEntityManagerSubscriber::class);
|
||||
$this->eventDispatcher->addServiceSubscriber(LogWorkerExceptionSubscriber::class);
|
||||
$this->eventDispatcher->addServiceSubscriber(ResetArrayCacheMiddleware::class);
|
||||
|
||||
if ($runtime <= 0) {
|
||||
$runtime = $environment->isProduction()
|
||||
$runtime = $this->environment->isProduction()
|
||||
? 300
|
||||
: 30;
|
||||
}
|
||||
|
||||
$eventDispatcher->addSubscriber(new StopWorkerOnTimeLimitListener($runtime, $logger));
|
||||
$this->eventDispatcher->addSubscriber(new StopWorkerOnTimeLimitListener($runtime, $this->logger));
|
||||
|
||||
try {
|
||||
$worker = new Worker($receivers, $messageBus, $eventDispatcher, $logger);
|
||||
$worker = new Worker($receivers, $this->messageBus, $this->eventDispatcher, $this->logger);
|
||||
$worker->run();
|
||||
} catch (Throwable $e) {
|
||||
$logger->error(
|
||||
$this->logger->error(
|
||||
sprintf('Message queue error: %s', $e->getMessage()),
|
||||
[
|
||||
'workerName' => $workerName,
|
||||
'exception' => $e,
|
||||
'exception' => $e,
|
||||
]
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,34 +5,47 @@ declare(strict_types=1);
|
|||
namespace App\Console\Command;
|
||||
|
||||
use App\Environment;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:config:migrate',
|
||||
description: 'Migrate existing configuration to new INI format if any exists.',
|
||||
)]
|
||||
class MigrateConfigCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
Environment $environment
|
||||
): int {
|
||||
public function __construct(
|
||||
protected Environment $environment,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$envSettings = [];
|
||||
|
||||
$iniPath = $environment->getBaseDirectory() . '/env.ini';
|
||||
$iniPath = $this->environment->getBaseDirectory() . '/env.ini';
|
||||
if (is_file($iniPath)) {
|
||||
$envSettings = (array)parse_ini_file($iniPath);
|
||||
}
|
||||
|
||||
// Migrate from existing legacy config files.
|
||||
$legacyIniPath = $environment->getBaseDirectory() . '/app/env.ini';
|
||||
$legacyIniPath = $this->environment->getBaseDirectory() . '/app/env.ini';
|
||||
if (is_file($legacyIniPath)) {
|
||||
$iniSettings = parse_ini_file($legacyIniPath);
|
||||
$envSettings = array_merge($envSettings, (array)$iniSettings);
|
||||
}
|
||||
|
||||
$legacyAppEnvFile = $environment->getBaseDirectory() . '/app/.env';
|
||||
$legacyAppEnvFile = $this->environment->getBaseDirectory() . '/app/.env';
|
||||
if (is_file($legacyAppEnvFile)) {
|
||||
$envSettings[Environment::APP_ENV] ??= file_get_contents($legacyAppEnvFile);
|
||||
}
|
||||
|
||||
$legacyDbConfFile = $environment->getBaseDirectory() . '/app/config/db.conf.php';
|
||||
$legacyDbConfFile = $this->environment->getBaseDirectory() . '/app/config/db.conf.php';
|
||||
if (is_file($legacyDbConfFile)) {
|
||||
$dbConf = include($legacyDbConfFile);
|
||||
|
||||
|
@ -45,11 +58,11 @@ class MigrateConfigCommand extends CommandAbstract
|
|||
// Migrate from older environment variable names to new ones.
|
||||
$settingsToMigrate = [
|
||||
'application_env' => Environment::APP_ENV,
|
||||
'db_host' => Environment::DB_HOST,
|
||||
'db_port' => Environment::DB_PORT,
|
||||
'db_name' => Environment::DB_NAME,
|
||||
'db_username' => Environment::DB_USER,
|
||||
'db_password' => Environment::DB_PASSWORD,
|
||||
'db_host' => Environment::DB_HOST,
|
||||
'db_port' => Environment::DB_PORT,
|
||||
'db_name' => Environment::DB_NAME,
|
||||
'db_username' => Environment::DB_USER,
|
||||
'db_password' => Environment::DB_PASSWORD,
|
||||
];
|
||||
|
||||
foreach ($settingsToMigrate as $oldSetting => $newSetting) {
|
||||
|
|
|
@ -5,19 +5,38 @@ declare(strict_types=1);
|
|||
namespace App\Console\Command;
|
||||
|
||||
use App\Entity;
|
||||
use App\Entity\Repository\StationRepository;
|
||||
use App\Entity\Station;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:media:reprocess',
|
||||
description: 'Manually reload all media metadata from file.',
|
||||
)]
|
||||
class ReprocessMediaCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
StationRepository $stationRepo,
|
||||
?string $stationName = null
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
protected Entity\Repository\StationRepository $stationRepo,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('station-name', InputArgument::OPTIONAL);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$stationName = $input->getArgument('station-name');
|
||||
|
||||
$io->title('Manually Reprocess Media');
|
||||
|
||||
if (empty($stationName)) {
|
||||
|
@ -25,7 +44,7 @@ class ReprocessMediaCommand extends CommandAbstract
|
|||
|
||||
$storageLocation = null;
|
||||
} else {
|
||||
$station = $stationRepo->findByIdentifier($stationName);
|
||||
$station = $this->stationRepo->findByIdentifier($stationName);
|
||||
if (!$station instanceof Station) {
|
||||
$io->error('Station not found.');
|
||||
return 1;
|
||||
|
@ -36,7 +55,7 @@ class ReprocessMediaCommand extends CommandAbstract
|
|||
$io->writeln(sprintf('Reprocessing media for station: %s', $station->getName()));
|
||||
}
|
||||
|
||||
$reprocessMediaQueue = $em->createQueryBuilder()
|
||||
$reprocessMediaQueue = $this->em->createQueryBuilder()
|
||||
->update(Entity\StationMedia::class, 'sm')
|
||||
->set('sm.mtime', 'NULL');
|
||||
|
||||
|
|
|
@ -4,25 +4,44 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Console\Command;
|
||||
|
||||
use App\Entity\Repository\StationRepository;
|
||||
use App\Entity\Station;
|
||||
use App\Entity;
|
||||
use App\Radio\Configuration;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:radio:restart',
|
||||
description: 'Restart all radio stations, or a single one if specified.',
|
||||
)]
|
||||
class RestartRadioCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
StationRepository $stationRepo,
|
||||
Configuration $configuration,
|
||||
?string $stationName = null
|
||||
): int {
|
||||
if (!empty($stationName)) {
|
||||
$station = $stationRepo->findByIdentifier($stationName);
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
protected Entity\Repository\StationRepository $stationRepo,
|
||||
protected Configuration $configuration,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
if (!$station instanceof Station) {
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('station-name', InputArgument::OPTIONAL);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$stationName = $input->getArgument('station-name');
|
||||
|
||||
if (!empty($stationName)) {
|
||||
$station = $this->stationRepo->findByIdentifier($stationName);
|
||||
|
||||
if (!$station instanceof Entity\Station) {
|
||||
$io->error('Station not found.');
|
||||
return 1;
|
||||
}
|
||||
|
@ -31,14 +50,14 @@ class RestartRadioCommand extends CommandAbstract
|
|||
} else {
|
||||
$io->section('Restarting all radio stations...');
|
||||
|
||||
/** @var Station[] $stations */
|
||||
$stations = $stationRepo->fetchAll();
|
||||
/** @var Entity\Station[] $stations */
|
||||
$stations = $this->stationRepo->fetchAll();
|
||||
}
|
||||
|
||||
$io->progressStart(count($stations));
|
||||
|
||||
foreach ($stations as $station) {
|
||||
$configuration->writeConfiguration($station, true);
|
||||
$this->configuration->writeConfiguration($station, true);
|
||||
$io->progressAdvance();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,14 +7,26 @@ namespace App\Console\Command\Settings;
|
|||
use App\Console\Command\CommandAbstract;
|
||||
use App\Entity;
|
||||
use App\Utilities;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:settings:list',
|
||||
description: 'List all settings in the AzuraCast settings database.',
|
||||
)]
|
||||
class ListCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
Entity\Repository\SettingsRepository $settingsTableRepo
|
||||
): int {
|
||||
public function __construct(
|
||||
protected Entity\Repository\SettingsRepository $settingsTableRepo,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->title(__('AzuraCast Settings'));
|
||||
|
||||
$headers = [
|
||||
|
@ -23,8 +35,8 @@ class ListCommand extends CommandAbstract
|
|||
];
|
||||
$rows = [];
|
||||
|
||||
$settings = $settingsTableRepo->readSettings();
|
||||
foreach ($settingsTableRepo->toArray($settings) as $setting_key => $setting_value) {
|
||||
$settings = $this->settingsTableRepo->readSettings();
|
||||
foreach ($this->settingsTableRepo->toArray($settings) as $setting_key => $setting_value) {
|
||||
$value = print_r($setting_value, true);
|
||||
$value = Utilities\Strings::truncateText($value, 600);
|
||||
|
||||
|
|
|
@ -6,20 +6,41 @@ namespace App\Console\Command\Settings;
|
|||
|
||||
use App\Console\Command\CommandAbstract;
|
||||
use App\Entity;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:settings:set',
|
||||
description: 'Set the value of a setting in the AzuraCast settings database.',
|
||||
)]
|
||||
class SetCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
Entity\Repository\SettingsRepository $settingsTableRepo,
|
||||
string $settingKey,
|
||||
string $settingValue
|
||||
): int {
|
||||
public function __construct(
|
||||
protected Entity\Repository\SettingsRepository $settingsTableRepo,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('setting-key', InputArgument::REQUIRED)
|
||||
->addArgument('setting-value', InputArgument::REQUIRED);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$settingKey = $input->getArgument('setting-key');
|
||||
$settingValue = $input->getArgument('setting-value');
|
||||
|
||||
$io->title('AzuraCast Settings');
|
||||
|
||||
if (strtolower($settingValue) === 'null') {
|
||||
$settingsTableRepo->writeSettings([$settingKey => null]);
|
||||
$this->settingsTableRepo->writeSettings([$settingKey => null]);
|
||||
|
||||
$io->success(sprintf('Setting "%s" removed.', $settingKey));
|
||||
return 0;
|
||||
|
@ -29,7 +50,7 @@ class SetCommand extends CommandAbstract
|
|||
$settingValue = json_decode($settingValue, true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
$settingsTableRepo->writeSettings([$settingKey => $settingValue]);
|
||||
$this->settingsTableRepo->writeSettings([$settingKey => $settingValue]);
|
||||
|
||||
$io->success(sprintf('Setting "%s" updated.', $settingKey));
|
||||
|
||||
|
|
|
@ -7,30 +7,46 @@ namespace App\Console\Command;
|
|||
use App\Entity;
|
||||
use App\Environment;
|
||||
use App\Service\AzuraCastCentral;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:setup',
|
||||
description: 'Run all general AzuraCast setup steps.',
|
||||
)]
|
||||
class SetupCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
OutputInterface $output,
|
||||
Environment $environment,
|
||||
ContainerInterface $di,
|
||||
Entity\Repository\SettingsRepository $settingsRepo,
|
||||
Entity\Repository\StationRepository $stationRepo,
|
||||
Entity\Repository\StorageLocationRepository $storageLocationRepo,
|
||||
AzuraCastCentral $acCentral,
|
||||
bool $update = false,
|
||||
bool $loadFixtures = false
|
||||
): int {
|
||||
public function __construct(
|
||||
protected Environment $environment,
|
||||
protected Entity\Repository\SettingsRepository $settingsRepo,
|
||||
protected AzuraCastCentral $acCentral,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('update', null, InputOption::VALUE_NONE)
|
||||
->addOption('load-fixtures', null, InputOption::VALUE_NONE)
|
||||
->addOption('release', null, InputOption::VALUE_NONE);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$update = (bool)$input->getOption('update');
|
||||
$loadFixtures = (bool)$input->getOption('load-fixtures');
|
||||
|
||||
$io->title(__('AzuraCast Setup'));
|
||||
$io->writeln(__('Welcome to AzuraCast. Please wait while some key dependencies of AzuraCast are set up...'));
|
||||
|
||||
$this->runCommand($output, 'azuracast:setup:initialize');
|
||||
|
||||
if ($loadFixtures || (!$environment->isProduction() && !$update)) {
|
||||
if ($loadFixtures || (!$this->environment->isProduction() && !$update)) {
|
||||
$io->newLine();
|
||||
$io->section(__('Installing Data Fixtures'));
|
||||
|
||||
|
@ -43,9 +59,9 @@ class SetupCommand extends CommandAbstract
|
|||
$this->runCommand($output, 'azuracast:radio:restart');
|
||||
|
||||
// Update system setting logging when updates were last run.
|
||||
$settings = $settingsRepo->readSettings();
|
||||
$settings = $this->settingsRepo->readSettings();
|
||||
$settings->updateUpdateLastRun();
|
||||
$settingsRepo->writeSettings($settings);
|
||||
$this->settingsRepo->writeSettings($settings);
|
||||
|
||||
if ($update) {
|
||||
$io->success(
|
||||
|
@ -54,7 +70,7 @@ class SetupCommand extends CommandAbstract
|
|||
]
|
||||
);
|
||||
} else {
|
||||
$public_ip = $acCentral->getIp(false);
|
||||
$public_ip = $this->acCentral->getIp(false);
|
||||
|
||||
/** @noinspection HttpUrlsUsage */
|
||||
$io->success(
|
||||
|
|
|
@ -12,20 +12,32 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||
use Psr\Container\ContainerInterface;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:setup:fixtures',
|
||||
description: 'Install fixtures for demo / local development.',
|
||||
)]
|
||||
class SetupFixturesCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
ContainerInterface $di,
|
||||
Environment $environment
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
protected ContainerInterface $di,
|
||||
protected Environment $environment,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$loader = new Loader();
|
||||
|
||||
// Dependency-inject the fixtures and load them.
|
||||
$fixturesDir = $environment->getBaseDirectory() . '/src/Entity/Fixture';
|
||||
$fixturesDir = $this->environment->getBaseDirectory() . '/src/Entity/Fixture';
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($fixturesDir),
|
||||
|
@ -39,13 +51,13 @@ class SetupFixturesCommand extends CommandAbstract
|
|||
}
|
||||
|
||||
$className = 'App\\Entity\\Fixture\\' . $fileName;
|
||||
$fixture = $di->get($className);
|
||||
$fixture = $this->di->get($className);
|
||||
|
||||
$loader->addFixture($fixture);
|
||||
}
|
||||
|
||||
$purger = new ORMPurger($em);
|
||||
$executor = new ORMExecutor($em, $purger);
|
||||
$purger = new ORMPurger($this->em);
|
||||
$executor = new ORMExecutor($this->em, $purger);
|
||||
$executor->execute($loader->getFixtures());
|
||||
|
||||
$io->success(__('Fixtures loaded.'));
|
||||
|
|
|
@ -6,17 +6,36 @@ namespace App\Console\Command;
|
|||
|
||||
use App;
|
||||
use App\Sync\Runner;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'sync:run',
|
||||
description: 'Run one or more scheduled synchronization tasks.',
|
||||
)]
|
||||
class SyncCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
Runner $sync,
|
||||
?string $task = null,
|
||||
bool $force = false
|
||||
): int {
|
||||
$task ??= App\Event\GetSyncTasks::SYNC_NOWPLAYING;
|
||||
public function __construct(
|
||||
protected Runner $sync,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
$sync->runSyncTask($task, $force);
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('task', InputArgument::OPTIONAL)
|
||||
->addOption('force', null, InputOption::VALUE_NONE);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$task = $input->getArgument('task') ?? App\Event\GetSyncTasks::SYNC_NOWPLAYING;
|
||||
$force = (bool)$input->getOption('force');
|
||||
|
||||
$this->sync->runSyncTask($task, $force);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,17 +7,29 @@ namespace App\Console\Command\Users;
|
|||
use App\Console\Command\CommandAbstract;
|
||||
use App\Entity;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:account:list',
|
||||
description: 'List all accounts in the system.',
|
||||
)]
|
||||
class ListCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->title('AzuraCast User Accounts');
|
||||
|
||||
$usersRaw = $em->getRepository(Entity\User::class)
|
||||
$usersRaw = $this->em->getRepository(Entity\User::class)
|
||||
->findAll();
|
||||
|
||||
$headers = [
|
||||
|
@ -44,7 +56,6 @@ class ListCommand extends CommandAbstract
|
|||
];
|
||||
}
|
||||
|
||||
|
||||
$io->table($headers, $users);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,30 +8,49 @@ use App\Console\Command\CommandAbstract;
|
|||
use App\Entity;
|
||||
use App\Http\RouterInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:account:login-token',
|
||||
description: 'Create a unique login recovery URL for the specified account.',
|
||||
)]
|
||||
class LoginTokenCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
Entity\Repository\UserLoginTokenRepository $loginTokenRepo,
|
||||
RouterInterface $router,
|
||||
string $email
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
protected Entity\Repository\UserLoginTokenRepository $loginTokenRepo,
|
||||
protected RouterInterface $router,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('email', InputArgument::REQUIRED);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$email = $input->getArgument('email');
|
||||
|
||||
$io->title('Generate Account Login Recovery URL');
|
||||
|
||||
$user = $em->getRepository(Entity\User::class)
|
||||
$user = $this->em->getRepository(Entity\User::class)
|
||||
->findOneBy(['email' => $email]);
|
||||
|
||||
if ($user instanceof Entity\User) {
|
||||
$loginToken = $loginTokenRepo->createToken($user);
|
||||
$loginToken = $this->loginTokenRepo->createToken($user);
|
||||
|
||||
$url = $router->named(
|
||||
'account:recover',
|
||||
['token' => $loginToken],
|
||||
[],
|
||||
true
|
||||
$url = $this->router->named(
|
||||
route_name: 'account:recover',
|
||||
route_params: ['token' => $loginToken],
|
||||
absolute: true
|
||||
);
|
||||
|
||||
$io->text([
|
||||
|
|
|
@ -8,18 +8,38 @@ use App\Console\Command\CommandAbstract;
|
|||
use App\Entity;
|
||||
use App\Utilities;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:account:reset-password',
|
||||
description: 'Reset the password of the specified account.',
|
||||
)]
|
||||
class ResetPasswordCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
string $email
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('email', InputArgument::REQUIRED);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$email = $input->getArgument('email');
|
||||
|
||||
$io->title('Reset Account Password');
|
||||
|
||||
$user = $em->getRepository(Entity\User::class)
|
||||
$user = $this->em->getRepository(Entity\User::class)
|
||||
->findOneBy(['email' => $email]);
|
||||
|
||||
if ($user instanceof Entity\User) {
|
||||
|
@ -28,8 +48,8 @@ class ResetPasswordCommand extends CommandAbstract
|
|||
$user->setNewPassword($temp_pw);
|
||||
$user->setTwoFactorSecret();
|
||||
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
$this->em->persist($user);
|
||||
$this->em->flush();
|
||||
|
||||
$io->text([
|
||||
'The account password has been reset. The new temporary password is:',
|
||||
|
|
|
@ -7,31 +7,51 @@ namespace App\Console\Command\Users;
|
|||
use App\Console\Command\CommandAbstract;
|
||||
use App\Entity;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'azuracast:account:set-administrator',
|
||||
description: 'Set the account specified as a global administrator.',
|
||||
)]
|
||||
class SetAdministratorCommand extends CommandAbstract
|
||||
{
|
||||
public function __invoke(
|
||||
SymfonyStyle $io,
|
||||
EntityManagerInterface $em,
|
||||
Entity\Repository\RolePermissionRepository $perms_repo,
|
||||
string $email
|
||||
): int {
|
||||
public function __construct(
|
||||
protected EntityManagerInterface $em,
|
||||
protected Entity\Repository\RolePermissionRepository $permsRepo,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('email', InputArgument::REQUIRED);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$email = $input->getArgument('email');
|
||||
|
||||
$io->title('Set Administrator');
|
||||
|
||||
$user = $em->getRepository(Entity\User::class)
|
||||
$user = $this->em->getRepository(Entity\User::class)
|
||||
->findOneBy(['email' => $email]);
|
||||
|
||||
if ($user instanceof Entity\User) {
|
||||
$adminRole = $perms_repo->ensureSuperAdministratorRole();
|
||||
$adminRole = $this->permsRepo->ensureSuperAdministratorRole();
|
||||
|
||||
$user_roles = $user->getRoles();
|
||||
if (!$user_roles->contains($adminRole)) {
|
||||
$user_roles->add($adminRole);
|
||||
}
|
||||
|
||||
$em->persist($user);
|
||||
$em->flush();
|
||||
$this->em->persist($user);
|
||||
$this->em->flush();
|
||||
|
||||
$io->text(
|
||||
__(
|
||||
|
|
|
@ -5,12 +5,16 @@ declare(strict_types=1);
|
|||
namespace App\Event;
|
||||
|
||||
use App\Console\Application;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class BuildConsoleCommands extends Event
|
||||
{
|
||||
protected array $aliases = [];
|
||||
|
||||
public function __construct(
|
||||
protected Application $cli
|
||||
protected Application $cli,
|
||||
protected ContainerInterface $di
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -18,4 +22,19 @@ class BuildConsoleCommands extends Event
|
|||
{
|
||||
return $this->cli;
|
||||
}
|
||||
|
||||
public function getContainer(): ContainerInterface
|
||||
{
|
||||
return $this->di;
|
||||
}
|
||||
|
||||
public function addAliases(array $aliases): void
|
||||
{
|
||||
$this->aliases = array_merge($this->aliases, $aliases);
|
||||
}
|
||||
|
||||
public function getAliases(): array
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue