2016-05-04 00:03:28 +00:00
|
|
|
<?php
|
|
|
|
namespace Entity;
|
|
|
|
|
2018-03-28 06:07:56 +00:00
|
|
|
use Cake\Chronos\Chronos;
|
2017-01-24 00:35:16 +00:00
|
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
2017-08-17 18:28:48 +00:00
|
|
|
use Doctrine\Common\Collections\Collection;
|
2018-01-13 12:45:16 +00:00
|
|
|
use DateTime;
|
2016-05-04 00:03:28 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @Table(name="station_playlists")
|
|
|
|
* @Entity
|
|
|
|
* @HasLifecycleCallbacks
|
|
|
|
*/
|
2017-08-17 18:28:48 +00:00
|
|
|
class StationPlaylist
|
2016-05-04 00:03:28 +00:00
|
|
|
{
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @Column(name="id", type="integer")
|
|
|
|
* @Id
|
|
|
|
* @GeneratedValue(strategy="IDENTITY")
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $id;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="station_id", type="integer")
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $station_id;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ManyToOne(targetEntity="Station", inversedBy="playlists")
|
|
|
|
* @JoinColumns({
|
|
|
|
* @JoinColumn(name="station_id", referencedColumnName="id", onDelete="CASCADE")
|
|
|
|
* })
|
|
|
|
* @var Station
|
|
|
|
*/
|
|
|
|
protected $station;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="name", type="string", length=200)
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $name;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="type", type="string", length=50)
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $type;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="is_enabled", type="boolean")
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $is_enabled;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="play_per_songs", type="smallint")
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $play_per_songs;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="play_per_minutes", type="smallint")
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $play_per_minutes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="schedule_start_time", type="smallint")
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $schedule_start_time;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="schedule_end_time", type="smallint")
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $schedule_end_time;
|
|
|
|
|
2017-08-24 01:30:43 +00:00
|
|
|
/**
|
|
|
|
* @Column(name="schedule_days", type="string", length=50, nullable=true)
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $schedule_days;
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @Column(name="play_once_time", type="smallint")
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $play_once_time;
|
|
|
|
|
2017-08-24 01:30:43 +00:00
|
|
|
/**
|
|
|
|
* @Column(name="play_once_days", type="string", length=50, nullable=true)
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $play_once_days;
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @Column(name="weight", type="smallint")
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $weight;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Column(name="include_in_automation", type="boolean")
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
protected $include_in_automation;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ManyToMany(targetEntity="StationMedia", mappedBy="playlists", fetch="EXTRA_LAZY")
|
|
|
|
* @var Collection
|
|
|
|
*/
|
|
|
|
protected $media;
|
|
|
|
|
|
|
|
public function __construct(Station $station)
|
2017-01-24 00:17:50 +00:00
|
|
|
{
|
2017-08-17 18:28:48 +00:00
|
|
|
$this->station = $station;
|
|
|
|
|
2017-01-24 00:17:50 +00:00
|
|
|
$this->type = 'default';
|
|
|
|
$this->is_enabled = 1;
|
|
|
|
|
|
|
|
$this->weight = 3;
|
|
|
|
$this->include_in_automation = false;
|
|
|
|
$this->play_once_time = 0;
|
|
|
|
$this->play_per_minutes = 0;
|
|
|
|
$this->play_per_songs = 0;
|
|
|
|
$this->schedule_start_time = 0;
|
|
|
|
$this->schedule_end_time = 0;
|
|
|
|
|
|
|
|
$this->media = new ArrayCollection;
|
|
|
|
}
|
|
|
|
|
2016-05-04 00:03:28 +00:00
|
|
|
/**
|
2017-08-17 18:28:48 +00:00
|
|
|
* @return int
|
2016-05-04 00:03:28 +00:00
|
|
|
*/
|
2017-08-17 18:28:48 +00:00
|
|
|
public function getId(): int
|
|
|
|
{
|
|
|
|
return $this->id;
|
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return Station
|
|
|
|
*/
|
|
|
|
public function getStation(): Station
|
|
|
|
{
|
|
|
|
return $this->station;
|
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getName(): string
|
|
|
|
{
|
|
|
|
return $this->name;
|
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getShortName(): string
|
2017-01-24 00:17:50 +00:00
|
|
|
{
|
|
|
|
return Station::getStationShortName($this->name);
|
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @param string $name
|
|
|
|
*/
|
|
|
|
public function setName(string $name)
|
|
|
|
{
|
|
|
|
$this->name = $name;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getType(): string
|
|
|
|
{
|
|
|
|
return $this->type;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @param string $type
|
|
|
|
*/
|
|
|
|
public function setType(string $type)
|
|
|
|
{
|
|
|
|
$this->type = $type;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function getIsEnabled(): bool
|
|
|
|
{
|
|
|
|
return $this->is_enabled;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @param bool $is_enabled
|
|
|
|
*/
|
|
|
|
public function setIsEnabled(bool $is_enabled)
|
|
|
|
{
|
|
|
|
$this->is_enabled = $is_enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getPlayPerSongs(): int
|
|
|
|
{
|
|
|
|
return $this->play_per_songs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param int $play_per_songs
|
|
|
|
*/
|
|
|
|
public function setPlayPerSongs(int $play_per_songs)
|
|
|
|
{
|
|
|
|
$this->play_per_songs = $play_per_songs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getPlayPerMinutes(): int
|
|
|
|
{
|
|
|
|
return $this->play_per_minutes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param int $play_per_minutes
|
|
|
|
*/
|
|
|
|
public function setPlayPerMinutes(int $play_per_minutes)
|
|
|
|
{
|
|
|
|
$this->play_per_minutes = $play_per_minutes;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getScheduleStartTime(): int
|
|
|
|
{
|
|
|
|
return $this->schedule_start_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getScheduleStartTimeText(): string
|
2017-01-24 00:17:50 +00:00
|
|
|
{
|
2017-11-12 08:19:47 +00:00
|
|
|
return self::formatTimeCodeForInput($this->schedule_start_time);
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @param int $schedule_start_time
|
|
|
|
*/
|
|
|
|
public function setScheduleStartTime(int $schedule_start_time)
|
|
|
|
{
|
|
|
|
$this->schedule_start_time = $schedule_start_time;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getScheduleEndTime(): int
|
2017-01-24 00:17:50 +00:00
|
|
|
{
|
2017-08-17 18:28:48 +00:00
|
|
|
return $this->schedule_end_time;
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 08:19:47 +00:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getScheduleEndTimeText(): string
|
|
|
|
{
|
|
|
|
return self::formatTimeCodeForInput($this->schedule_end_time);
|
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @param int $schedule_end_time
|
|
|
|
*/
|
|
|
|
public function setScheduleEndTime(int $schedule_end_time)
|
|
|
|
{
|
|
|
|
$this->schedule_end_time = $schedule_end_time;
|
|
|
|
}
|
2016-09-21 07:53:48 +00:00
|
|
|
|
2017-08-24 01:30:43 +00:00
|
|
|
/**
|
2017-09-01 00:37:57 +00:00
|
|
|
* @return array|null
|
2017-08-24 01:30:43 +00:00
|
|
|
*/
|
2017-09-01 00:37:57 +00:00
|
|
|
public function getScheduleDays(): ?array
|
2017-08-24 01:30:43 +00:00
|
|
|
{
|
2017-09-01 00:37:57 +00:00
|
|
|
return (!empty($this->schedule_days)) ? explode(',', $this->schedule_days) : null;
|
2017-08-24 01:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $schedule_days
|
|
|
|
*/
|
|
|
|
public function setScheduleDays($schedule_days)
|
|
|
|
{
|
|
|
|
$this->schedule_days = implode(',', (array)$schedule_days);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the playlist is scheduled to play according to schedule rules.
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function canPlayScheduled(): bool
|
|
|
|
{
|
|
|
|
$play_once_days = $this->getScheduleDays();
|
|
|
|
|
|
|
|
if (!empty($play_once_days) && !in_array(gmdate('N'), $play_once_days)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$current_timecode = self::getCurrentTimeCode();
|
|
|
|
|
|
|
|
if ($this->getScheduleEndTime() < $this->getScheduleStartTime()) {
|
|
|
|
// Overnight playlist
|
|
|
|
return ($current_timecode >= $this->getScheduleStartTime() || $current_timecode <= $this->getScheduleEndTime());
|
|
|
|
} else {
|
|
|
|
// Normal playlist
|
|
|
|
return ($current_timecode >= $this->getScheduleStartTime() && $current_timecode <= $this->getScheduleEndTime());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getPlayOnceTime(): int
|
2017-01-24 00:17:50 +00:00
|
|
|
{
|
2017-08-17 18:28:48 +00:00
|
|
|
return $this->play_once_time;
|
2017-01-24 00:17:50 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 08:19:47 +00:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getPlayOnceTimeText(): string
|
|
|
|
{
|
|
|
|
return self::formatTimeCodeForInput($this->play_once_time);
|
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @param int $play_once_time
|
|
|
|
*/
|
|
|
|
public function setPlayOnceTime(int $play_once_time)
|
|
|
|
{
|
|
|
|
$this->play_once_time = $play_once_time;
|
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
|
2017-08-24 01:30:43 +00:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getPlayOnceDays(): array
|
|
|
|
{
|
|
|
|
return explode(',', $this->play_once_days);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $play_once_days
|
|
|
|
*/
|
|
|
|
public function setPlayOnceDays($play_once_days)
|
|
|
|
{
|
|
|
|
$this->play_once_days = implode(',', (array)$play_once_days);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the playlist is scheduled to play once.
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function canPlayOnce(): bool
|
|
|
|
{
|
|
|
|
$play_once_days = $this->getPlayOnceDays();
|
|
|
|
|
|
|
|
if (!empty($play_once_days) && !in_array(gmdate('N'), $play_once_days)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$current_timecode = self::getCurrentTimeCode();
|
|
|
|
|
|
|
|
$playlist_play_time = $this->getPlayOnceTime();
|
|
|
|
$playlist_diff = $current_timecode - $playlist_play_time;
|
|
|
|
|
|
|
|
return ($playlist_diff > 0 && $playlist_diff <= 15);
|
|
|
|
}
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getWeight(): int
|
|
|
|
{
|
|
|
|
return $this->weight;
|
|
|
|
}
|
2016-08-30 06:28:56 +00:00
|
|
|
|
2016-05-04 00:03:28 +00:00
|
|
|
/**
|
2017-08-17 18:28:48 +00:00
|
|
|
* @param int $weight
|
2016-05-04 00:03:28 +00:00
|
|
|
*/
|
2017-08-17 18:28:48 +00:00
|
|
|
public function setWeight(int $weight)
|
|
|
|
{
|
|
|
|
$this->weight = $weight;
|
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
|
|
|
|
/**
|
2017-08-17 18:28:48 +00:00
|
|
|
* @return bool
|
2016-05-04 00:03:28 +00:00
|
|
|
*/
|
2017-08-17 18:28:48 +00:00
|
|
|
public function getIncludeInAutomation(): bool
|
|
|
|
{
|
|
|
|
return $this->include_in_automation;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param bool $include_in_automation
|
|
|
|
*/
|
|
|
|
public function setIncludeInAutomation(bool $include_in_automation)
|
|
|
|
{
|
|
|
|
$this->include_in_automation = $include_in_automation;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return Collection
|
|
|
|
*/
|
|
|
|
public function getMedia(): Collection
|
|
|
|
{
|
|
|
|
return $this->media;
|
|
|
|
}
|
|
|
|
|
2017-05-13 04:15:36 +00:00
|
|
|
/**
|
|
|
|
* Export the playlist into a reusable format.
|
|
|
|
*
|
|
|
|
* @param string $file_format
|
|
|
|
* @param bool $absolute_paths
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function export($file_format = 'pls', $absolute_paths = false)
|
|
|
|
{
|
|
|
|
$media_path = ($absolute_paths) ? $this->station->getRadioMediaDir().'/' : '';
|
|
|
|
|
|
|
|
switch($file_format)
|
|
|
|
{
|
2017-05-13 16:16:57 +00:00
|
|
|
case 'm3u':
|
|
|
|
$playlist_file = [];
|
|
|
|
foreach ($this->media as $media_file) {
|
2017-08-17 18:28:48 +00:00
|
|
|
$media_file_path = $media_path . $media_file->getPath();
|
2017-05-13 16:16:57 +00:00
|
|
|
$playlist_file[] = $media_file_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
shuffle($playlist_file);
|
|
|
|
|
|
|
|
return implode("\n", $playlist_file);
|
2017-08-24 01:30:43 +00:00
|
|
|
break;
|
2017-05-13 16:16:57 +00:00
|
|
|
|
2017-05-13 04:15:36 +00:00
|
|
|
case 'pls':
|
|
|
|
default:
|
2017-05-13 16:16:57 +00:00
|
|
|
$playlist_file = [
|
|
|
|
'[playlist]',
|
|
|
|
];
|
|
|
|
|
|
|
|
$i = 0;
|
|
|
|
foreach($this->media as $media_file) {
|
|
|
|
$i++;
|
|
|
|
|
2017-08-17 18:28:48 +00:00
|
|
|
$media_file_path = $media_path . $media_file->getPath();
|
2017-05-13 16:16:57 +00:00
|
|
|
$playlist_file[] = 'File'.$i.'='.$media_file_path;
|
2017-08-17 18:28:48 +00:00
|
|
|
$playlist_file[] = 'Title'.$i.'='.$media_file->getArtist().' - '.$media_file->getTitle();
|
|
|
|
$playlist_file[] = 'Length'.$i.'='.$media_file->getLength();
|
2017-05-13 16:16:57 +00:00
|
|
|
$playlist_file[] = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
$playlist_file[] = 'NumberOfEntries='.$i;
|
|
|
|
$playlist_file[] = 'Version=2';
|
|
|
|
|
2017-05-13 04:15:36 +00:00
|
|
|
return implode("\n", $playlist_file);
|
2017-08-24 01:30:43 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-14 18:04:58 +00:00
|
|
|
/**
|
|
|
|
* Given a time code i.e. "2300", return a UNIX timestamp that can be used to format the time for display.
|
|
|
|
*
|
|
|
|
* @param $time_code
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public static function getTimestamp($time_code): int
|
|
|
|
{
|
2018-03-28 06:07:56 +00:00
|
|
|
return self::getDateTime($time_code)
|
|
|
|
->getTimestamp();
|
2017-12-14 18:04:58 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 01:30:43 +00:00
|
|
|
/**
|
2018-01-13 12:45:16 +00:00
|
|
|
* Given a time code i.e. "2300", return a time suitable for HTML5 inputs, i.e. "23:00".
|
2017-11-12 08:19:47 +00:00
|
|
|
*
|
2017-08-24 01:30:43 +00:00
|
|
|
* @param $time_code
|
|
|
|
* @return string
|
|
|
|
*/
|
2018-01-13 12:45:16 +00:00
|
|
|
public static function formatTimeCodeForInput($time_code): string
|
2017-08-24 01:30:43 +00:00
|
|
|
{
|
2018-01-13 12:45:16 +00:00
|
|
|
$dt = self::getDateTime($time_code);
|
|
|
|
if ($dt) {
|
|
|
|
$dt->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
|
|
|
return $dt->format('H:i');
|
2017-11-20 08:24:22 +00:00
|
|
|
}
|
|
|
|
return '';
|
2017-11-12 08:19:47 +00:00
|
|
|
}
|
2017-08-24 01:30:43 +00:00
|
|
|
|
2017-11-12 08:19:47 +00:00
|
|
|
/**
|
2018-01-13 12:45:16 +00:00
|
|
|
* Return a \DateTime object (or null) for a given time code, by default in the UTC time zone.
|
2017-11-12 08:19:47 +00:00
|
|
|
*
|
|
|
|
* @param $time_code
|
2018-03-28 06:07:56 +00:00
|
|
|
* @return Chronos
|
2017-11-12 08:19:47 +00:00
|
|
|
*/
|
2018-03-28 06:07:56 +00:00
|
|
|
public static function getDateTime($time_code): Chronos
|
2017-11-12 08:19:47 +00:00
|
|
|
{
|
|
|
|
$time_code = str_pad($time_code, 4, '0', STR_PAD_LEFT);
|
2018-03-28 06:07:56 +00:00
|
|
|
|
|
|
|
return Chronos::now(new \DateTimeZone('UTC'))
|
|
|
|
->setTime(substr($time_code, 0, 2), substr($time_code, 2));
|
2017-08-24 01:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the current UTC time in "time code" style.
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public static function getCurrentTimeCode(): int
|
|
|
|
{
|
2017-11-12 08:19:47 +00:00
|
|
|
return (int)gmdate('Hi');
|
2017-05-13 04:15:36 +00:00
|
|
|
}
|
2016-05-04 00:03:28 +00:00
|
|
|
}
|