Make the profile page the per-station "home" page, moving previous index to "Statistics Overview" under "Reports".

This commit is contained in:
Buster "Silver Eagle" Neece 2018-09-29 08:35:40 -05:00
parent fef1c487bb
commit 0c464588fa
15 changed files with 1127 additions and 1114 deletions

View File

@ -4,7 +4,6 @@ use App\Middleware;
return function(\Slim\App $app)
{
$app->group('/admin', function () {
$this->get('', Controller\Admin\IndexController::class.':indexAction')
@ -296,7 +295,7 @@ return function(\Slim\App $app)
$app->group('/station/{station}', function () {
$this->get('', Controller\Stations\IndexController::class.':indexAction')
$this->get('', Controller\Stations\IndexController::class)
->setName('stations:index:index');
$this->group('/automation', function () {
@ -380,16 +379,13 @@ return function(\Slim\App $app)
})->add([Middleware\Permissions::class, 'manage station mounts', true]);
$this->group('/profile', function () {
$this->get('/profile', function (\App\Http\Request $request, \App\Http\Response $response) {
return $response->withRedirect($request->getRouter()->fromHere('stations:index:index'));
})->setName('stations:profile:index');
$this->get('', Controller\Stations\ProfileController::class.':indexAction')
->setName('stations:profile:index');
$this->map(['GET', 'POST'], '/edit', Controller\Stations\ProfileController::class.':editAction')
->setName('stations:profile:edit')
->add([Middleware\Permissions::class, 'manage station profile', true]);
});
$this->map(['GET', 'POST'], '/profile/edit', Controller\Stations\EditProfileController::class)
->setName('stations:profile:edit')
->add([Middleware\Permissions::class, 'manage station profile', true]);
$this->group('/remotes', function () {
@ -409,6 +405,9 @@ return function(\Slim\App $app)
$this->group('/reports', function () {
$this->get('/overview', Controller\Stations\Reports\OverviewController::class)
->setName('stations:reports:overview');
$this->get('/timeline[/format/{format}]', Controller\Stations\Reports\TimelineController::class)
->setName('stations:reports:timeline');

View File

@ -7,8 +7,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"POT-Creation-Date: 2018-09-27T17:24:43+00:00\n"
"PO-Revision-Date: 2018-09-27T17:24:43+00:00\n"
"POT-Creation-Date: 2018-09-29T13:33:36+00:00\n"
"PO-Revision-Date: 2018-09-29T13:33:36+00:00\n"
"Language: \n"
#: /var/azuracast/www/src/Controller/Admin/ApiController.php:54
@ -122,7 +122,7 @@ msgstr ""
#: /var/azuracast/www/resources/templates/admin/users/index.phtml:7
#: /var/azuracast/www/resources/templates/admin/users/index.phtml:8
#: /var/azuracast/www/resources/templates/frontend/api_keys/index.phtml:10
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:52
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:56
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:7
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:8
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:14
@ -166,7 +166,7 @@ msgid "System Settings"
msgstr ""
#: /var/azuracast/www/src/Controller/Admin/StationsController.php:97
#: /var/azuracast/www/src/Controller/Stations/ProfileController.php:147
#: /var/azuracast/www/src/Controller/Stations/EditProfileController.php:70
msgid "This port is currently in use by the station \"%s\"."
msgstr ""
@ -178,7 +178,7 @@ msgstr ""
#: /var/azuracast/www/resources/templates/admin/stations/index.phtml:7
#: /var/azuracast/www/resources/templates/admin/stations/index.phtml:8
#: /var/azuracast/www/resources/templates/admin/stations/index.phtml:21
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:52
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:56
msgid "Station"
msgstr ""
@ -286,19 +286,23 @@ msgstr ""
msgid "Station not found!"
msgstr ""
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:79
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:80
msgid "Setup has already been completed!"
msgstr ""
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:102
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:103
msgid "Super Administrator"
msgstr ""
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:196
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:152
msgid "Want to use SHOUTcast 2? <a href=\"%s\" target=\"_blank\">Install it here</a>, then reload this page."
msgstr ""
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:201
msgid "Setup is now complete!"
msgstr ""
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:196
#: /var/azuracast/www/src/Controller/Frontend/SetupController.php:201
msgid "Continue setting up your station in the main AzuraCast app."
msgstr ""
@ -337,6 +341,25 @@ msgstr ""
msgid "File Not Processed"
msgstr ""
#: /var/azuracast/www/src/Controller/Stations/IndexController.php:64
#: /var/azuracast/www/src/Controller/Stations/IndexController.php:81
#: /var/azuracast/www/config/forms/media.php:21
#: /var/azuracast/www/config/forms/song.php:33
#: /var/azuracast/www/resources/templates/frontend/public/index.js.phtml:8
#: /var/azuracast/www/resources/templates/frontend/public/player_component.js.phtml:7
#: /var/azuracast/www/resources/templates/stations/reports/requests.phtml:26
#: /var/azuracast/www/resources/templates/stations/reports/timeline.phtml:40
msgid "Song Title"
msgstr ""
#: /var/azuracast/www/src/Controller/Stations/IndexController.php:65
#: /var/azuracast/www/src/Controller/Stations/IndexController.php:82
#: /var/azuracast/www/config/forms/media.php:28
#: /var/azuracast/www/resources/templates/frontend/public/index.js.phtml:9
#: /var/azuracast/www/resources/templates/frontend/public/player_component.js.phtml:8
msgid "Song Artist"
msgstr ""
#: /var/azuracast/www/src/Controller/Stations/MountsController.php:38
#: /var/azuracast/www/src/Controller/Stations/PlaylistsController.php:62
#: /var/azuracast/www/src/Controller/Stations/StreamersController.php:43
@ -358,7 +381,7 @@ msgstr ""
#: /var/azuracast/www/src/Controller/Stations/PlaylistsController.php:303
#: /var/azuracast/www/src/Controller/Stations/PlaylistsController.php:310
#: /var/azuracast/www/src/Controller/Stations/PlaylistsController.php:421
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:46
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:47
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:14
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:15
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:39
@ -381,25 +404,6 @@ msgstr ""
msgid "%d song(s) were imported into the playlist."
msgstr ""
#: /var/azuracast/www/src/Controller/Stations/ProfileController.php:88
#: /var/azuracast/www/src/Controller/Stations/ProfileController.php:105
#: /var/azuracast/www/config/forms/media.php:21
#: /var/azuracast/www/config/forms/song.php:33
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:112
#: /var/azuracast/www/resources/templates/frontend/public/player_component.phtml:8
#: /var/azuracast/www/resources/templates/stations/reports/requests.phtml:26
#: /var/azuracast/www/resources/templates/stations/reports/timeline.phtml:40
msgid "Song Title"
msgstr ""
#: /var/azuracast/www/src/Controller/Stations/ProfileController.php:89
#: /var/azuracast/www/src/Controller/Stations/ProfileController.php:106
#: /var/azuracast/www/config/forms/media.php:28
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:113
#: /var/azuracast/www/resources/templates/frontend/public/player_component.phtml:9
msgid "Song Artist"
msgstr ""
#: /var/azuracast/www/src/Controller/Stations/RemotesController.php:81
#: /var/azuracast/www/src/Controller/Stations/RemotesController.php:89
#: /var/azuracast/www/src/Controller/Stations/RemotesController.php:114
@ -620,10 +624,9 @@ msgid "Manage Station %s"
msgstr ""
#: /var/azuracast/www/config/admin/actions.php:17
#: /var/azuracast/www/resources/templates/stations/profile/disabled.phtml:5
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:5
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:99
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:38
#: /var/azuracast/www/resources/templates/stations/index/disabled.phtml:5
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:5
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:99
msgid "Profile"
msgstr ""
@ -638,15 +641,15 @@ msgstr ""
#: /var/azuracast/www/config/admin/actions.php:20
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:1
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:5
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:58
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:54
msgid "Mount Points"
msgstr ""
#: /var/azuracast/www/config/admin/actions.php:21
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:216
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:216
#: /var/azuracast/www/resources/templates/stations/remotes/index.phtml:1
#: /var/azuracast/www/resources/templates/stations/remotes/index.phtml:5
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:64
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:60
msgid "Remote Relays"
msgstr ""
@ -655,7 +658,7 @@ msgid "Automation"
msgstr ""
#: /var/azuracast/www/config/admin/actions.php:24
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:70
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:66
#: /var/azuracast/www/resources/templates/stations/webhooks/index.phtml:1
#: /var/azuracast/www/resources/templates/stations/webhooks/index.phtml:5
msgid "Web Hooks"
@ -729,10 +732,10 @@ msgstr ""
#: /var/azuracast/www/config/forms/station.php:62
#: /var/azuracast/www/config/forms/station.php:75
#: /var/azuracast/www/config/forms/station.php:107
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:134
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:147
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:47
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:81
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:134
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:147
#: /var/azuracast/www/resources/templates/stations/remotes/index.phtml:44
#: /var/azuracast/www/resources/templates/stations/webhooks/index.phtml:41
msgid "Disabled"
@ -741,9 +744,9 @@ msgstr ""
#: /var/azuracast/www/config/forms/automation.php:15
#: /var/azuracast/www/config/forms/station.php:63
#: /var/azuracast/www/config/forms/station.php:76
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:127
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:144
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:45
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:127
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:144
#: /var/azuracast/www/resources/templates/stations/remotes/index.phtml:42
msgid "Enabled"
msgstr ""
@ -1397,7 +1400,7 @@ msgstr ""
#: /var/azuracast/www/config/forms/user.php:10
#: /var/azuracast/www/resources/templates/admin/custom_fields/index.phtml:22
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:75
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:115
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:115
#: /var/azuracast/www/resources/templates/stations/webhooks/index.phtml:25
msgid "Name"
msgstr ""
@ -1537,8 +1540,8 @@ msgstr ""
#: /var/azuracast/www/resources/templates/admin/permissions/index.phtml:21
#: /var/azuracast/www/resources/templates/admin/stations/index.phtml:20
#: /var/azuracast/www/resources/templates/admin/users/index.phtml:21
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:21
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:94
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:23
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:95
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:86
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:24
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:38
@ -1680,7 +1683,7 @@ msgstr ""
#: /var/azuracast/www/config/forms/station.php:28
#: /var/azuracast/www/config/forms/station_clone.php:15
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:68
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:72
msgid "Station Name"
msgstr ""
@ -2045,8 +2048,8 @@ msgid "Now playing on %s:"
msgstr ""
#: /var/azuracast/www/config/forms/webhook/discord.php:64
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:18
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:91
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:20
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:92
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:76
#: /var/azuracast/www/resources/templates/stations/playlists/reorder.phtml:23
#: /var/azuracast/www/resources/templates/stations/reports/performance.phtml:31
@ -2054,7 +2057,7 @@ msgid "Title"
msgstr ""
#: /var/azuracast/www/config/forms/webhook/discord.php:73
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:119
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:119
msgid "Description"
msgstr ""
@ -2280,10 +2283,10 @@ msgstr ""
#: /var/azuracast/www/resources/templates/frontend/api_keys/index.phtml:36
#: /var/azuracast/www/resources/templates/frontend/profile/index.phtml:22
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:213
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:102
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:103
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:34
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:48
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:102
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:103
#: /var/azuracast/www/resources/templates/stations/remotes/index.phtml:34
#: /var/azuracast/www/resources/templates/stations/streamers/index.phtml:34
#: /var/azuracast/www/resources/templates/stations/webhooks/index.phtml:34
@ -2322,11 +2325,11 @@ msgstr ""
#: /var/azuracast/www/resources/templates/admin/install_shoutcast/installed.phtml:1
#: /var/azuracast/www/resources/templates/admin/install_shoutcast/installed.phtml:5
msgid "SHOUTcast is Installed"
msgid "SHOUTcast Installed"
msgstr ""
#: /var/azuracast/www/resources/templates/admin/install_shoutcast/installed.phtml:9
msgid "The SHOUTcast 2 DNAS is already installed."
msgid "The SHOUTcast 2 DNAS is installed and ready for use."
msgstr ""
#: /var/azuracast/www/resources/templates/admin/permissions/index.phtml:24
@ -2342,7 +2345,7 @@ msgid "Manage Stations"
msgstr ""
#: /var/azuracast/www/resources/templates/admin/stations/index.phtml:28
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:92
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:96
msgid "Manage"
msgstr ""
@ -2407,11 +2410,11 @@ msgid "Your full API key is below:"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/api_keys/new_key.phtml:25
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:132
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:164
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:279
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:286
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:294
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:132
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:164
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:279
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:286
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:294
msgid "Copy to Clipboard"
msgstr ""
@ -2424,48 +2427,48 @@ msgid "Continue"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:1
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:16
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:20
#: /var/azuracast/www/resources/templates/main.phtml:74
msgid "Dashboard"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:24
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:28
msgid "Listeners Across All Stations"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:34
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:38
msgid "Listeners Per Station"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:48
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:52
msgid "Station Overview"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:69
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:140
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:178
#: /var/azuracast/www/resources/templates/stations/index/index-dailystats.phtml:28
#: /var/azuracast/www/resources/templates/stations/index/index-dayofweek.phtml:27
#: /var/azuracast/www/resources/templates/stations/index/index-hourlystats.phtml:14
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:76
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:73
#: /var/azuracast/www/resources/templates/frontend/index/index_metrics.js.phtml:37
#: /var/azuracast/www/resources/templates/frontend/index/index_metrics.js.phtml:75
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:76
#: /var/azuracast/www/resources/templates/stations/reports/listeners.phtml:1
#: /var/azuracast/www/resources/templates/stations/reports/listeners.phtml:18
#: /var/azuracast/www/resources/templates/stations/reports/overview.js.phtml:32
#: /var/azuracast/www/resources/templates/stations/reports/overview.js.phtml:96
#: /var/azuracast/www/resources/templates/stations/reports/overview.js.phtml:110
#: /var/azuracast/www/resources/templates/stations/reports/timeline.phtml:38
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:80
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:77
msgid "Listeners"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:70
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:32
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:74
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:32
msgid "Now Playing"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:71
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:75
msgid "Options"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:77
#: /var/azuracast/www/resources/templates/frontend/public/player_component.phtml:21
#: /var/azuracast/www/resources/templates/frontend/index/index.phtml:81
#: /var/azuracast/www/resources/templates/frontend/public/player_component.phtml:7
msgid "Play/Pause"
msgstr ""
@ -2481,61 +2484,61 @@ msgstr ""
msgid "Update Profile Picture"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/embed.phtml:20
#: /var/azuracast/www/resources/templates/frontend/public/embed.phtml:24
msgid "Volume"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:19
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:92
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.js.phtml:16
#: /var/azuracast/www/resources/templates/frontend/public/index.js.phtml:63
msgid "Request"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:21
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:93
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:77
#: /var/azuracast/www/resources/templates/stations/playlists/reorder.phtml:24
#: /var/azuracast/www/resources/templates/stations/reports/performance.phtml:32
msgid "Artist"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:20
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:93
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:22
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:94
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:78
#: /var/azuracast/www/resources/templates/stations/playlists/reorder.phtml:25
msgid "Album"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/embedrequests.phtml:43
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:165
msgid "Request"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:21
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:22
#: /var/azuracast/www/resources/templates/partials/radio_controls.phtml:8
msgid "Mute"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:22
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:23
#: /var/azuracast/www/resources/templates/partials/radio_controls.phtml:11
msgid "Unmute"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:37
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:64
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:38
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:65
msgid "Song History"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:41
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:42
msgid "Request Song"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:50
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:51
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:182
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:183
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:60
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:182
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:183
msgid "Download %s"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:83
#: /var/azuracast/www/resources/templates/frontend/public/index.phtml:84
msgid "Request a Song"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/public/player_component.phtml:27
#: /var/azuracast/www/resources/templates/frontend/public/player_component.phtml:13
msgid "Album Cover"
msgstr ""
@ -2545,8 +2548,8 @@ msgstr ""
#: /var/azuracast/www/resources/templates/frontend/setup/settings.phtml:1
#: /var/azuracast/www/resources/templates/frontend/setup/settings.phtml:4
#: /var/azuracast/www/resources/templates/frontend/setup/station.phtml:1
#: /var/azuracast/www/resources/templates/frontend/setup/station.phtml:4
#: /var/azuracast/www/resources/templates/frontend/setup/station.phtml:3
#: /var/azuracast/www/resources/templates/frontend/setup/station.phtml:13
msgid "AzuraCast Setup"
msgstr ""
@ -2558,11 +2561,11 @@ msgstr ""
msgid "Complete the setup process by providing some information about your broadcast environment. These settings can be changed later from the administration panel."
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/setup/station.phtml:9
#: /var/azuracast/www/resources/templates/frontend/setup/station.phtml:18
msgid "Step 2: Create a New Station"
msgstr ""
#: /var/azuracast/www/resources/templates/frontend/setup/station.phtml:10
#: /var/azuracast/www/resources/templates/frontend/setup/station.phtml:19
msgid "Continue the setup process by creating your first radio station below. You can edit any of these details later."
msgstr ""
@ -2621,8 +2624,8 @@ msgstr ""
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:1
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:22
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:342
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:43
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:342
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:39
msgid "Music Files"
msgstr ""
@ -2673,11 +2676,11 @@ msgid "Modified"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/files/index.phtml:85
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:343
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:4
#: /var/azuracast/www/resources/templates/stations/playlists/index.phtml:12
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:343
#: /var/azuracast/www/resources/templates/stations/reports/duplicates.phtml:25
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:46
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:42
msgid "Playlists"
msgstr ""
@ -2706,78 +2709,146 @@ msgstr ""
msgid "Rename"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index-dailystats.phtml:56
msgid "Daily Listener Range"
#: /var/azuracast/www/resources/templates/stations/index/disabled.phtml:15
msgid "Station Broadcasting Disabled"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index-dailystats.phtml:60
msgid "Hourly Average Listeners"
#: /var/azuracast/www/resources/templates/stations/index/disabled.phtml:19
msgid "Your station is currently not enabled for broadcasting. You can still manage media, playlists, and other station settings. To re-enable broadcasting, edit your station profile."
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index-dailystats.phtml:64
msgid "Daily Average Listeners"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:23
msgid "On the Air"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index-hourlystats.phtml:24
msgid "Average Listeners"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:50
msgid "Playing Next"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:1
msgid "Station Center"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:64
msgid "Now Streaming"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:25
msgid "Station Statistics"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:78
#: /var/azuracast/www/resources/templates/stations/reports/performance.phtml:39
msgid "Total"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:29
msgid "Listeners by Day"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:80
msgid "Unique"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:30
msgid "Listeners by Hour"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:89
msgid "Skip Song"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:31
msgid "Listeners by Day of Week"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:90
msgid "Disconnect Streamer"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:62
msgid "Best Performing Songs"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:124
#: /var/azuracast/www/resources/templates/stations/reports/requests.phtml:1
#: /var/azuracast/www/resources/templates/stations/reports/requests.phtml:11
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:80
msgid "Song Requests"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:62
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:101
msgid "in the last 48 hours"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:134
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:147
msgid "Edit station profile to enable."
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:72
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:111
#: /var/azuracast/www/resources/templates/stations/reports/timeline.phtml:39
msgid "Change"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:141
msgid "Streamers/DJs"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:73
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:112
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:154
msgid "Song"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:101
msgid "Worst Performing Songs"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:143
msgid "Most Played Songs"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:143
msgid "in the last month"
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:145
msgid "Manage streamer accounts"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:153
#: /var/azuracast/www/resources/templates/stations/reports/performance.phtml:40
msgid "Plays"
msgid "Base Directory"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:157
msgid "Media Directory"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:161
msgid "Player Embed Code"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:175
msgid "Streams"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:195
msgid "Local Streams"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:243
msgid "Icecast (Broadcasting Service)"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:245
msgid "SHOUTcast DNAS 2 (Broadcasting Service)"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:247
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:319
msgid "Running"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:247
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:319
msgid "Not Running"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:257
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:329
msgid "Restart Service"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:258
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:330
msgid "Start Service"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:259
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:331
msgid "Stop Service"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:272
msgid "Administration URL"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:276
msgid "Administrator Password"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:283
msgid "Source Password"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:291
msgid "Relay Password"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:306
msgid "AutoDJ Disabled"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:309
msgid "AutoDJ has been disabled for this station. No music will automatically be played when a source is not live."
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:317
msgid "Liquidsoap (AutoDJ Service)"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/index/index.phtml:338
msgid "LiquidSoap is currently shuffling from <b>%d uploaded songs</b> in <b>%d playlists</b>."
msgstr ""
#: /var/azuracast/www/resources/templates/stations/mounts/index.phtml:13
@ -2857,148 +2928,6 @@ msgstr ""
msgid "Reorder Playlist: %s"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/disabled.phtml:15
msgid "Station Broadcasting Disabled"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/disabled.phtml:19
msgid "Your station is currently not enabled for broadcasting. You can still manage media, playlists, and other station settings. To re-enable broadcasting, edit your station profile."
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:23
msgid "On the Air"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:50
msgid "Playing Next"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:64
msgid "Now Streaming"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:78
#: /var/azuracast/www/resources/templates/stations/reports/performance.phtml:39
msgid "Total"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:80
msgid "Unique"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:89
msgid "Skip Song"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:90
msgid "Disconnect Streamer"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:124
#: /var/azuracast/www/resources/templates/stations/reports/requests.phtml:1
#: /var/azuracast/www/resources/templates/stations/reports/requests.phtml:11
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:83
msgid "Song Requests"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:134
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:147
msgid "Edit station profile to enable."
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:141
msgid "Streamers/DJs"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:145
msgid "Manage streamer accounts"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:153
msgid "Base Directory"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:157
msgid "Media Directory"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:161
msgid "Player Embed Code"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:175
msgid "Streams"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:195
msgid "Local Streams"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:243
msgid "Icecast (Broadcasting Service)"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:245
msgid "SHOUTcast DNAS 2 (Broadcasting Service)"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:247
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:319
msgid "Running"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:247
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:319
msgid "Not Running"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:257
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:329
msgid "Restart Service"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:258
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:330
msgid "Start Service"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:259
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:331
msgid "Stop Service"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:272
msgid "Administration URL"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:276
msgid "Administrator Password"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:283
msgid "Source Password"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:291
msgid "Relay Password"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:306
msgid "AutoDJ Disabled"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:309
msgid "AutoDJ has been disabled for this station. No music will automatically be played when a source is not live."
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:317
msgid "Liquidsoap (AutoDJ Service)"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/profile/index.phtml:338
msgid "LiquidSoap is currently shuffling from <b>%d uploaded songs</b> in <b>%d playlists</b>."
msgstr ""
#: /var/azuracast/www/resources/templates/stations/remotes/index.phtml:13
msgid "Remote relays let you work with broadcasting software outside this server. Any relay you include here will be included in your station's statistics. You can also broadcast from this server to remote relays."
msgstr ""
@ -3083,9 +3012,81 @@ msgstr ""
msgid "Last Month"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.js.phtml:60
msgid "Daily Listener Range"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.js.phtml:64
msgid "Hourly Average Listeners"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.js.phtml:68
msgid "Daily Average Listeners"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.js.phtml:120
msgid "Average Listeners"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:1
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:21
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:75
msgid "Statistics Overview"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:25
msgid "Listeners by Day"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:26
msgid "Listeners by Hour"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:27
msgid "Listeners by Day of Week"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:50
msgid "Best Performing Songs"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:50
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:89
msgid "in the last 48 hours"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:60
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:99
#: /var/azuracast/www/resources/templates/stations/reports/timeline.phtml:39
msgid "Change"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:61
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:100
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:142
msgid "Song"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:89
msgid "Worst Performing Songs"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:131
msgid "Most Played Songs"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:131
msgid "in the last month"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/overview.phtml:141
#: /var/azuracast/www/resources/templates/stations/reports/performance.phtml:40
msgid "Plays"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/performance.phtml:1
#: /var/azuracast/www/resources/templates/stations/reports/performance.phtml:11
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:87
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:84
msgid "Song Listener Impact"
msgstr ""
@ -3150,7 +3151,7 @@ msgstr ""
#: /var/azuracast/www/resources/templates/stations/reports/timeline.phtml:1
#: /var/azuracast/www/resources/templates/stations/reports/timeline.phtml:14
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:85
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:82
msgid "Song Playback Timeline"
msgstr ""
@ -3182,7 +3183,7 @@ msgstr ""
msgid "Public Page"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:52
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:48
#: /var/azuracast/www/resources/templates/stations/streamers/disabled.phtml:1
#: /var/azuracast/www/resources/templates/stations/streamers/disabled.phtml:5
#: /var/azuracast/www/resources/templates/stations/streamers/index.phtml:1
@ -3190,31 +3191,31 @@ msgstr ""
msgid "Streamer/DJ Accounts"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:76
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:72
msgid "Reports"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:88
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:85
msgid "Duplicate Songs"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:91
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:88
msgid "SoundExchange Royalties"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:98
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:95
msgid "Utilities"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:102
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:99
msgid "Automated Assignment"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:105
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:102
msgid "Restart Broadcasting"
msgstr ""
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:120
#: /var/azuracast/www/resources/templates/stations/sidebar.phtml:117
msgid "Please wait..."
msgstr ""

View File

@ -1,32 +0,0 @@
<div id="listeners_by_day_of_week" style="height: 250px;"></div>
<script type="text/javascript" nonce="<?=$assets->getCspNonce() ?>">
$(function () {
$('#listeners_by_day_of_week').highcharts({
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false
},
title: { text:'' },
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
color: '#000000',
connectorColor: '#000000',
format: '<b>{point.name}</b>: {point.percentage:.1f} %'
}
}
},
series: [{
type: 'pie',
name: <?=$this->escapeJs(__('Listeners')) ?>,
data: <?=$day_of_week_stats ?>
}]
});
});
</script>

View File

@ -1,30 +0,0 @@
<div id="listeners_by_hour" style="height: 250px;"></div>
<script type="text/javascript" nonce="<?=$assets->getCspNonce() ?>">
$(function () {
$('#listeners_by_hour').highcharts({
chart: {},
title: { text:'' },
xAxis: {
allowDecimals: false,
tickInterval: 1
},
yAxis: {
title: {
text: '<?=__('Listeners') ?>'
},
min: 0
},
tooltip: {
crosshairs: true,
shared: true
},
series: [{
name: <?=$this->escapeJs(__('Average Listeners')) ?>,
type: 'column',
data: <?=$averages_by_hour ?>
}]
});
});
</script>

View File

@ -1,176 +1,350 @@
<?php $this->layout('main', ['title' => __('Station Center'), 'manual' => true]) ?>
<?php
/** @var App\Entity\Station $station */
$this->layout('main', [
'title' => __('Profile'),
'manual' => true,
'sidebar_tab' => 'profile',
]);
/** @var \App\Assets $assets */
$assets
->load('highcharts')
->load('highcharts_theme_'.$customization->getTheme());
->load('vue')
->load('radio')
->load('clipboard')
->addInlineJs($this->fetch('stations/index/index.js', ['nowplaying' => $nowplaying]), 99);
?>
<script type="text/javascript" nonce="<?=$assets->getCspNonce() ?>">
$(function () {
Highcharts.setOptions({
global: {
timezoneOffset: <?=\App\Timezone::getOffsetMinutes() ?>,
useUTC: false
}
});
});
</script>
<div class="row">
<div class="col-sm-12">
<div class="col-sm-6">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><?=__('Station Statistics') ?></h2>
<h2><?=__('On the Air') ?></h2>
</div>
<div role="tabpanel">
<ul class="tab-nav" role="tablist">
<li class="p-l-10 active"><a role="tab" data-toggle="tab" aria-expanded="true" aria-controls="listeners-by-day" href="#listeners-by-day"><?=__('Listeners by Day') ?></a></li>
<li><a role="tab" data-toggle="tab" aria-controls="listeners-by-hour" href="#listeners-by-hour"><?=__('Listeners by Hour') ?></a></li>
<li><a role="tab" data-toggle="tab" aria-controls="listeners-by-day-of-week" href="#listeners-by-day-of-week"><?=__('Listeners by Day of Week') ?></a></li>
</ul>
<div class="card-body card-padding nowplaying" id="profile-nowplaying">
<div class="row">
<div class="tab-content">
<div class="tab-pane card-body card-padding active" id="listeners-by-day" role="tabpanel">
<?=$this->fetch('stations/index/index-dailystats', [
'daily_ranges' => $daily_ranges,
'hourly_averages' => $hourly_averages,
'daily_averages' => $daily_averages
]) ?>
<div class="col-sm-4">
<div class="m-l-25" style="position: relative;">
<div style="position: absolute; top: 0; left: -22px;"><i class="zmdi-hc-lg zmdi zmdi-collection-music"></i></div>
<h6><?=__('Now Playing') ?>:</h6>
<h4 class="media-heading">
{{ np.now_playing.song.title }}
</h4>
<div>
{{ np.now_playing.song.artist }}
</div>
<div class="nowplaying-progress" v-if="time_display">
{{ time_display }}
</div>
</div>
</div>
<div class="tab-pane card-padding" id="listeners-by-hour" role="tabpanel">
<?=$this->fetch('stations/index/index-hourlystats', [
'averages_by_hour' => $averages_by_hour,
]) ?>
<div class="col-sm-4" v-if="!np.live.is_live && np.playing_next">
<div class="m-l-25" style="position: relative;">
<div style="position: absolute; top: 0; left: -22px;"><i class="zmdi-hc-lg zmdi zmdi-skip-next"></i></div>
<h6><?=__('Playing Next') ?>:</h6>
<h4 class="media-heading">
{{ np.playing_next.song.title }}
</h4>
<div>
{{ np.playing_next.song.artist }}
</div>
</div>
</div>
<div class="tab-pane card-padding" id="listeners-by-day-of-week" role="tabpanel">
<?=$this->fetch('stations/index/index-dayofweek', [
'day_of_week_stats' => $day_of_week_stats,
]) ?>
<div class="col-sm-4" v-if="np.live.is_live">
<div class="m-l-25" style="position: relative;">
<div style="position: absolute; top: 0; left: -22px;"><i class="zmdi-hc-lg zmdi zmdi-account"></i></div>
<h6><?=__('Now Streaming') ?>:</h6>
<h4 class="media-heading">
{{ np.live.streamer_name }}
</h4>
</div>
</div>
<div class="col-sm-3">
<div class="m-l-25" style="position: relative;">
<div style="position: absolute; top: 0; left: -22px;"><i class="zmdi-hc-lg zmdi zmdi-headset"></i></div>
<h6><?=__('Listeners') ?>:</h6>
<h4><span>{{ np.listeners.total }}</span> <?=__('Total') ?></h4>
<div>
<span>{{ np.listeners.unique }}</span> <?=__('Unique') ?>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><i class="zmdi zmdi-thumb-up"></i> <?=__('Best Performing Songs') ?> <small><?=__('in the last 48 hours') ?></small></h2>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-nopadding">
<colgroup>
<col width="20%">
<col width="80%">
</colgroup>
<thead>
<tr>
<th><?=__('Change') ?></th>
<th><?=__('Song') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($best_performing_songs as $song_row): ?>
<tr>
<td class="text-center text-success">
<i class="zmdi zmdi-thumb-up"></i> <?=abs($song_row['stat_delta']) ?><br>
<small><?=$song_row['stat_start'] ?> to <?=$song_row['stat_end'] ?>
</td>
<td>
<?php if ($song_row['song']['title']): ?>
<b><?=$song_row['song']['title'] ?></b><br>
<?=$song_row['song']['artist'] ?>
<?php else: ?>
<?=$song_row['song']['text'] ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php if ($backend_type == 'liquidsoap' && $acl->userAllowed($user, 'manage station broadcasting', $station->getId())): ?>
<div class="buttons pull-right p-t-15">
<a id="btn_skip_song" class="btn btn-sm btn-primary" role="button" href="<?=$router->fromHere('api:stations:backend', ['do' => 'skip']) ?>" v-if="!np.live.is_live"><i class="zmdi zmdi-skip-next"></i> <?=__('Skip Song') ?></a>
<a id="btn_disconnect_streamer" class="btn btn-sm btn-primary" role="button" href="<?=$router->fromHere('api:stations:backend', ['do' => 'disconnect']) ?>" v-if="np.live.is_live"><i class="zmdi zmdi-volume-off"></i> <?=__('Disconnect Streamer') ?></a>
</div>
<div class="clearfix"></div>
<?php endif; ?>
</div>
</section>
</div>
<div class="col-sm-6">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><i class="zmdi zmdi-thumb-down"></i> <?=__('Worst Performing Songs') ?> <small><?=__('in the last 48 hours') ?></small></h2>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-nopadding">
<colgroup>
<col width="20%">
<col width="80%">
</colgroup>
<thead>
<tr>
<th><?=__('Change') ?></th>
<th><?=__('Song') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($worst_performing_songs as $song_row): ?>
<tr>
<td class="text-center text-danger">
<i class="zmdi zmdi-thumb-down"></i> <?=abs($song_row['stat_delta']) ?><br>
<small><?=$song_row['stat_start'] ?> to <?=$song_row['stat_end'] ?>
</td>
<td>
<?php if ($song_row['song']['title']): ?>
<b><?=$song_row['song']['title'] ?></b><br>
<?=$song_row['song']['artist'] ?>
<?php else: ?>
<?=$song_row['song']['text'] ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><?=__('Most Played Songs') ?> <small><?=__('in the last month') ?></small></h2>
<h2><?=__('Profile') ?></h2>
<?php if ($acl->userAllowed($user, 'manage station profile', $station->getId())): ?>
<a class="btn bgm-blue btn-float" role="button" title="<?=__('Edit') ?>" href="<?=$router->fromHere('stations:profile:edit') ?>">
<span class="sr-only"><?=__('Edit') ?></span>
<i class="zmdi zmdi-edit"></i>
</a>
<?php endif; ?>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-nopadding">
<colgroup>
<col width="10%">
<col width="90%">
</colgroup>
<thead>
<tr>
<th><?=__('Plays') ?></th>
<th><?=__('Song') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($song_totals['played'] as $song_row): ?>
<tr>
<td class="text-center"><?=$song_row['records'] ?></td>
<td>
<?php if ($song_row['song']['title']): ?>
<b><?=$song_row['song']['title'] ?></b><br>
<?=$song_row['song']['artist'] ?>
<table class="table table-striped table-responsive">
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tbody>
<tr>
<td><?=__('Name') ?></td>
<td><?=$this->e($station->getName()) ?></td>
</tr>
<tr>
<td><?=__('Description') ?></td>
<td><?=$this->e($station->getDescription()) ?></td>
</tr>
<?php if ($backend::supportsRequests()): ?>
<tr>
<td><?=__('Song Requests') ?></td>
<td>
<?php if ($station->getEnableRequests()): ?>
<span class="text-success"><?=__('Enabled') ?></span>
<div class="form-field">
<textarea id="request_embed_url" class="full-width form-control text-preformatted" spellcheck="false" style="height: 70px;"><iframe src="<?=$router->named('public:embedrequests', ['station' => $station->getShortName()], [], true) ?>" frameborder="0" allowtransparency="true" style="width: 100%; min-height: 850px; border: 0;"></iframe></textarea>
</div>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#request_embed_url"><i class="zmdi zmdi-copy"></i> <?=__('Copy to Clipboard') ?></button>
<?php else: ?>
<?=$song_row['song']['text'] ?>
<span class="text-danger"><?=__('Disabled') ?></span><br><small><?=__('Edit station profile to enable.') ?></small>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</td>
</tr>
<? endif; ?>
<?php if ($backend::supportsStreamers()): ?>
<tr>
<td><?=__('Streamers/DJs') ?></td>
<td>
<?php if ($station->getEnableStreamers()): ?>
<span class="text-success"><?=__('Enabled') ?></span><br>
<a href="<?=$router->fromHere('stations:streamers:index') ?>"><?=__('Manage streamer accounts') ?></a>
<?php else: ?>
<span class="text-danger"><?=__('Disabled') ?></span><br><small><?=__('Edit station profile to enable.') ?></small>
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
<tr>
<td><?=__('Base Directory') ?></td>
<td class="text-preformatted"><?=$this->e($station->getRadioBaseDir()) ?></td>
</tr>
<tr>
<td><?=__('Media Directory') ?></td>
<td class="text-preformatted"><?=$this->e($station->getRadioMediaDir()) ?></td>
</tr>
<tr>
<td><?=__('Player Embed Code') ?></td>
<td class="form-field">
<textarea id="player_embed_url" class="full-width form-control text-preformatted" spellcheck="false" style="height: 70px;"><iframe src="<?=$router->named('public:embed', ['station' => $station->getShortName()], [], true) ?>" frameborder="0" allowtransparency="true" style="width: 100%; min-height: 150px; border: 0;"></iframe></textarea>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#player_embed_url"><i class="zmdi zmdi-copy"></i> <?=__('Copy to Clipboard') ?></button>
</td>
</tr>
</tbody>
</table>
</section>
</div>
<div class="col-sm-6">
<?php if (count($stream_urls['local']) > 0 || count($stream_urls['remote']) > 0): ?>
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><?=__('Streams') ?></h2>
<ul class="actions">
<a href="" data-toggle="dropdown" aria-expanded="false">
<i class="zmdi zmdi-download"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
<li><a href="<?=$router->fromHere('public:playlist', ['format' => 'pls']) ?>"><?=__('Download %s', 'PLS') ?></a></li>
<li><a href="<?=$router->fromHere('public:playlist', ['format' => 'm3u']) ?>"><?=__('Download %s', 'M3U') ?></a></li>
</ul>
</ul>
</div>
<table class="table table-condensed table-striped">
<colgroup>
<col width="10%">
<col width="90%">
</colgroup>
<?php if (count($stream_urls['local']) > 0): ?>
<thead>
<tr>
<th colspan="2"><?=__('Local Streams') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($stream_urls['local'] as $stream_url): ?>
<tr class="input">
<td>
<a class="btn-audio" href="#" data-url="<?=$this->e($stream_url) ?>">
<i class="zmdi zmdi-play"></i>
</a>
</td>
<td>
<a href="<?=$this->e($stream_url) ?>"><?=$this->e($stream_url) ?></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<?php endif; ?>
<?php if (count($stream_urls['remote']) > 0): ?>
<thead>
<tr>
<th colspan="2"><?=__('Remote Relays') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($stream_urls['remote'] as $stream_url): ?>
<tr class="input">
<td>
<a class="btn-audio" href="#" data-url="<?=$this->e($stream_url) ?>">
<i class="zmdi zmdi-play"></i>
</a>
</td>
<td>
<a href="<?=$this->e($stream_url) ?>"><?=$this->e($stream_url) ?></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<?php endif; ?>
</table>
</section>
<?php endif; ?>
<?php if ($frontend_type !== \App\Radio\Adapters::FRONTEND_REMOTE): ?>
<section class="card" role="region">
<div class="card-header <?php if ($frontend_is_running): ?>bgm-green<?php else: ?>bgm-red<?php endif; ?> ch-alt">
<h2>
<?php if ($frontend_type === \App\Radio\Adapters::FRONTEND_ICECAST): ?>
<?=__('Icecast (Broadcasting Service)') ?>
<?php elseif ($frontend_type === \App\Radio\Adapters::FRONTEND_SHOUTCAST): ?>
<?=__('SHOUTcast DNAS 2 (Broadcasting Service)') ?>
<?php endif; ?>
<br><small><?php if ($frontend_is_running): ?><?=__('Running') ?><?php else: ?><?=__('Not Running') ?><?php endif; ?></small>
</h2>
<?php if ($acl->userAllowed($user, 'manage station broadcasting', $station->getId())): ?>
<ul class="actions">
<li class="dropdown">
<a href="" data-toggle="dropdown" aria-expanded="false">
<i class="zmdi zmdi-more-vert"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
<li><a class="api-call" href="<?=$router->fromHere('api:stations:frontend', ['do' => 'restart']) ?>"><?=__('Restart Service') ?></a></li>
<li><a class="api-call" href="<?=$router->fromHere('api:stations:frontend', ['do' => 'start']) ?>"><?=__('Start Service') ?></a></li>
<li><a class="api-call" href="<?=$router->fromHere('api:stations:frontend', ['do' => 'stop']) ?>"><?=__('Stop Service') ?></a></li>
</ul>
</li>
</ul>
<?php endif; ?>
</div>
<table class="table table-striped table-responsive">
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tbody>
<tr>
<td><?=__('Administration URL') ?></td>
<td><a href="<?=$this->e($frontend->getAdminUrl($station)) ?>"><?=$this->e($frontend->getAdminUrl($station)) ?></a></td>
</tr>
<tr>
<td><?=__('Administrator Password') ?></td>
<td>
<span id="frontend_admin_pw"><?=$this->e($frontend_config['admin_pw']) ?></span>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#frontend_admin_pw"><i class="zmdi zmdi-copy"></i><span class="sr-only"><?=__('Copy to Clipboard') ?></span></button>
</td>
</tr>
<tr>
<td><?=__('Source Password') ?></td>
<td>
<span id="frontend_source_pw"><?=$this->e($frontend_config['source_pw']) ?></span>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#frontend_source_pw"><i class="zmdi zmdi-copy"></i><span class="sr-only"><?=__('Copy to Clipboard') ?></span></button>
</td>
</tr>
<?php if ($frontend_type === \App\Radio\Adapters::FRONTEND_ICECAST): ?>
<tr>
<td><?=__('Relay Password') ?></td>
<td>
<span id="frontend_relay_pw"><?=$this->e($frontend_config['relay_pw']) ?></span>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#frontend_relay_pw"><i class="zmdi zmdi-copy"></i><span class="sr-only"><?=__('Copy to Clipboard') ?></span></button>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</section>
<?php endif; ?>
<?php if ($backend_type == 'none'): ?>
<section class="card" role="region">
<div class="card-header bgm-bluegray ch-alt">
<h2><?=__('AutoDJ Disabled') ?></h2>
</div>
<div class="card-body card-padding">
<p><?=__('AutoDJ has been disabled for this station. No music will automatically be played when a source is not live.') ?></p>
</div>
</section>
<?php else: ?>
<section class="card" role="region">
<div class="card-header <?php if ($backend_is_running): ?>bgm-green<?php else: ?>bgm-red<?php endif; ?> ch-alt">
<h2>
<?php if ($backend_type === \App\Radio\Adapters::BACKEND_LIQUIDSOAP): ?>
<?=__('Liquidsoap (AutoDJ Service)') ?><br>
<?php endif; ?>
<small><?php if ($backend_is_running): ?><?=__('Running') ?><?php else: ?><?=__('Not Running') ?><?php endif; ?></small>
</h2>
<?php if ($acl->userAllowed($user, 'manage station broadcasting', $station->getId())): ?>
<ul class="actions">
<li class="dropdown">
<a href="" data-toggle="dropdown" aria-expanded="false">
<i class="zmdi zmdi-more-vert"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
<li><a class="api-call" href="<?=$router->fromHere('api:stations:backend', ['do' => 'restart']) ?>"><?=__('Restart Service') ?></a></li>
<li><a class="api-call" href="<?=$router->fromHere('api:stations:backend', ['do' => 'start']) ?>"><?=__('Start Service') ?></a></li>
<li><a class="api-call" href="<?=$router->fromHere('api:stations:backend', ['do' => 'stop']) ?>"><?=__('Stop Service') ?></a></li>
</ul>
</li>
</ul>
<?php endif; ?>
</div>
<div class="card-body card-padding">
<p><?=sprintf(__('LiquidSoap is currently shuffling from <b>%d uploaded songs</b> in <b>%d playlists</b>.'), $num_songs, $num_playlists) ?></p>
<?php if ($acl->userAllowed($user, 'manage station media', $station->getId())): ?>
<div class="buttons">
<a class="btn btn-primary" href="<?=$router->fromHere('stations:files:index') ?>"><?=__('Music Files') ?></a>
<a class="btn btn-primary" href="<?=$router->fromHere('stations:playlists:index') ?>"><?=__('Playlists') ?></a>
</div>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
</div>
</div>

View File

@ -1,350 +0,0 @@
<?php
/** @var App\Entity\Station $station */
$this->layout('main', [
'title' => __('Profile'),
'manual' => true,
'sidebar_tab' => 'profile',
]);
/** @var \App\Assets $assets */
$assets
->load('vue')
->load('radio')
->load('clipboard')
->addInlineJs($this->fetch('stations/profile/index.js', ['nowplaying' => $nowplaying]), 99);
?>
<div class="row">
<div class="col-sm-6">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><?=__('On the Air') ?></h2>
</div>
<div class="card-body card-padding nowplaying" id="profile-nowplaying">
<div class="row">
<div class="col-sm-4">
<div class="m-l-25" style="position: relative;">
<div style="position: absolute; top: 0; left: -22px;"><i class="zmdi-hc-lg zmdi zmdi-collection-music"></i></div>
<h6><?=__('Now Playing') ?>:</h6>
<h4 class="media-heading">
{{ np.now_playing.song.title }}
</h4>
<div>
{{ np.now_playing.song.artist }}
</div>
<div class="nowplaying-progress" v-if="time_display">
{{ time_display }}
</div>
</div>
</div>
<div class="col-sm-4" v-if="!np.live.is_live && np.playing_next">
<div class="m-l-25" style="position: relative;">
<div style="position: absolute; top: 0; left: -22px;"><i class="zmdi-hc-lg zmdi zmdi-skip-next"></i></div>
<h6><?=__('Playing Next') ?>:</h6>
<h4 class="media-heading">
{{ np.playing_next.song.title }}
</h4>
<div>
{{ np.playing_next.song.artist }}
</div>
</div>
</div>
<div class="col-sm-4" v-if="np.live.is_live">
<div class="m-l-25" style="position: relative;">
<div style="position: absolute; top: 0; left: -22px;"><i class="zmdi-hc-lg zmdi zmdi-account"></i></div>
<h6><?=__('Now Streaming') ?>:</h6>
<h4 class="media-heading">
{{ np.live.streamer_name }}
</h4>
</div>
</div>
<div class="col-sm-3">
<div class="m-l-25" style="position: relative;">
<div style="position: absolute; top: 0; left: -22px;"><i class="zmdi-hc-lg zmdi zmdi-headset"></i></div>
<h6><?=__('Listeners') ?>:</h6>
<h4><span>{{ np.listeners.total }}</span> <?=__('Total') ?></h4>
<div>
<span>{{ np.listeners.unique }}</span> <?=__('Unique') ?>
</div>
</div>
</div>
</div>
<?php if ($backend_type == 'liquidsoap' && $acl->userAllowed($user, 'manage station broadcasting', $station->getId())): ?>
<div class="buttons pull-right p-t-15">
<a id="btn_skip_song" class="btn btn-sm btn-primary" role="button" href="<?=$router->fromHere('api:stations:backend', ['do' => 'skip']) ?>" v-if="!np.live.is_live"><i class="zmdi zmdi-skip-next"></i> <?=__('Skip Song') ?></a>
<a id="btn_disconnect_streamer" class="btn btn-sm btn-primary" role="button" href="<?=$router->fromHere('api:stations:backend', ['do' => 'disconnect']) ?>" v-if="np.live.is_live"><i class="zmdi zmdi-volume-off"></i> <?=__('Disconnect Streamer') ?></a>
</div>
<div class="clearfix"></div>
<?php endif; ?>
</div>
</section>
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><?=__('Profile') ?></h2>
<?php if ($acl->userAllowed($user, 'manage station profile', $station->getId())): ?>
<a class="btn bgm-blue btn-float" role="button" title="<?=__('Edit') ?>" href="<?=$router->fromHere('stations:profile:edit') ?>">
<span class="sr-only"><?=__('Edit') ?></span>
<i class="zmdi zmdi-edit"></i>
</a>
<?php endif; ?>
</div>
<table class="table table-striped table-responsive">
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tbody>
<tr>
<td><?=__('Name') ?></td>
<td><?=$this->e($station->getName()) ?></td>
</tr>
<tr>
<td><?=__('Description') ?></td>
<td><?=$this->e($station->getDescription()) ?></td>
</tr>
<?php if ($backend::supportsRequests()): ?>
<tr>
<td><?=__('Song Requests') ?></td>
<td>
<?php if ($station->getEnableRequests()): ?>
<span class="text-success"><?=__('Enabled') ?></span>
<div class="form-field">
<textarea id="request_embed_url" class="full-width form-control text-preformatted" spellcheck="false" style="height: 70px;"><iframe src="<?=$router->named('public:embedrequests', ['station' => $station->getShortName()], [], true) ?>" frameborder="0" allowtransparency="true" style="width: 100%; min-height: 850px; border: 0;"></iframe></textarea>
</div>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#request_embed_url"><i class="zmdi zmdi-copy"></i> <?=__('Copy to Clipboard') ?></button>
<?php else: ?>
<span class="text-danger"><?=__('Disabled') ?></span><br><small><?=__('Edit station profile to enable.') ?></small>
<?php endif; ?>
</td>
</tr>
<? endif; ?>
<?php if ($backend::supportsStreamers()): ?>
<tr>
<td><?=__('Streamers/DJs') ?></td>
<td>
<?php if ($station->getEnableStreamers()): ?>
<span class="text-success"><?=__('Enabled') ?></span><br>
<a href="<?=$router->fromHere('stations:streamers:index') ?>"><?=__('Manage streamer accounts') ?></a>
<?php else: ?>
<span class="text-danger"><?=__('Disabled') ?></span><br><small><?=__('Edit station profile to enable.') ?></small>
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
<tr>
<td><?=__('Base Directory') ?></td>
<td class="text-preformatted"><?=$this->e($station->getRadioBaseDir()) ?></td>
</tr>
<tr>
<td><?=__('Media Directory') ?></td>
<td class="text-preformatted"><?=$this->e($station->getRadioMediaDir()) ?></td>
</tr>
<tr>
<td><?=__('Player Embed Code') ?></td>
<td class="form-field">
<textarea id="player_embed_url" class="full-width form-control text-preformatted" spellcheck="false" style="height: 70px;"><iframe src="<?=$router->named('public:embed', ['station' => $station->getShortName()], [], true) ?>" frameborder="0" allowtransparency="true" style="width: 100%; min-height: 150px; border: 0;"></iframe></textarea>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#player_embed_url"><i class="zmdi zmdi-copy"></i> <?=__('Copy to Clipboard') ?></button>
</td>
</tr>
</tbody>
</table>
</section>
</div>
<div class="col-sm-6">
<?php if (count($stream_urls['local']) > 0 || count($stream_urls['remote']) > 0): ?>
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><?=__('Streams') ?></h2>
<ul class="actions">
<a href="" data-toggle="dropdown" aria-expanded="false">
<i class="zmdi zmdi-download"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
<li><a href="<?=$router->fromHere('public:playlist', ['format' => 'pls']) ?>"><?=__('Download %s', 'PLS') ?></a></li>
<li><a href="<?=$router->fromHere('public:playlist', ['format' => 'm3u']) ?>"><?=__('Download %s', 'M3U') ?></a></li>
</ul>
</ul>
</div>
<table class="table table-condensed table-striped">
<colgroup>
<col width="10%">
<col width="90%">
</colgroup>
<?php if (count($stream_urls['local']) > 0): ?>
<thead>
<tr>
<th colspan="2"><?=__('Local Streams') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($stream_urls['local'] as $stream_url): ?>
<tr class="input">
<td>
<a class="btn-audio" href="#" data-url="<?=$this->e($stream_url) ?>">
<i class="zmdi zmdi-play"></i>
</a>
</td>
<td>
<a href="<?=$this->e($stream_url) ?>"><?=$this->e($stream_url) ?></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<?php endif; ?>
<?php if (count($stream_urls['remote']) > 0): ?>
<thead>
<tr>
<th colspan="2"><?=__('Remote Relays') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($stream_urls['remote'] as $stream_url): ?>
<tr class="input">
<td>
<a class="btn-audio" href="#" data-url="<?=$this->e($stream_url) ?>">
<i class="zmdi zmdi-play"></i>
</a>
</td>
<td>
<a href="<?=$this->e($stream_url) ?>"><?=$this->e($stream_url) ?></a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<?php endif; ?>
</table>
</section>
<?php endif; ?>
<?php if ($frontend_type !== \App\Radio\Adapters::FRONTEND_REMOTE): ?>
<section class="card" role="region">
<div class="card-header <?php if ($frontend_is_running): ?>bgm-green<?php else: ?>bgm-red<?php endif; ?> ch-alt">
<h2>
<?php if ($frontend_type === \App\Radio\Adapters::FRONTEND_ICECAST): ?>
<?=__('Icecast (Broadcasting Service)') ?>
<?php elseif ($frontend_type === \App\Radio\Adapters::FRONTEND_SHOUTCAST): ?>
<?=__('SHOUTcast DNAS 2 (Broadcasting Service)') ?>
<?php endif; ?>
<br><small><?php if ($frontend_is_running): ?><?=__('Running') ?><?php else: ?><?=__('Not Running') ?><?php endif; ?></small>
</h2>
<?php if ($acl->userAllowed($user, 'manage station broadcasting', $station->getId())): ?>
<ul class="actions">
<li class="dropdown">
<a href="" data-toggle="dropdown" aria-expanded="false">
<i class="zmdi zmdi-more-vert"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
<li><a class="api-call" href="<?=$router->fromHere('api:stations:frontend', ['do' => 'restart']) ?>"><?=__('Restart Service') ?></a></li>
<li><a class="api-call" href="<?=$router->fromHere('api:stations:frontend', ['do' => 'start']) ?>"><?=__('Start Service') ?></a></li>
<li><a class="api-call" href="<?=$router->fromHere('api:stations:frontend', ['do' => 'stop']) ?>"><?=__('Stop Service') ?></a></li>
</ul>
</li>
</ul>
<?php endif; ?>
</div>
<table class="table table-striped table-responsive">
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tbody>
<tr>
<td><?=__('Administration URL') ?></td>
<td><a href="<?=$this->e($frontend->getAdminUrl($station)) ?>"><?=$this->e($frontend->getAdminUrl($station)) ?></a></td>
</tr>
<tr>
<td><?=__('Administrator Password') ?></td>
<td>
<span id="frontend_admin_pw"><?=$this->e($frontend_config['admin_pw']) ?></span>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#frontend_admin_pw"><i class="zmdi zmdi-copy"></i><span class="sr-only"><?=__('Copy to Clipboard') ?></span></button>
</td>
</tr>
<tr>
<td><?=__('Source Password') ?></td>
<td>
<span id="frontend_source_pw"><?=$this->e($frontend_config['source_pw']) ?></span>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#frontend_source_pw"><i class="zmdi zmdi-copy"></i><span class="sr-only"><?=__('Copy to Clipboard') ?></span></button>
</td>
</tr>
<?php if ($frontend_type === \App\Radio\Adapters::FRONTEND_ICECAST): ?>
<tr>
<td><?=__('Relay Password') ?></td>
<td>
<span id="frontend_relay_pw"><?=$this->e($frontend_config['relay_pw']) ?></span>
<button class="btn btn-copy btn-link btn-xs" data-clipboard-target="#frontend_relay_pw"><i class="zmdi zmdi-copy"></i><span class="sr-only"><?=__('Copy to Clipboard') ?></span></button>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</section>
<?php endif; ?>
<?php if ($backend_type == 'none'): ?>
<section class="card" role="region">
<div class="card-header bgm-bluegray ch-alt">
<h2><?=__('AutoDJ Disabled') ?></h2>
</div>
<div class="card-body card-padding">
<p><?=__('AutoDJ has been disabled for this station. No music will automatically be played when a source is not live.') ?></p>
</div>
</section>
<?php else: ?>
<section class="card" role="region">
<div class="card-header <?php if ($backend_is_running): ?>bgm-green<?php else: ?>bgm-red<?php endif; ?> ch-alt">
<h2>
<?php if ($backend_type === \App\Radio\Adapters::BACKEND_LIQUIDSOAP): ?>
<?=__('Liquidsoap (AutoDJ Service)') ?><br>
<?php endif; ?>
<small><?php if ($backend_is_running): ?><?=__('Running') ?><?php else: ?><?=__('Not Running') ?><?php endif; ?></small>
</h2>
<?php if ($acl->userAllowed($user, 'manage station broadcasting', $station->getId())): ?>
<ul class="actions">
<li class="dropdown">
<a href="" data-toggle="dropdown" aria-expanded="false">
<i class="zmdi zmdi-more-vert"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right">
<li><a class="api-call" href="<?=$router->fromHere('api:stations:backend', ['do' => 'restart']) ?>"><?=__('Restart Service') ?></a></li>
<li><a class="api-call" href="<?=$router->fromHere('api:stations:backend', ['do' => 'start']) ?>"><?=__('Start Service') ?></a></li>
<li><a class="api-call" href="<?=$router->fromHere('api:stations:backend', ['do' => 'stop']) ?>"><?=__('Stop Service') ?></a></li>
</ul>
</li>
</ul>
<?php endif; ?>
</div>
<div class="card-body card-padding">
<p><?=sprintf(__('LiquidSoap is currently shuffling from <b>%d uploaded songs</b> in <b>%d playlists</b>.'), $num_songs, $num_playlists) ?></p>
<?php if ($acl->userAllowed($user, 'manage station media', $station->getId())): ?>
<div class="buttons">
<a class="btn btn-primary" href="<?=$router->fromHere('stations:files:index') ?>"><?=__('Music Files') ?></a>
<a class="btn btn-primary" href="<?=$router->fromHere('stations:playlists:index') ?>"><?=__('Playlists') ?></a>
</div>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
</div>
</div>

View File

@ -1,7 +1,11 @@
<div id="listeners_by_day" style="height: 250px;"></div>
<script type="text/javascript" nonce="<?=$assets->getCspNonce() ?>">
$(function () {
Highcharts.setOptions({
global: {
timezoneOffset: <?=\App\Timezone::getOffsetMinutes() ?>,
useUTC: false
}
});
$('#listeners_by_day').highcharts({
chart: {
zoomType: 'x'
@ -51,7 +55,7 @@ $(function () {
'#2f7ed8',
'#000000'
],
series: [{
name: <?=$this->escapeJs(__('Daily Listener Range')) ?>,
type: 'arearange',
@ -66,5 +70,56 @@ $(function () {
data: <?=$daily_averages ?>
}]
});
$('#listeners_by_day_of_week').highcharts({
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false
},
title: { text:'' },
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
color: '#000000',
connectorColor: '#000000',
format: '<b>{point.name}</b>: {point.percentage:.1f} %'
}
}
},
series: [{
type: 'pie',
name: <?=$this->escapeJs(__('Listeners')) ?>,
data: <?=$day_of_week_stats ?>
}]
});
$('#listeners_by_hour').highcharts({
chart: {},
title: { text:'' },
xAxis: {
allowDecimals: false,
tickInterval: 1
},
yAxis: {
title: {
text: '<?=__('Listeners') ?>'
},
min: 0
},
tooltip: {
crosshairs: true,
shared: true
},
series: [{
name: <?=$this->escapeJs(__('Average Listeners')) ?>,
type: 'column',
data: <?=$averages_by_hour ?>
}]
});
});
</script>

View File

@ -0,0 +1,164 @@
<?php $this->layout('main', ['title' => __('Statistics Overview'), 'manual' => true]) ?>
<?php
/** @var \App\Assets $assets */
$assets
->load('highcharts')
->load('highcharts_theme_'.$customization->getTheme())
->addInlineJs($this->fetch('stations/reports/overview.js', [
'daily_ranges' => $daily_ranges,
'hourly_averages' => $hourly_averages,
'daily_averages' => $daily_averages,
'day_of_week_stats' => $day_of_week_stats,
'averages_by_hour' => $averages_by_hour,
]));
?>
<div class="row">
<div class="col-sm-12">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><?=__('Statistics Overview') ?></h2>
</div>
<div role="tabpanel">
<ul class="tab-nav" role="tablist">
<li class="p-l-10 active"><a role="tab" data-toggle="tab" aria-expanded="true" aria-controls="listeners-by-day" href="#listeners-by-day"><?=__('Listeners by Day') ?></a></li>
<li><a role="tab" data-toggle="tab" aria-controls="listeners-by-hour" href="#listeners-by-hour"><?=__('Listeners by Hour') ?></a></li>
<li><a role="tab" data-toggle="tab" aria-controls="listeners-by-day-of-week" href="#listeners-by-day-of-week"><?=__('Listeners by Day of Week') ?></a></li>
</ul>
<div class="tab-content">
<div class="tab-pane card-body card-padding active" id="listeners-by-day" role="tabpanel">
<div id="listeners_by_day" style="height: 250px;"></div>
</div>
<div class="tab-pane card-padding" id="listeners-by-hour" role="tabpanel">
<div id="listeners_by_hour" style="height: 250px;"></div>
</div>
<div class="tab-pane card-padding" id="listeners-by-day-of-week" role="tabpanel">
<div id="listeners_by_day_of_week" style="height: 250px;"></div>
</div>
</div>
</div>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-6">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><i class="zmdi zmdi-thumb-up"></i> <?=__('Best Performing Songs') ?> <small><?=__('in the last 48 hours') ?></small></h2>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-nopadding">
<colgroup>
<col width="20%">
<col width="80%">
</colgroup>
<thead>
<tr>
<th><?=__('Change') ?></th>
<th><?=__('Song') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($best_performing_songs as $song_row): ?>
<tr>
<td class="text-center text-success">
<i class="zmdi zmdi-thumb-up"></i> <?=abs($song_row['stat_delta']) ?><br>
<small><?=$song_row['stat_start'] ?> to <?=$song_row['stat_end'] ?>
</td>
<td>
<?php if ($song_row['song']['title']): ?>
<b><?=$song_row['song']['title'] ?></b><br>
<?=$song_row['song']['artist'] ?>
<?php else: ?>
<?=$song_row['song']['text'] ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
</div>
<div class="col-sm-6">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><i class="zmdi zmdi-thumb-down"></i> <?=__('Worst Performing Songs') ?> <small><?=__('in the last 48 hours') ?></small></h2>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-nopadding">
<colgroup>
<col width="20%">
<col width="80%">
</colgroup>
<thead>
<tr>
<th><?=__('Change') ?></th>
<th><?=__('Song') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($worst_performing_songs as $song_row): ?>
<tr>
<td class="text-center text-danger">
<i class="zmdi zmdi-thumb-down"></i> <?=abs($song_row['stat_delta']) ?><br>
<small><?=$song_row['stat_start'] ?> to <?=$song_row['stat_end'] ?>
</td>
<td>
<?php if ($song_row['song']['title']): ?>
<b><?=$song_row['song']['title'] ?></b><br>
<?=$song_row['song']['artist'] ?>
<?php else: ?>
<?=$song_row['song']['text'] ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<section class="card" role="region">
<div class="card-header ch-alt">
<h2><?=__('Most Played Songs') ?> <small><?=__('in the last month') ?></small></h2>
</div>
<div class="table-responsive">
<table class="table table-striped table-condensed table-nopadding">
<colgroup>
<col width="10%">
<col width="90%">
</colgroup>
<thead>
<tr>
<th><?=__('Plays') ?></th>
<th><?=__('Song') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($song_totals['played'] as $song_row): ?>
<tr>
<td class="text-center"><?=$song_row['records'] ?></td>
<td>
<?php if ($song_row['song']['title']): ?>
<b><?=$song_row['song']['title'] ?></b><br>
<?=$song_row['song']['artist'] ?>
<?php else: ?>
<?=$song_row['song']['text'] ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
</div>
</div>

View File

@ -34,10 +34,6 @@
<li><a href="<?=$router->named('public:index', ['station' => $station->getShortName()]) ?>" target="_blank"><i class="zmdi zmdi-globe"></i> <?=__('Public Page') ?></a></li>
<?php endif; ?>
<li <?php if ($active_tab === 'profile'): ?>class="active"<?php endif; ?>>
<a href="<?=$router->fromHere('stations:profile:index') ?>"><i class="zmdi zmdi-image"></i> <?=__('Profile') ?></a>
</li>
<?php if ($acl->userAllowed($user, 'manage station media', $station->getId()) && $backend::supportsMedia()): ?>
<li <?php if ($active_tab === 'files'): ?>class="active"<?php endif; ?>>
<a href="<?=$router->fromHere('stations:files:index') ?>"><i class="zmdi zmdi-collection-music"></i> <?=__('Music Files') ?></a>
@ -76,6 +72,7 @@
<a href="" data-ma-action="submenu-toggle"><i class="zmdi zmdi-assignment"></i> <?=__('Reports') ?></a>
<ul>
<li><a href="<?=$router->fromHere('stations:reports:overview') ?>"><?=__('Statistics Overview') ?></a></li>
<?php if ($frontend::supportsListenerDetail()): ?>
<li><a href="<?=$router->fromHere('stations:reports:listeners') ?>"><?=__('Listeners') ?></a></li>
<?php endif; ?>

View File

@ -11,7 +11,7 @@ use App\Entity;
use App\Http\Request;
use App\Http\Response;
class ProfileController
class EditProfileController
{
/** @var EntityManager */
protected $em;
@ -50,84 +50,7 @@ class ProfileController
$this->station_repo = $em->getRepository(Entity\Station::class);
}
public function indexAction(Request $request, Response $response): Response
{
$station = $request->getStation();
$view = $request->getView();
if (!$station->isEnabled()) {
return $view->renderToResponse($response, 'stations/profile/disabled');
}
$backend = $request->getStationBackend();
$frontend = $request->getStationFrontend();
$remotes = $request->getStationRemotes();
$stream_urls = [
'local' => $frontend->getStreamUrls($station),
'remote' => [],
];
foreach($remotes as $ra_proxy) {
$stream_urls['remote'][] = $ra_proxy->getAdapter()->getPublicUrl($ra_proxy->getRemote());
}
// Statistics about backend playback.
$num_songs = $this->em->createQuery('SELECT COUNT(sm.id) FROM '.Entity\StationMedia::class.' sm LEFT JOIN sm.playlist_items spm LEFT JOIN spm.playlist sp WHERE sp.id IS NOT NULL AND sm.station_id = :station_id')
->setParameter('station_id', $station->getId())
->getSingleScalarResult();
$num_playlists = $this->em->createQuery('SELECT COUNT(sp.id) FROM '.Entity\StationPlaylist::class.' sp WHERE sp.station_id = :station_id')
->setParameter('station_id', $station->getId())
->getSingleScalarResult();
// Populate initial nowplaying data.
$np = [
'now_playing' => [
'song' => [
'title' => __('Song Title'),
'artist' => __('Song Artist'),
],
'is_request' => false,
'elapsed' => 0,
'duration' => 0,
],
'listeners' => [
'unique' => 0,
'total' => 0,
],
'live' => [
'is_live' => false,
'streamer_name' => '',
],
'playing_next' => [
'song' => [
'title' => __('Song Title'),
'artist' => __('Song Artist'),
],
],
];
$station_np = $station->getNowplaying();
if ($station_np instanceof Entity\Api\NowPlaying) {
$np = array_intersect_key($station_np->toArray(), $np) + $np;
}
return $view->renderToResponse($response, 'stations/profile/index', [
'num_songs' => $num_songs,
'num_playlists' => $num_playlists,
'stream_urls' => $stream_urls,
'backend_type' => $station->getBackendType(),
'backend_config' => (array)$station->getBackendConfig(),
'backend_is_running' => $backend->isRunning($station),
'frontend_type' => $station->getFrontendType(),
'frontend_config' => (array)$station->getFrontendConfig(),
'frontend_is_running' => $frontend->isRunning($station),
'nowplaying' => $np,
]);
}
public function editAction(Request $request, Response $response, $station_id): Response
public function __invoke(Request $request, Response $response, $station_id): Response
{
$station = $request->getStation();
$frontend = $request->getStationFrontend();
@ -185,7 +108,7 @@ class ProfileController
// Clear station cache.
$this->cache->remove('stations');
return $response->withRedirect($request->getRouter()->fromHere('stations:profile:index'));
return $response->withRedirect($request->getRouter()->fromHere('stations:index:index'));
}
return $request->getView()->renderToResponse($response, 'stations/profile/edit', [

View File

@ -1,181 +1,105 @@
<?php
namespace App\Controller\Stations;
use App\Cache;
use Doctrine\ORM\EntityManager;
use App\Entity;
use App\Http\Request;
use App\Http\Response;
use InfluxDB\Database;
class IndexController
{
/** @var EntityManager */
protected $em;
/** @var Database */
protected $influx;
/** @var Entity\Repository\StationRepository */
protected $station_repo;
/**
* @param EntityManager $em
* @param Database $influx
* @see \App\Provider\StationsProvider
*/
public function __construct(EntityManager $em, Database $influx)
public function __construct(
EntityManager $em
)
{
$this->em = $em;
$this->influx = $influx;
$this->station_repo = $em->getRepository(Entity\Station::class);
}
public function indexAction(Request $request, Response $response, $station_id): Response
public function __invoke(Request $request, Response $response): Response
{
$station = $request->getStation();
$view = $request->getView();
// Get current analytics level.
/** @var Entity\Repository\SettingsRepository $settings_repo */
$settings_repo = $this->em->getRepository(Entity\Settings::class);
$analytics_level = $settings_repo->getSetting('analytics', Entity\Analytics::LEVEL_ALL);
if ($analytics_level === Entity\Analytics::LEVEL_NONE) {
// The entirety of the dashboard can't be shown, so redirect user to the profile page.
return $response->withRedirect($request->getRouter()->named('stations:profile:index', ['station' => $station_id]));
if (!$station->isEnabled()) {
return $view->renderToResponse($response, 'stations/index/disabled');
}
/* Statistics */
$threshold = strtotime('-1 month');
$backend = $request->getStationBackend();
$frontend = $request->getStationFrontend();
$remotes = $request->getStationRemotes();
// Statistics by day.
$resultset = $this->influx->query('SELECT * FROM "1d"."station.' . $station->getId() . '.listeners" WHERE time > now() - 30d',
[
'epoch' => 'ms',
]);
$stream_urls = [
'local' => $frontend->getStreamUrls($station),
'remote' => [],
];
$daily_stats = $resultset->getPoints();
$daily_ranges = [];
$daily_averages = [];
$days_of_week = [];
foreach ($daily_stats as $stat) {
// Add 12 hours to statistics so they always land inside the day they represent.
$stat['time'] = $stat['time'] + (60 * 60 * 12 * 1000);
$daily_ranges[] = [$stat['time'], $stat['min'], $stat['max']];
$daily_averages[] = [$stat['time'], round($stat['value'], 2)];
$day_of_week = date('l', round($stat['time'] / 1000));
$days_of_week[$day_of_week][] = $stat['value'];
foreach($remotes as $ra_proxy) {
$stream_urls['remote'][] = $ra_proxy->getAdapter()->getPublicUrl($ra_proxy->getRemote());
}
$day_of_week_stats = [];
foreach ($days_of_week as $day_name => $day_totals) {
$day_of_week_stats[] = [$day_name, round(array_sum($day_totals) / count($day_totals), 2)];
}
// Statistics by hour.
$resultset = $this->influx->query('SELECT * FROM "1h"."station.' . $station->getId() . '.listeners"', [
'epoch' => 'ms',
]);
$hourly_stats = $resultset->getPoints();
$hourly_averages = [];
$hourly_ranges = [];
$totals_by_hour = [];
foreach ($hourly_stats as $stat) {
$hourly_ranges[] = [$stat['time'], $stat['min'], $stat['max']];
$hourly_averages[] = [$stat['time'], round($stat['value'], 2)];
$hour = date('G', round($stat['time'] / 1000));
$totals_by_hour[$hour][] = $stat['value'];
}
$averages_by_hour = [];
for ($i = 0; $i < 24; $i++) {
$totals = $totals_by_hour[$i] ?: [0];
$averages_by_hour[] = [$i . ':00', round(array_sum($totals) / count($totals), 2)];
}
/* Play Count Statistics */
$song_totals_raw = [];
$song_totals_raw['played'] = $this->em->createQuery('SELECT sh.song_id, COUNT(sh.id) AS records
FROM '.Entity\SongHistory::class.' sh
WHERE sh.station_id = :station_id AND sh.timestamp_start >= :timestamp
GROUP BY sh.song_id
ORDER BY records DESC')
// Statistics about backend playback.
$num_songs = $this->em->createQuery('SELECT COUNT(sm.id) FROM '.Entity\StationMedia::class.' sm LEFT JOIN sm.playlist_items spm LEFT JOIN spm.playlist sp WHERE sp.id IS NOT NULL AND sm.station_id = :station_id')
->setParameter('station_id', $station->getId())
->setParameter('timestamp', $threshold)
->setMaxResults(40)
->getArrayResult();
->getSingleScalarResult();
// Compile the above data.
$song_totals = [];
foreach ($song_totals_raw as $total_type => $total_records) {
foreach ($total_records as $total_record) {
$song = $this->em->getRepository(Entity\Song::class)->findAsArray($total_record['song_id']);
$total_record['song'] = $song;
$song_totals[$total_type][] = $total_record;
}
$song_totals[$total_type] = array_slice((array)$song_totals[$total_type], 0, 10, true);
}
/* Song "Deltas" (Changes in Listener Count) */
$threshold = strtotime('-2 weeks');
// Get all songs played in timeline.
$songs_played_raw = $this->em->createQuery('SELECT sh, s
FROM '.Entity\SongHistory::class.' sh
LEFT JOIN sh.song s
WHERE sh.station_id = :station_id AND sh.timestamp_start >= :timestamp AND sh.listeners_start IS NOT NULL
ORDER BY sh.timestamp_start ASC')
$num_playlists = $this->em->createQuery('SELECT COUNT(sp.id) FROM '.Entity\StationPlaylist::class.' sp WHERE sp.station_id = :station_id')
->setParameter('station_id', $station->getId())
->setParameter('timestamp', $threshold)
->getArrayResult();
->getSingleScalarResult();
$songs_played_raw = array_values($songs_played_raw);
$songs = [];
// Populate initial nowplaying data.
$np = [
'now_playing' => [
'song' => [
'title' => __('Song Title'),
'artist' => __('Song Artist'),
],
'is_request' => false,
'elapsed' => 0,
'duration' => 0,
],
'listeners' => [
'unique' => 0,
'total' => 0,
],
'live' => [
'is_live' => false,
'streamer_name' => '',
],
'playing_next' => [
'song' => [
'title' => __('Song Title'),
'artist' => __('Song Artist'),
],
],
];
foreach ($songs_played_raw as $i => $song_row) {
// Song has no recorded ending.
if ($song_row['timestamp_end'] == 0) {
continue;
}
$song_row['stat_start'] = $song_row['listeners_start'];
$song_row['stat_end'] = $song_row['listeners_end'];
$song_row['stat_delta'] = $song_row['delta_total'];
$songs[] = $song_row;
$station_np = $station->getNowplaying();
if ($station_np instanceof Entity\Api\NowPlaying) {
$np = array_intersect_key($station_np->toArray(), $np) + $np;
}
usort($songs, function ($a_arr, $b_arr) {
$a = $a_arr['stat_delta'];
$b = $b_arr['stat_delta'];
if ($a == $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
});
return $request->getView()->renderToResponse($response, 'stations/index/index', [
'day_of_week_stats' => json_encode((array)$day_of_week_stats),
'daily_ranges' => json_encode((array)$daily_ranges),
'daily_averages' => json_encode((array)$daily_averages),
'hourly_ranges' => json_encode((array)$hourly_ranges),
'hourly_averages' => json_encode((array)$hourly_averages),
'averages_by_hour' => json_encode((array)$averages_by_hour),
'song_totals' => $song_totals,
'best_performing_songs' => \array_reverse(\array_slice($songs, -5)),
'worst_performing_songs' => \array_slice($songs, 0, 5),
return $view->renderToResponse($response, 'stations/index/index', [
'num_songs' => $num_songs,
'num_playlists' => $num_playlists,
'stream_urls' => $stream_urls,
'backend_type' => $station->getBackendType(),
'backend_config' => (array)$station->getBackendConfig(),
'backend_is_running' => $backend->isRunning($station),
'frontend_type' => $station->getFrontendType(),
'frontend_config' => (array)$station->getFrontendConfig(),
'frontend_is_running' => $frontend->isRunning($station),
'nowplaying' => $np,
]);
}
}

View File

@ -0,0 +1,182 @@
<?php
namespace App\Controller\Stations\Reports;
use App\Cache;
use Doctrine\ORM\EntityManager;
use App\Entity;
use App\Http\Request;
use App\Http\Response;
use InfluxDB\Database;
class OverviewController
{
/** @var EntityManager */
protected $em;
/** @var Database */
protected $influx;
/**
* @param EntityManager $em
* @param Database $influx
* @see \App\Provider\StationsProvider
*/
public function __construct(EntityManager $em, Database $influx)
{
$this->em = $em;
$this->influx = $influx;
}
public function __invoke(Request $request, Response $response, $station_id): Response
{
$station = $request->getStation();
// Get current analytics level.
/** @var Entity\Repository\SettingsRepository $settings_repo */
$settings_repo = $this->em->getRepository(Entity\Settings::class);
$analytics_level = $settings_repo->getSetting('analytics', Entity\Analytics::LEVEL_ALL);
if ($analytics_level === Entity\Analytics::LEVEL_NONE) {
// The entirety of the dashboard can't be shown, so redirect user to the profile page.
return $request->getView()->renderToResponse($response, 'stations/reports/restricted');
}
/* Statistics */
$threshold = strtotime('-1 month');
// Statistics by day.
$resultset = $this->influx->query('SELECT * FROM "1d"."station.' . $station->getId() . '.listeners" WHERE time > now() - 30d',
[
'epoch' => 'ms',
]);
$daily_stats = $resultset->getPoints();
$daily_ranges = [];
$daily_averages = [];
$days_of_week = [];
foreach ($daily_stats as $stat) {
// Add 12 hours to statistics so they always land inside the day they represent.
$stat['time'] = $stat['time'] + (60 * 60 * 12 * 1000);
$daily_ranges[] = [$stat['time'], $stat['min'], $stat['max']];
$daily_averages[] = [$stat['time'], round($stat['value'], 2)];
$day_of_week = date('l', round($stat['time'] / 1000));
$days_of_week[$day_of_week][] = $stat['value'];
}
$day_of_week_stats = [];
foreach ($days_of_week as $day_name => $day_totals) {
$day_of_week_stats[] = [$day_name, round(array_sum($day_totals) / count($day_totals), 2)];
}
// Statistics by hour.
$resultset = $this->influx->query('SELECT * FROM "1h"."station.' . $station->getId() . '.listeners"', [
'epoch' => 'ms',
]);
$hourly_stats = $resultset->getPoints();
$hourly_averages = [];
$hourly_ranges = [];
$totals_by_hour = [];
foreach ($hourly_stats as $stat) {
$hourly_ranges[] = [$stat['time'], $stat['min'], $stat['max']];
$hourly_averages[] = [$stat['time'], round($stat['value'], 2)];
$hour = date('G', round($stat['time'] / 1000));
$totals_by_hour[$hour][] = $stat['value'];
}
$averages_by_hour = [];
for ($i = 0; $i < 24; $i++) {
$totals = $totals_by_hour[$i] ?: [0];
$averages_by_hour[] = [$i . ':00', round(array_sum($totals) / count($totals), 2)];
}
/* Play Count Statistics */
$song_totals_raw = [];
$song_totals_raw['played'] = $this->em->createQuery('SELECT sh.song_id, COUNT(sh.id) AS records
FROM '.Entity\SongHistory::class.' sh
WHERE sh.station_id = :station_id AND sh.timestamp_start >= :timestamp
GROUP BY sh.song_id
ORDER BY records DESC')
->setParameter('station_id', $station->getId())
->setParameter('timestamp', $threshold)
->setMaxResults(40)
->getArrayResult();
// Compile the above data.
$song_totals = [];
foreach ($song_totals_raw as $total_type => $total_records) {
foreach ($total_records as $total_record) {
$song = $this->em->getRepository(Entity\Song::class)->findAsArray($total_record['song_id']);
$total_record['song'] = $song;
$song_totals[$total_type][] = $total_record;
}
$song_totals[$total_type] = array_slice((array)$song_totals[$total_type], 0, 10, true);
}
/* Song "Deltas" (Changes in Listener Count) */
$threshold = strtotime('-2 weeks');
// Get all songs played in timeline.
$songs_played_raw = $this->em->createQuery('SELECT sh, s
FROM '.Entity\SongHistory::class.' sh
LEFT JOIN sh.song s
WHERE sh.station_id = :station_id AND sh.timestamp_start >= :timestamp AND sh.listeners_start IS NOT NULL
ORDER BY sh.timestamp_start ASC')
->setParameter('station_id', $station->getId())
->setParameter('timestamp', $threshold)
->getArrayResult();
$songs_played_raw = array_values($songs_played_raw);
$songs = [];
foreach ($songs_played_raw as $i => $song_row) {
// Song has no recorded ending.
if ($song_row['timestamp_end'] == 0) {
continue;
}
$song_row['stat_start'] = $song_row['listeners_start'];
$song_row['stat_end'] = $song_row['listeners_end'];
$song_row['stat_delta'] = $song_row['delta_total'];
$songs[] = $song_row;
}
usort($songs, function ($a_arr, $b_arr) {
$a = $a_arr['stat_delta'];
$b = $b_arr['stat_delta'];
if ($a == $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
});
return $request->getView()->renderToResponse($response, 'stations/reports/overview', [
'day_of_week_stats' => json_encode((array)$day_of_week_stats),
'daily_ranges' => json_encode((array)$daily_ranges),
'daily_averages' => json_encode((array)$daily_averages),
'hourly_ranges' => json_encode((array)$hourly_ranges),
'hourly_averages' => json_encode((array)$hourly_averages),
'averages_by_hour' => json_encode((array)$averages_by_hour),
'song_totals' => $song_totals,
'best_performing_songs' => \array_reverse(\array_slice($songs, -5)),
'worst_performing_songs' => \array_slice($songs, 0, 5),
]);
}
}

View File

@ -20,6 +20,18 @@ class StationsProvider implements ServiceProviderInterface
);
};
$di[Stations\EditProfileController::class] = function($di) {
/** @var \App\Config $config */
$config = $di[\App\Config::class];
return new Stations\EditProfileController(
$di[\Doctrine\ORM\EntityManager::class],
$di[\App\Cache::class],
$di[\App\Radio\Configuration::class],
$config->get('forms/station')
);
};
$di[Stations\Files\FilesController::class] = function($di) {
/** @var \App\Config $config */
$config = $di[\App\Config::class];
@ -52,6 +64,12 @@ class StationsProvider implements ServiceProviderInterface
$di[Stations\IndexController::class] = function($di) {
return new Stations\IndexController(
$di[\Doctrine\ORM\EntityManager::class]
);
};
$di[Stations\Reports\OverviewController::class] = function($di) {
return new Stations\Reports\OverviewController(
$di[\Doctrine\ORM\EntityManager::class],
$di[\InfluxDB\Database::class]
);
@ -84,18 +102,6 @@ class StationsProvider implements ServiceProviderInterface
);
};
$di[Stations\ProfileController::class] = function($di) {
/** @var \App\Config $config */
$config = $di[\App\Config::class];
return new Stations\ProfileController(
$di[\Doctrine\ORM\EntityManager::class],
$di[\App\Cache::class],
$di[\App\Radio\Configuration::class],
$config->get('forms/station')
);
};
$di[Stations\RemotesController::class] = function($di) {
/** @var \App\Config $config */
$config = $di[\App\Config::class];