Make timezone Station-specific and remove now-unused offset code.
This commit is contained in:
parent
6e618ef595
commit
48427d46a3
|
@ -281,18 +281,6 @@ return [
|
|||
]
|
||||
],
|
||||
|
||||
'schedule_tz' => [
|
||||
'select',
|
||||
[
|
||||
'label' => __('Time Zone'),
|
||||
'label_class' => 'mb-2',
|
||||
'description' => __('The time zone for these scheduled times and the days specified below.'),
|
||||
'options' => \Azura\Timezone::fetchSelect(),
|
||||
'default' => $customization->getTimeZone(),
|
||||
'form_group_class' => 'col-md-6 mt-1',
|
||||
]
|
||||
],
|
||||
|
||||
'schedule_days' => [
|
||||
'checkbox',
|
||||
[
|
||||
|
@ -307,7 +295,7 @@ return [
|
|||
6 => __('Saturday'),
|
||||
7 => __('Sunday'),
|
||||
],
|
||||
'form_group_class' => 'col-md-6 mt-3',
|
||||
'form_group_class' => 'col-md-12 mt-3',
|
||||
]
|
||||
],
|
||||
|
||||
|
|
|
@ -89,19 +89,6 @@ return [
|
|||
'legend_class' => 'col-sm-12',
|
||||
'elements' => [
|
||||
|
||||
'timezone' => [
|
||||
'select',
|
||||
[
|
||||
'label' => __('Time Zone'),
|
||||
'description' => __('All times displayed on the site will be based on this time zone.') . '<br>' . sprintf(__('Current server time is <b>%s</b>.'),
|
||||
date('g:ia')),
|
||||
'options' => \Azura\Timezone::fetchSelect(),
|
||||
'default' => \App\Customization::DEFAULT_TIMEZONE,
|
||||
'label_class' => 'mb-2',
|
||||
'form_group_class' => 'col-sm-12 mt-3',
|
||||
]
|
||||
],
|
||||
|
||||
'locale' => [
|
||||
'radio',
|
||||
[
|
||||
|
|
|
@ -31,18 +31,6 @@ return [
|
|||
],
|
||||
],
|
||||
|
||||
Entity\Settings::TIMEZONE => [
|
||||
'select',
|
||||
[
|
||||
'label' => __('System Default Time Zone'),
|
||||
'description' => __('For users who have not customized their time zone, all times displayed on the site will be based on this time zone.'),
|
||||
'options' => \Azura\Timezone::fetchSelect(),
|
||||
'default' => 'UTC',
|
||||
'label_class' => 'mb-2',
|
||||
'form_group_class' => 'col-sm-12 mt-3',
|
||||
],
|
||||
],
|
||||
|
||||
Entity\Settings::PREFER_BROWSER_URL => [
|
||||
'toggle',
|
||||
[
|
||||
|
|
|
@ -61,6 +61,18 @@ return [
|
|||
]
|
||||
],
|
||||
|
||||
'timezone' => [
|
||||
'select',
|
||||
[
|
||||
'label' => __('Time Zone'),
|
||||
'description' => __('Scheduled playlists and other timed items will be controlled by this time zone.'),
|
||||
'options' => \Azura\Timezone::fetchSelect(),
|
||||
'default' => \App\Customization::DEFAULT_TIMEZONE,
|
||||
'label_class' => 'mb-2',
|
||||
'form_group_class' => 'col-sm-12 mt-3',
|
||||
]
|
||||
],
|
||||
|
||||
'enable_public_page' => [
|
||||
'toggle',
|
||||
[
|
||||
|
|
|
@ -52,6 +52,9 @@ class PlaylistsController extends AbstractStationCrudController
|
|||
}
|
||||
}
|
||||
|
||||
$tz = new \DateTimeZone($station->getTimezone());
|
||||
$now = Chronos::now($tz);
|
||||
|
||||
$playlists = [];
|
||||
|
||||
$songs_query = $this->em->createQuery(/** @lang DQL */'
|
||||
|
@ -63,8 +66,16 @@ class PlaylistsController extends AbstractStationCrudController
|
|||
foreach ($all_playlists as $playlist) {
|
||||
$playlist_row = $this->record_repo->toArray($playlist);
|
||||
|
||||
if ($playlist->getIsEnabled() && $playlist->getType() === 'default') {
|
||||
$playlist_row['probability'] = round(($playlist->getWeight() / $total_weights) * 100, 1) . '%';
|
||||
if ($playlist->getIsEnabled()) {
|
||||
if (Entity\StationPlaylist::TYPE_DEFAULT === $playlist->getType()) {
|
||||
$playlist_row['probability'] = round(($playlist->getWeight() / $total_weights) * 100, 1) . '%';
|
||||
} elseif (Entity\StationPlaylist::TYPE_SCHEDULED === $playlist->getType()) {
|
||||
$schedule_start = Entity\StationPlaylist::getDateTime($playlist->getScheduleStartTime(), $now);
|
||||
$schedule_end = Entity\StationPlaylist::getDateTime($playlist->getScheduleEndTime(), $now);
|
||||
|
||||
$playlist_row['schedule_start'] = $schedule_start->toIso8601String();
|
||||
$playlist_row['schedule_end'] = $schedule_end->toIso8601String();
|
||||
}
|
||||
}
|
||||
|
||||
$song_totals = $songs_query->setParameter('playlist', $playlist)
|
||||
|
@ -93,22 +104,20 @@ class PlaylistsController extends AbstractStationCrudController
|
|||
*/
|
||||
public function scheduleAction(Request $request, Response $response, $station_id): ResponseInterface
|
||||
{
|
||||
$utc = new \DateTimeZone('UTC');
|
||||
$user_tz = new \DateTimeZone(date_default_timezone_get());
|
||||
$station = $request->getStation();
|
||||
$tz = new \DateTimeZone($station->getTimezone());
|
||||
|
||||
$start_date_str = substr($request->getParam('start'), 0, 10);
|
||||
$start_date = Chronos::createFromFormat('Y-m-d', $start_date_str, $utc)
|
||||
$start_date = Chronos::createFromFormat('Y-m-d', $start_date_str, $tz)
|
||||
->subDay();
|
||||
|
||||
$end_date_str = substr($request->getParam('end'), 0, 10);
|
||||
$end_date = Chronos::createFromFormat('Y-m-d', $end_date_str, $utc);
|
||||
|
||||
$station = $request->getStation();
|
||||
$end_date = Chronos::createFromFormat('Y-m-d', $end_date_str, $tz);
|
||||
|
||||
/** @var Entity\StationPlaylist[] $all_playlists */
|
||||
$playlists = $station->getPlaylists()->filter(function($record) {
|
||||
/** @var Entity\StationPlaylist $record */
|
||||
return ($record->getType() === 'scheduled');
|
||||
return ($record->getType() === Entity\StationPlaylist::TYPE_SCHEDULED);
|
||||
});
|
||||
|
||||
$events = [];
|
||||
|
@ -132,9 +141,6 @@ class PlaylistsController extends AbstractStationCrudController
|
|||
$playlist_end = $playlist_end->addDay();
|
||||
}
|
||||
|
||||
$playlist_start = $playlist_start->setTimezone($user_tz);
|
||||
$playlist_end = $playlist_end->setTimezone($user_tz);
|
||||
|
||||
$events[] = [
|
||||
'id' => $playlist->getId(),
|
||||
'title' => $playlist->getName(),
|
||||
|
|
|
@ -38,10 +38,8 @@ class Customization
|
|||
public function init(Request $request): Request
|
||||
{
|
||||
if (!$this->app_settings->isCli() || $this->app_settings->isTesting()) {
|
||||
$timezone = $this->getTimeZone();
|
||||
$locale = $this->getLocale();
|
||||
} else {
|
||||
$timezone = self::DEFAULT_TIMEZONE;
|
||||
$locale = self::DEFAULT_LOCALE;
|
||||
}
|
||||
|
||||
|
@ -57,11 +55,8 @@ class Customization
|
|||
// Register translation superglobal functions
|
||||
$translator->register();
|
||||
|
||||
self::setGlobalValues($timezone, $locale);
|
||||
|
||||
return $request
|
||||
->withAttribute('locale', $locale)
|
||||
->withAttribute('timezone', $timezone);
|
||||
self::setGlobalValues($locale);
|
||||
return $request->withAttribute('locale', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,94 +69,6 @@ class Customization
|
|||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's custom time zone or the system default.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTimeZone(): string
|
||||
{
|
||||
if ($this->user !== null && !empty($this->user->getTimezone())) {
|
||||
return $this->user->getTimezone();
|
||||
}
|
||||
|
||||
return $this->getDefaultTimeZone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return either the configured global default timezone or the system's regular default.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultTimeZone(): string
|
||||
{
|
||||
$global_tz = $this->settings_repo->getSetting(Entity\Settings::TIMEZONE);
|
||||
|
||||
if (!empty($global_tz)) {
|
||||
return $global_tz;
|
||||
}
|
||||
|
||||
return date_default_timezone_get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given UNIX timestamp into a locale-friendly time.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @param bool $use_utc
|
||||
* @param bool $show_timezone_abbr
|
||||
* @return string Formatted time for presentation.
|
||||
*/
|
||||
public function formatTime($timestamp = null, $use_utc = false, $show_timezone_abbr = false): string
|
||||
{
|
||||
$timestamp = $timestamp ?? time();
|
||||
|
||||
$time_formats = $this->app_settings['time_formats'];
|
||||
$locale = $this->getLocale();
|
||||
|
||||
$time_format = $time_formats[$locale] ?? $time_formats['default'];
|
||||
|
||||
if ($show_timezone_abbr) {
|
||||
$time_format .= ($use_utc) ? ' \U\T\C' : ' T';
|
||||
}
|
||||
|
||||
return ($use_utc) ? gmdate($time_format, $timestamp) : date($time_format, $timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a date/time using PHP's IntlDateFormatter constants.
|
||||
*
|
||||
* @param int $timestamp
|
||||
* @param bool $use_utc
|
||||
* @param int|null $date_display One of:
|
||||
* IntlDateFormatter::NONE - Do not include
|
||||
* IntlDateFormatter::FULL - (Tuesday, April 12, 1952 AD or 3:30:42pm PST)
|
||||
* IntlDateFormatter::LONG (default) - (January 12, 1952 or 3:30:32pm)
|
||||
* IntlDateFormatter::MEDIUM - (Jan 12, 1952)
|
||||
* IntlDateFormatter::SHORT - (12/13/52 or 3:30pm)
|
||||
* @param int|null $time_display One of the above.
|
||||
* @return string
|
||||
*/
|
||||
public function formatDateTime(
|
||||
$timestamp,
|
||||
$use_utc = false,
|
||||
$date_display = \IntlDateFormatter::LONG,
|
||||
$time_display = \IntlDateFormatter::LONG): string
|
||||
{
|
||||
$timezone = ($use_utc) ? 'UTC' : date_default_timezone_get();
|
||||
$locale = str_replace('.UTF-8', '', $this->getLocale());
|
||||
|
||||
$fmt = new \IntlDateFormatter(
|
||||
$locale,
|
||||
$date_display,
|
||||
$time_display,
|
||||
$timezone,
|
||||
\IntlDateFormatter::GREGORIAN
|
||||
);
|
||||
|
||||
return $fmt->format($timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user-customized, browser-specified or system default locale.
|
||||
*
|
||||
|
@ -340,20 +247,14 @@ class Customization
|
|||
}
|
||||
|
||||
/**
|
||||
* @param null $timezone
|
||||
* @param null $locale
|
||||
*/
|
||||
public static function setGlobalValues($timezone = null, $locale = null): void
|
||||
public static function setGlobalValues($locale = null): void
|
||||
{
|
||||
if (empty($timezone)) {
|
||||
$timezone = self::DEFAULT_TIMEZONE;
|
||||
}
|
||||
if (empty($locale)) {
|
||||
$locale = self::DEFAULT_LOCALE;
|
||||
}
|
||||
|
||||
date_default_timezone_set($timezone);
|
||||
|
||||
putenv("LANG=" . $locale);
|
||||
setlocale(\LC_ALL, $locale);
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Entity\Migration;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20190513124232 extends AbstractMigration
|
||||
{
|
||||
public function up(Schema $schema) : void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
|
||||
|
||||
// Move "play once per day" playlists to be standard scheduled ones with the same start/end time.
|
||||
$this->addSql('UPDATE station_playlists SET type="scheduled", schedule_start_time=play_once_time, schedule_end_time=play_once_time, schedule_days=play_once_days WHERE type = "once_per_day"');
|
||||
|
||||
$this->addSql('ALTER TABLE station_playlists ADD schedule_tz VARCHAR(100) DEFAULT NULL, DROP play_once_time, DROP play_once_days');
|
||||
|
||||
// Set all legacy playlists to be scheduled against UTC for back-compatibility.
|
||||
$this->addSql('UPDATE station_playlists SET schedule_tz="UTC" WHERE type = "scheduled"');
|
||||
}
|
||||
|
||||
public function down(Schema $schema) : void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE station_playlists ADD play_once_time SMALLINT NOT NULL, ADD play_once_days VARCHAR(50) DEFAULT NULL COLLATE utf8mb4_general_ci, DROP schedule_tz');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace App\Entity\Migration;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20190513163051 extends AbstractMigration
|
||||
{
|
||||
public function up(Schema $schema) : void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
|
||||
|
||||
// Move "play once per day" playlists to be standard scheduled ones with the same start/end time.
|
||||
$this->addSql('UPDATE station_playlists SET type="scheduled", schedule_start_time=play_once_time, schedule_end_time=play_once_time, schedule_days=play_once_days WHERE type = "once_per_day"');
|
||||
|
||||
$this->addSql('ALTER TABLE station ADD timezone VARCHAR(100) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE station_playlists DROP play_once_time, DROP play_once_days');
|
||||
$this->addSql('ALTER TABLE users DROP timezone');
|
||||
}
|
||||
|
||||
public function postUp(Schema $schema)
|
||||
{
|
||||
// Use the system setting for "global timezone" to set the station timezones.
|
||||
$global_tz = $this->connection->fetchColumn('SELECT setting_value FROM settings WHERE setting_key="timezone"');
|
||||
|
||||
if (!empty($global_tz)) {
|
||||
$global_tz = json_decode($global_tz, true);
|
||||
} else {
|
||||
$global_tz = 'UTC';
|
||||
}
|
||||
|
||||
// Set all stations' timezones to this value.
|
||||
$this->connection->update('station', [
|
||||
'timezone' => $global_tz,
|
||||
], [1 => 1]);
|
||||
|
||||
// Calculate the offset of any currently scheduled playlists.
|
||||
if ('UTC' !== $global_tz) {
|
||||
|
||||
$system_tz = new \DateTimeZone('UTC');
|
||||
$system_dt = new \DateTime('now', $system_tz);
|
||||
$system_offset = $system_tz->getOffset($system_dt);
|
||||
|
||||
$app_tz = new \DateTimeZone($global_tz);
|
||||
$app_dt = new \DateTime('now', $app_tz);
|
||||
$app_offset = $app_tz->getOffset($app_dt);
|
||||
|
||||
$offset = $system_offset - $app_offset;
|
||||
$offset_hours = (int)floor($offset / 3600);
|
||||
|
||||
if (0 !== $offset_hours) {
|
||||
|
||||
$playlists = $this->connection->fetchAll('SELECT sp.* FROM station_playlists AS sp WHERE sp.type = "scheduled"');
|
||||
|
||||
foreach($playlists as $playlist) {
|
||||
$this->connection->update('station_playlists', [
|
||||
'schedule_start_time' => $this->_applyOffset($playlist['schedule_start_time'], $offset_hours),
|
||||
'schedule_end_time' => $this->_applyOffset($playlist['schedule_end_time'], $offset_hours),
|
||||
], [
|
||||
'id' => $playlist['id'],
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $time_code
|
||||
* @param int $offset_hours
|
||||
* @return int
|
||||
*/
|
||||
protected function _applyOffset($time_code, $offset_hours): int
|
||||
{
|
||||
$hours = (int)floor($time_code / 100);
|
||||
$mins = $time_code % 100;
|
||||
|
||||
$hours += $offset_hours;
|
||||
|
||||
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
|
||||
$hours %= 24;
|
||||
if ($hours < 0) {
|
||||
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
|
||||
$hours += 24;
|
||||
}
|
||||
|
||||
return ($hours*100)+$mins;
|
||||
}
|
||||
|
||||
public function down(Schema $schema) : void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
|
||||
|
||||
$this->addSql('ALTER TABLE station DROP timezone');
|
||||
$this->addSql('ALTER TABLE station_playlists ADD play_once_time SMALLINT NOT NULL, ADD play_once_days VARCHAR(50) DEFAULT NULL COLLATE utf8mb4_general_ci');
|
||||
$this->addSql('ALTER TABLE users ADD timezone VARCHAR(100) DEFAULT NULL COLLATE utf8mb4_general_ci');
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Customization;
|
||||
use App\Radio\Quota;
|
||||
use Azura\Normalizer\Annotation\DeepNormalize;
|
||||
use Brick\Math\BigInteger;
|
||||
|
@ -289,6 +290,14 @@ class Station
|
|||
*/
|
||||
protected $storage_used_bytes;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="timezone", type="string", length=100, nullable=true)
|
||||
*
|
||||
* @OA\Property(example="UTC")
|
||||
* @var string|null The time zone that station operations should take place in.
|
||||
*/
|
||||
protected $timezone = 'UTC';
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="SongHistory", mappedBy="station")
|
||||
* @ORM\OrderBy({"timestamp_start" = "DESC"})
|
||||
|
@ -1114,6 +1123,26 @@ class Station
|
|||
return Quota::getPercentage($this->getStorageUsedBytes(), $this->getRawStorageAvailable());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTimezone(): string
|
||||
{
|
||||
if (!empty($this->timezone)) {
|
||||
return $this->timezone;
|
||||
}
|
||||
|
||||
return Customization::DEFAULT_TIMEZONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $timezone
|
||||
*/
|
||||
public function setTimezone(?string $timezone): void
|
||||
{
|
||||
$this->timezone = $timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
|
|
|
@ -184,14 +184,6 @@ class StationPlaylist
|
|||
*/
|
||||
protected $play_per_hour_minute = 0;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="schedule_tz", type="string", length=100, nullable=true)
|
||||
*
|
||||
* @OA\Property(example="UTC")
|
||||
* @var string|null
|
||||
*/
|
||||
protected $schedule_tz = 'UTC';
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="schedule_start_time", type="smallint")
|
||||
*
|
||||
|
@ -515,14 +507,6 @@ class StationPlaylist
|
|||
return (int)$this->schedule_start_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getScheduleStartTimeText(): string
|
||||
{
|
||||
return self::formatTimeCodeForInput($this->schedule_start_time);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $schedule_start_time
|
||||
*/
|
||||
|
@ -539,14 +523,6 @@ class StationPlaylist
|
|||
return (int)$this->schedule_end_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getScheduleEndTimeText(): string
|
||||
{
|
||||
return self::formatTimeCodeForInput($this->schedule_end_time);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $schedule_end_time
|
||||
*/
|
||||
|
@ -564,10 +540,13 @@ class StationPlaylist
|
|||
return 0;
|
||||
}
|
||||
|
||||
$start_time = self::getTimestamp($this->schedule_start_time);
|
||||
$end_time = self::getTimestamp($this->schedule_end_time);
|
||||
$start_time = self::getDateTime($this->schedule_start_time)
|
||||
->getTimestamp();
|
||||
$end_time = self::getDateTime($this->schedule_end_time)
|
||||
->getTimestamp();
|
||||
|
||||
if ($start_time > $end_time) {
|
||||
/** @noinspection SummerTimeUnsafeTimeManipulationInspection */
|
||||
return 86400 - ($start_time - $end_time);
|
||||
}
|
||||
|
||||
|
@ -590,46 +569,6 @@ class StationPlaylist
|
|||
$this->schedule_days = implode(',', (array)$schedule_days);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPlayOnceTime(): int
|
||||
{
|
||||
return $this->play_once_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPlayOnceTimeText(): string
|
||||
{
|
||||
return self::formatTimeCodeForInput($this->play_once_time);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $play_once_time
|
||||
*/
|
||||
public function setPlayOnceTime(int $play_once_time): void
|
||||
{
|
||||
$this->play_once_time = $play_once_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getPlayOnceDays(): array
|
||||
{
|
||||
return explode(',', $this->play_once_days);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $play_once_days
|
||||
*/
|
||||
public function setPlayOnceDays($play_once_days): void
|
||||
{
|
||||
$this->play_once_days = implode(',', (array)$play_once_days);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
|
@ -791,31 +730,6 @@ class StationPlaylist
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a time code i.e. "2300", return a UNIX timestamp that can be used to format the time for display.
|
||||
*
|
||||
* @param string|int $time_code
|
||||
* @return int
|
||||
*/
|
||||
public static function getTimestamp($time_code): int
|
||||
{
|
||||
return self::getDateTime($time_code)
|
||||
->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a time code i.e. "2300", return a time suitable for HTML5 inputs, i.e. "23:00".
|
||||
*
|
||||
* @param string|int $time_code
|
||||
* @return string
|
||||
*/
|
||||
public static function formatTimeCodeForInput($time_code): string
|
||||
{
|
||||
$now = Chronos::now(new \DateTimeZone(date_default_timezone_get()));
|
||||
return self::getDateTime($time_code, $now)
|
||||
->format('H:i');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a \DateTime object (or null) for a given time code, by default in the UTC time zone.
|
||||
*
|
||||
|
@ -832,19 +746,4 @@ class StationPlaylist
|
|||
$time_code = str_pad($time_code, 4, '0', STR_PAD_LEFT);
|
||||
return $now->setTime(substr($time_code, 0, 2), substr($time_code, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current UTC time in "time code" style.
|
||||
*
|
||||
* @param Chronos|null $now
|
||||
* @return int
|
||||
*/
|
||||
public static function getCurrentTimeCode(Chronos $now = null): int
|
||||
{
|
||||
if ($now === null) {
|
||||
$now = Chronos::now(new \DateTimeZone('UTC'));
|
||||
}
|
||||
|
||||
return (int)$now->format('Hi');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,14 +59,6 @@ class User
|
|||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="timezone", type="string", length=100, nullable=true)
|
||||
*
|
||||
* @OA\Property(example="America/Chicago")
|
||||
* @var string|null
|
||||
*/
|
||||
protected $timezone;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="locale", type="string", length=25, nullable=true)
|
||||
*
|
||||
|
@ -236,22 +228,6 @@ class User
|
|||
$this->name = $this->_truncateString($name, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
*/
|
||||
public function getTimezone(): ?string
|
||||
{
|
||||
return $this->timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $timezone
|
||||
*/
|
||||
public function setTimezone($timezone): void
|
||||
{
|
||||
$this->timezone = $timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,16 @@ class PlaylistTime extends Time
|
|||
|
||||
$this->attributes['pattern'] = '[0-9]{2}:[0-9]{2}';
|
||||
$this->attributes['placeholder'] = '13:45';
|
||||
|
||||
// Handle the "time code" format used by the database entity,
|
||||
// which is just the regular 24-hour time minus the ":".
|
||||
$this->filters[] = function($new_value) {
|
||||
if (!empty($new_value) && false === strpos($new_value, ':')) {
|
||||
$time_code = str_pad($new_value, 4, '0', STR_PAD_LEFT);
|
||||
return substr($time_code, 0, 2).':'.substr($time_code, 2);
|
||||
}
|
||||
return $new_value;
|
||||
};
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
|
|
|
@ -379,15 +379,11 @@ class Liquidsoap extends AbstractBackend implements EventSubscriberInterface
|
|||
*/
|
||||
protected function _getScheduledPlaylistPlayTime(Entity\StationPlaylist $playlist): string
|
||||
{
|
||||
$playlist_tz = 'UTC'; // TODO
|
||||
$playlist_start_time = $playlist->getScheduleStartTime();
|
||||
$playlist_end_time = $playlist->getScheduleEndTime();
|
||||
|
||||
$start_time = $this->_getOffsetTimeCode($playlist_start_time, $playlist_tz);
|
||||
$end_time = $this->_getOffsetTimeCode($playlist_end_time, $playlist_tz);
|
||||
$start_time = $playlist->getScheduleStartTime();
|
||||
$end_time = $playlist->getScheduleEndTime();
|
||||
|
||||
// Handle multi-day playlists.
|
||||
if ($playlist_start_time > $playlist_end_time) {
|
||||
if ($start_time > $end_time) {
|
||||
$play_times = [
|
||||
$this->_formatTimeCode($start_time).'-23h59m',
|
||||
'00h00m-'.$this->_formatTimeCode($end_time),
|
||||
|
@ -417,7 +413,7 @@ class Liquidsoap extends AbstractBackend implements EventSubscriberInterface
|
|||
}
|
||||
|
||||
// Handle once-per-day playlists.
|
||||
$play_time = ($playlist_start_time === $playlist_end_time)
|
||||
$play_time = ($start_time === $end_time)
|
||||
? $this->_formatTimeCode($start_time)
|
||||
: $this->_formatTimeCode($start_time) . '-' . $this->_formatTimeCode($end_time);
|
||||
|
||||
|
@ -436,6 +432,20 @@ class Liquidsoap extends AbstractBackend implements EventSubscriberInterface
|
|||
return $play_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the time offset
|
||||
*
|
||||
* @param int $time_code
|
||||
* @return string
|
||||
*/
|
||||
protected function _formatTimeCode($time_code): string
|
||||
{
|
||||
$hours = floor($time_code / 100);
|
||||
$mins = $time_code % 100;
|
||||
|
||||
return $hours . 'h' . $mins . 'm';
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a playlist's contents to file so Liquidsoap can process it, and optionally notify
|
||||
* Liquidsoap of the change.
|
||||
|
@ -719,57 +729,6 @@ class Liquidsoap extends AbstractBackend implements EventSubscriberInterface
|
|||
return 'list.hd(get_process_lines("'.$command.'"), default="")';
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the time offset
|
||||
*
|
||||
* @param int $time_code
|
||||
* @return string
|
||||
*/
|
||||
protected function _formatTimeCode($time_code): string
|
||||
{
|
||||
$hours = floor($time_code / 100);
|
||||
$mins = $time_code % 100;
|
||||
|
||||
return $hours . 'h' . $mins . 'm';
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a time code (i.e. from a playlist), calculate the offset time code
|
||||
*
|
||||
* @param int $time_code
|
||||
* @param string $tz
|
||||
* @return int
|
||||
*/
|
||||
protected function _getOffsetTimeCode($time_code, $tz = 'UTC'): int
|
||||
{
|
||||
$hours = floor($time_code / 100);
|
||||
$mins = $time_code % 100;
|
||||
|
||||
$system_time_zone = \App\Utilities::getSystemTimeZone();
|
||||
|
||||
if ($system_time_zone !== $tz) {
|
||||
$system_tz = new \DateTimeZone($system_time_zone);
|
||||
$system_dt = new \DateTime('now', $system_tz);
|
||||
$system_offset = $system_tz->getOffset($system_dt);
|
||||
|
||||
$app_tz = new \DateTimeZone($tz);
|
||||
$app_dt = new \DateTime('now', $app_tz);
|
||||
$app_offset = $app_tz->getOffset($app_dt);
|
||||
|
||||
$offset = $system_offset - $app_offset;
|
||||
$offset_hours = floor($offset / 3600);
|
||||
|
||||
$hours += $offset_hours;
|
||||
}
|
||||
|
||||
$hours %= 24;
|
||||
if ($hours < 0) {
|
||||
$hours += 24;
|
||||
}
|
||||
|
||||
return (int)(($hours*100)+$mins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a user-supplied string to be a valid LiquidSoap config entry.
|
||||
*
|
||||
|
|
|
@ -127,23 +127,23 @@ class Configuration
|
|||
[,$program_name] = explode(':', $adapter->getProgramName($station));
|
||||
|
||||
$config_lines = [
|
||||
'user' => 'azuracast',
|
||||
'priority' => $priority,
|
||||
'command' => $adapter->getCommand($station),
|
||||
'directory' => $config_path,
|
||||
'stdout_logfile' => $adapter->getLogPath($station),
|
||||
'user' => 'azuracast',
|
||||
'priority' => $priority,
|
||||
'command' => $adapter->getCommand($station),
|
||||
'directory' => $config_path,
|
||||
'environment' => 'TZ="'.$station->getTimezone().'"',
|
||||
'stdout_logfile' => $adapter->getLogPath($station),
|
||||
'stdout_logfile_maxbytes' => '5MB',
|
||||
'stdout_logfile_backups' => '10',
|
||||
'redirect_stderr' => 'true',
|
||||
'redirect_stderr' => 'true',
|
||||
];
|
||||
|
||||
$supervisor_config[] = '[program:' . $program_name . ']';
|
||||
|
||||
foreach($config_lines as $config_key => $config_value) {
|
||||
$supervisor_config[] = $config_key . '=' . $config_value;
|
||||
}
|
||||
|
||||
$supervisor_config[] = '';
|
||||
|
||||
return implode("\n", $supervisor_config);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<h3 class="card-subtitle"><?=implode(', ', $sync_info['contents']) ?></h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text"><?=$customization->formatDateTime($sync_info['latest']) ?><br><small><?=sprintf(__('%s ago'), $sync_info['diff_text']) ?></small></p>
|
||||
<p class="card-text"><?=sprintf(__('%s ago'), $sync_info['diff_text']) ?></p>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a class="btn btn-outline-primary" role="button" href="<?=$router->named('admin:index:sync', ['type' => $sync_key]) ?>">
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
$(function() {
|
||||
$('time').each(function() {
|
||||
var tz_display = $(this).data('content');
|
||||
$(this).text(moment(tz_display).format('LT'));
|
||||
});
|
||||
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
var target = $(e.target).attr("href");
|
||||
|
||||
|
|
|
@ -96,16 +96,13 @@ $assets->load('fullcalendar')
|
|||
<?=__('Weight') ?>: <?=(int)$row['weight'] ?> (<?=$row['probability'] ?>)
|
||||
<?php elseif ($row['type'] === StationPlaylist::TYPE_SCHEDULED): ?>
|
||||
<?=__('Scheduled') ?><br>
|
||||
<?=__('Plays between %s and %s', $customization->formatTime(StationPlaylist::getTimestamp($row['schedule_start_time'])), $customization->formatTime(StationPlaylist::getTimestamp($row['schedule_end_time']))) ?>
|
||||
<?=__('Plays between %s and %s', '<time data-content="'.$row['schedule_start'].'"></time>', '<time data-content="'.$row['schedule_end'].'"></time>') ?>
|
||||
<?php elseif ($row['type'] === StationPlaylist::TYPE_ONCE_PER_X_SONGS): ?>
|
||||
<?=__('Once per %d Songs', $row['play_per_songs']) ?>
|
||||
<?php elseif ($row['type'] === StationPlaylist::TYPE_ONCE_PER_X_MINUTES): ?>
|
||||
<?=__('Once per %d Minutes', $row['play_per_minutes']) ?>
|
||||
<?php elseif ($row['type'] === StationPlaylist::TYPE_ONCE_PER_HOUR): ?>
|
||||
<?=__('Once per Hour (at :%02d)', $row['play_per_hour_minute']) ?>
|
||||
<?php elseif ($row['type'] === StationPlaylist::TYPE_ONCE_PER_DAY): ?>
|
||||
<?=__('Once per Day') ?><br>
|
||||
<?=__('Plays at %s', $customization->formatTime(StationPlaylist::getTimestamp($row['play_once_time']))) ?>
|
||||
<?php elseif ($row['type'] === StationPlaylist::TYPE_ADVANCED): ?>
|
||||
<?=__('Custom') ?>
|
||||
<?php endif; ?>
|
||||
|
|
Loading…
Reference in New Issue