feat(embeddable-player): add embeddable player widget
This commit is contained in:
parent
526809ef28
commit
141788fa08
|
@ -326,6 +326,14 @@ $routes->group(
|
||||||
'filter' => 'permission:podcast_episodes-edit',
|
'filter' => 'permission:podcast_episodes-edit',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
$routes->get(
|
||||||
|
'embeddable-player',
|
||||||
|
'Episode::embeddablePlayer/$1/$2',
|
||||||
|
[
|
||||||
|
'as' => 'embeddable-player-add',
|
||||||
|
'filter' => 'permission:podcast_episodes-edit',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
$routes->group('persons', function ($routes) {
|
$routes->group('persons', function ($routes) {
|
||||||
$routes->get('/', 'EpisodePerson/$1/$2', [
|
$routes->get('/', 'EpisodePerson/$1/$2', [
|
||||||
|
@ -565,9 +573,19 @@ $routes->group(config('App')->authGateway, function ($routes) {
|
||||||
// Public routes
|
// Public routes
|
||||||
$routes->group('@(:podcastName)', function ($routes) {
|
$routes->group('@(:podcastName)', function ($routes) {
|
||||||
$routes->get('/', 'Podcast/$1', ['as' => 'podcast']);
|
$routes->get('/', 'Podcast/$1', ['as' => 'podcast']);
|
||||||
$routes->get('(:slug)', 'Episode/$1/$2', [
|
$routes->group('(:slug)', function ($routes) {
|
||||||
|
$routes->get('/', 'Episode/$1/$2', [
|
||||||
'as' => 'episode',
|
'as' => 'episode',
|
||||||
]);
|
]);
|
||||||
|
$routes->group('embeddable-player', function ($routes) {
|
||||||
|
$routes->get('/', 'Episode::embeddablePlayer/$1/$2', [
|
||||||
|
'as' => 'embeddable-player',
|
||||||
|
]);
|
||||||
|
$routes->get('(:slug)', 'Episode::embeddablePlayer/$1/$2/$3', [
|
||||||
|
'as' => 'embeddable-player-theme',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
$routes->head('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
$routes->head('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
||||||
$routes->get('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
$routes->get('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
||||||
});
|
});
|
||||||
|
|
|
@ -420,4 +420,21 @@ class Episode extends BaseController
|
||||||
$this->episode->id,
|
$this->episode->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function embeddablePlayer()
|
||||||
|
{
|
||||||
|
helper(['form']);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
'episode' => $this->episode,
|
||||||
|
'themes' => EpisodeModel::$themes,
|
||||||
|
];
|
||||||
|
|
||||||
|
replace_breadcrumb_params([
|
||||||
|
0 => $this->podcast->title,
|
||||||
|
1 => $this->episode->title,
|
||||||
|
]);
|
||||||
|
return view('admin/episode/embeddable_player', $data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,12 @@ class PodcastPlatform extends BaseController
|
||||||
)
|
)
|
||||||
? $podcastPlatform['visible'] == 'yes'
|
? $podcastPlatform['visible'] == 'yes'
|
||||||
: false,
|
: false,
|
||||||
|
'is_on_embeddable_player' => array_key_exists(
|
||||||
|
'on_embeddable_player',
|
||||||
|
$podcastPlatform
|
||||||
|
)
|
||||||
|
? $podcastPlatform['on_embeddable_player'] == 'yes'
|
||||||
|
: false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,8 +49,16 @@ class Analytics extends Controller
|
||||||
public function hit($base64EpisodeData, ...$filename)
|
public function hit($base64EpisodeData, ...$filename)
|
||||||
{
|
{
|
||||||
helper('media', 'analytics');
|
helper('media', 'analytics');
|
||||||
|
$session = \Config\Services::session();
|
||||||
$serviceName = isset($_GET['_from']) ? $_GET['_from'] : '';
|
$session->start();
|
||||||
|
$serviceName = '';
|
||||||
|
if (isset($_GET['_from'])) {
|
||||||
|
$serviceName = $_GET['_from'];
|
||||||
|
} elseif (!empty($session->get('embeddable_player_domain'))) {
|
||||||
|
$serviceName = $session->get('embeddable_player_domain');
|
||||||
|
} elseif ($session->get('referer') !== '- Direct -') {
|
||||||
|
$serviceName = parse_url($session->get('referer'), PHP_URL_HOST);
|
||||||
|
}
|
||||||
|
|
||||||
$episodeData = unpack(
|
$episodeData = unpack(
|
||||||
'IpodcastId/IepisodeId/IbytesThreshold/IfileSize/Iduration/IpublicationDate',
|
'IpodcastId/IepisodeId/IbytesThreshold/IfileSize/Iduration/IpublicationDate',
|
||||||
|
|
|
@ -36,8 +36,9 @@ class Episode extends BaseController
|
||||||
) {
|
) {
|
||||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||||
}
|
}
|
||||||
|
unset($params[1]);
|
||||||
return $this->$method();
|
unset($params[0]);
|
||||||
|
return $this->$method(...$params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
|
@ -54,48 +55,12 @@ class Episode extends BaseController
|
||||||
$this->podcast->type
|
$this->podcast->type
|
||||||
);
|
);
|
||||||
|
|
||||||
|
helper(['persons']);
|
||||||
$persons = [];
|
$persons = [];
|
||||||
foreach ($this->episode->episode_persons as $episodePerson) {
|
construct_episode_person_array(
|
||||||
if (array_key_exists($episodePerson->person->id, $persons)) {
|
$this->episode->episode_persons,
|
||||||
$persons[$episodePerson->person->id]['roles'] .=
|
$persons
|
||||||
empty($episodePerson->person_group) ||
|
|
||||||
empty($episodePerson->person_role)
|
|
||||||
? ''
|
|
||||||
: (empty(
|
|
||||||
$persons[$episodePerson->person->id][
|
|
||||||
'roles'
|
|
||||||
]
|
|
||||||
)
|
|
||||||
? ''
|
|
||||||
: ', ') .
|
|
||||||
lang(
|
|
||||||
'PersonsTaxonomy.persons.' .
|
|
||||||
$episodePerson->person_group .
|
|
||||||
'.roles.' .
|
|
||||||
$episodePerson->person_role .
|
|
||||||
'.label'
|
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
$persons[$episodePerson->person->id] = [
|
|
||||||
'full_name' => $episodePerson->person->full_name,
|
|
||||||
'information_url' =>
|
|
||||||
$episodePerson->person->information_url,
|
|
||||||
'thumbnail_url' =>
|
|
||||||
$episodePerson->person->image->thumbnail_url,
|
|
||||||
'roles' =>
|
|
||||||
empty($episodePerson->person_group) ||
|
|
||||||
empty($episodePerson->person_role)
|
|
||||||
? ''
|
|
||||||
: lang(
|
|
||||||
'PersonsTaxonomy.persons.' .
|
|
||||||
$episodePerson->person_group .
|
|
||||||
'.roles.' .
|
|
||||||
$episodePerson->person_role .
|
|
||||||
'.label'
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'previousEpisode' => $previousNextEpisodes['previous'],
|
'previousEpisode' => $previousNextEpisodes['previous'],
|
||||||
|
@ -120,4 +85,58 @@ class Episode extends BaseController
|
||||||
|
|
||||||
return $cachedView;
|
return $cachedView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function embeddablePlayer($theme = 'light-transparent')
|
||||||
|
{
|
||||||
|
self::triggerWebpageHit($this->episode->podcast_id);
|
||||||
|
|
||||||
|
$session = \Config\Services::session();
|
||||||
|
$session->start();
|
||||||
|
if (isset($_SERVER['HTTP_REFERER'])) {
|
||||||
|
$session->set(
|
||||||
|
'embeddable_player_domain',
|
||||||
|
parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$locale = service('request')->getLocale();
|
||||||
|
|
||||||
|
$cacheName = "page_podcast{$this->episode->podcast_id}_episode{$this->episode->id}_embeddable_player_{$theme}_{$locale}";
|
||||||
|
|
||||||
|
if (!($cachedView = cache($cacheName))) {
|
||||||
|
$episodeModel = new EpisodeModel();
|
||||||
|
$theme = EpisodeModel::$themes[$theme];
|
||||||
|
helper(['persons']);
|
||||||
|
$persons = [];
|
||||||
|
construct_episode_person_array(
|
||||||
|
$this->episode->episode_persons,
|
||||||
|
$persons
|
||||||
|
);
|
||||||
|
constructs_podcast_person_array(
|
||||||
|
$this->podcast->podcast_persons,
|
||||||
|
$persons
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'podcast' => $this->podcast,
|
||||||
|
'episode' => $this->episode,
|
||||||
|
'persons' => $persons,
|
||||||
|
'theme' => $theme,
|
||||||
|
];
|
||||||
|
|
||||||
|
$secondsToNextUnpublishedEpisode = $episodeModel->getSecondsToNextUnpublishedEpisode(
|
||||||
|
$this->podcast->id
|
||||||
|
);
|
||||||
|
|
||||||
|
// The page cache is set to a decade so it is deleted manually upon podcast update
|
||||||
|
return view('embeddable_player', $data, [
|
||||||
|
'cache' => $secondsToNextUnpublishedEpisode
|
||||||
|
? $secondsToNextUnpublishedEpisode
|
||||||
|
: DECADE,
|
||||||
|
'cache_name' => $cacheName,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cachedView;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,48 +109,12 @@ class Podcast extends BaseController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
helper(['persons']);
|
||||||
$persons = [];
|
$persons = [];
|
||||||
foreach ($this->podcast->podcast_persons as $podcastPerson) {
|
constructs_podcast_person_array(
|
||||||
if (array_key_exists($podcastPerson->person->id, $persons)) {
|
$this->podcast->podcast_persons,
|
||||||
$persons[$podcastPerson->person->id]['roles'] .=
|
$persons
|
||||||
empty($podcastPerson->person_group) ||
|
|
||||||
empty($podcastPerson->person_role)
|
|
||||||
? ''
|
|
||||||
: (empty(
|
|
||||||
$persons[$podcastPerson->person->id][
|
|
||||||
'roles'
|
|
||||||
]
|
|
||||||
)
|
|
||||||
? ''
|
|
||||||
: ', ') .
|
|
||||||
lang(
|
|
||||||
'PersonsTaxonomy.persons.' .
|
|
||||||
$podcastPerson->person_group .
|
|
||||||
'.roles.' .
|
|
||||||
$podcastPerson->person_role .
|
|
||||||
'.label'
|
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
$persons[$podcastPerson->person->id] = [
|
|
||||||
'full_name' => $podcastPerson->person->full_name,
|
|
||||||
'information_url' =>
|
|
||||||
$podcastPerson->person->information_url,
|
|
||||||
'thumbnail_url' =>
|
|
||||||
$podcastPerson->person->image->thumbnail_url,
|
|
||||||
'roles' =>
|
|
||||||
empty($podcastPerson->person_group) ||
|
|
||||||
empty($podcastPerson->person_role)
|
|
||||||
? ''
|
|
||||||
: lang(
|
|
||||||
'PersonsTaxonomy.persons.' .
|
|
||||||
$podcastPerson->person_group .
|
|
||||||
'.roles.' .
|
|
||||||
$podcastPerson->person_role .
|
|
||||||
'.label'
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'podcast' => $this->podcast,
|
'podcast' => $this->podcast,
|
||||||
|
|
|
@ -40,6 +40,11 @@ class AddPodcastsPlatforms extends Migration
|
||||||
'constraint' => 1,
|
'constraint' => 1,
|
||||||
'default' => 0,
|
'default' => 0,
|
||||||
],
|
],
|
||||||
|
'is_on_embeddable_player' => [
|
||||||
|
'type' => 'TINYINT',
|
||||||
|
'constraint' => 1,
|
||||||
|
'default' => 0,
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->forge->addPrimaryKey(['podcast_id', 'platform_slug']);
|
$this->forge->addPrimaryKey(['podcast_id', 'platform_slug']);
|
||||||
|
|
|
@ -94,6 +94,13 @@ class Episode extends Entity
|
||||||
*/
|
*/
|
||||||
protected $description;
|
protected $description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The embeddable player URL
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $embeddable_player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
|
@ -421,6 +428,24 @@ class Episode extends Entity
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEmbeddablePlayer($theme = null)
|
||||||
|
{
|
||||||
|
return base_url(
|
||||||
|
$theme
|
||||||
|
? route_to(
|
||||||
|
'embeddable-player-theme',
|
||||||
|
$this->getPodcast()->name,
|
||||||
|
$this->attributes['slug'],
|
||||||
|
$theme
|
||||||
|
)
|
||||||
|
: route_to(
|
||||||
|
'embeddable-player',
|
||||||
|
$this->getPodcast()->name,
|
||||||
|
$this->attributes['slug']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function setGuid(string $guid)
|
public function setGuid(string $guid)
|
||||||
{
|
{
|
||||||
return $this->attributes['guid'] = empty($guid)
|
return $this->attributes['guid'] = empty($guid)
|
||||||
|
|
|
@ -21,5 +21,6 @@ class Platform extends Entity
|
||||||
'link_url' => '?string',
|
'link_url' => '?string',
|
||||||
'link_content' => '?string',
|
'link_content' => '?string',
|
||||||
'is_visible' => '?boolean',
|
'is_visible' => '?boolean',
|
||||||
|
'is_on_embeddable_player' => '?boolean',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,6 +324,24 @@ class Podcast extends Entity
|
||||||
return $this->podcastingPlatforms;
|
return $this->podcastingPlatforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the podcast has podcasting platform links
|
||||||
|
*/
|
||||||
|
public function getHasPodcastingPlatforms()
|
||||||
|
{
|
||||||
|
if (empty($this->id)) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'Podcast must be created before getting podcasting platform.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
foreach ($this->getPodcastingPlatforms() as $podcastingPlatform) {
|
||||||
|
if ($podcastingPlatform->is_on_embeddable_player) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the podcast's social platform links
|
* Returns the podcast's social platform links
|
||||||
*
|
*
|
||||||
|
@ -347,6 +365,24 @@ class Podcast extends Entity
|
||||||
return $this->socialPlatforms;
|
return $this->socialPlatforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the podcast has social platform links
|
||||||
|
*/
|
||||||
|
public function getHasSocialPlatforms()
|
||||||
|
{
|
||||||
|
if (empty($this->id)) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'Podcast must be created before getting social platform.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
foreach ($this->getSocialPlatforms() as $socialPlatform) {
|
||||||
|
if ($socialPlatform->is_on_embeddable_player) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the podcast's funding platform links
|
* Returns the podcast's funding platform links
|
||||||
*
|
*
|
||||||
|
@ -370,6 +406,24 @@ class Podcast extends Entity
|
||||||
return $this->fundingPlatforms;
|
return $this->fundingPlatforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the podcast has social platform links
|
||||||
|
*/
|
||||||
|
public function getHasFundingPlatforms()
|
||||||
|
{
|
||||||
|
if (empty($this->id)) {
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'Podcast must be created before getting Funding platform.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
foreach ($this->getFundingPlatforms() as $fundingPlatform) {
|
||||||
|
if ($fundingPlatform->is_on_embeddable_player) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function getOtherCategories()
|
public function getOtherCategories()
|
||||||
{
|
{
|
||||||
if (empty($this->id)) {
|
if (empty($this->id)) {
|
||||||
|
|
|
@ -111,9 +111,6 @@ function set_user_session_player()
|
||||||
$session->start();
|
$session->start();
|
||||||
|
|
||||||
if (!$session->has('player')) {
|
if (!$session->has('player')) {
|
||||||
$session = \Config\Services::session();
|
|
||||||
$session->start();
|
|
||||||
|
|
||||||
$playerFound = null;
|
$playerFound = null;
|
||||||
$userAgent = $_SERVER['HTTP_USER_AGENT'];
|
$userAgent = $_SERVER['HTTP_USER_AGENT'];
|
||||||
|
|
||||||
|
|
|
@ -384,29 +384,12 @@ if (!function_exists('location_link')) {
|
||||||
$locationOsmid,
|
$locationOsmid,
|
||||||
$class = ''
|
$class = ''
|
||||||
) {
|
) {
|
||||||
$link = null;
|
$link = '';
|
||||||
|
|
||||||
if (!empty($locationName)) {
|
if (!empty($locationName)) {
|
||||||
$uri = '';
|
|
||||||
if (!empty($locationOsmid)) {
|
|
||||||
$uri =
|
|
||||||
'https://www.openstreetmap.org/' .
|
|
||||||
['N' => 'node', 'W' => 'way', 'R' => 'relation'][
|
|
||||||
substr($locationOsmid, 0, 1)
|
|
||||||
] .
|
|
||||||
'/' .
|
|
||||||
substr($locationOsmid, 1);
|
|
||||||
} elseif (!empty($locationGeo)) {
|
|
||||||
$uri =
|
|
||||||
'https://www.openstreetmap.org/#map=17/' .
|
|
||||||
str_replace(',', '/', substr($locationGeo, 4));
|
|
||||||
} else {
|
|
||||||
$uri =
|
|
||||||
'https://www.openstreetmap.org/search?query=' .
|
|
||||||
urlencode($locationName);
|
|
||||||
}
|
|
||||||
$link = button(
|
$link = button(
|
||||||
$locationName,
|
$locationName,
|
||||||
$uri,
|
location_url($locationName, $locationGeo, $locationOsmid),
|
||||||
[
|
[
|
||||||
'variant' => 'default',
|
'variant' => 'default',
|
||||||
'size' => 'small',
|
'size' => 'small',
|
||||||
|
@ -421,6 +404,7 @@ if (!function_exists('location_link')) {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $link;
|
return $link;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright 2021 Podlibre
|
||||||
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
||||||
|
* @link https://castopod.org/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches persons from an episode
|
||||||
|
*
|
||||||
|
* @param array $podcast_persons
|
||||||
|
* @param array &$persons
|
||||||
|
*/
|
||||||
|
function constructs_podcast_person_array($podcast_persons, &$persons)
|
||||||
|
{
|
||||||
|
foreach ($podcast_persons as $podcastPerson) {
|
||||||
|
if (array_key_exists($podcastPerson->person->id, $persons)) {
|
||||||
|
$persons[$podcastPerson->person->id]['roles'] .=
|
||||||
|
empty($podcastPerson->person_group) ||
|
||||||
|
empty($podcastPerson->person_role)
|
||||||
|
? ''
|
||||||
|
: (empty($persons[$podcastPerson->person->id]['roles'])
|
||||||
|
? ''
|
||||||
|
: ', ') .
|
||||||
|
lang(
|
||||||
|
'PersonsTaxonomy.persons.' .
|
||||||
|
$podcastPerson->person_group .
|
||||||
|
'.roles.' .
|
||||||
|
$podcastPerson->person_role .
|
||||||
|
'.label'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$persons[$podcastPerson->person->id] = [
|
||||||
|
'full_name' => $podcastPerson->person->full_name,
|
||||||
|
'information_url' => $podcastPerson->person->information_url,
|
||||||
|
'thumbnail_url' => $podcastPerson->person->image->thumbnail_url,
|
||||||
|
'roles' =>
|
||||||
|
empty($podcastPerson->person_group) ||
|
||||||
|
empty($podcastPerson->person_role)
|
||||||
|
? ''
|
||||||
|
: lang(
|
||||||
|
'PersonsTaxonomy.persons.' .
|
||||||
|
$podcastPerson->person_group .
|
||||||
|
'.roles.' .
|
||||||
|
$podcastPerson->person_role .
|
||||||
|
'.label'
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches persons from an episode
|
||||||
|
*
|
||||||
|
* @param array $episode_persons
|
||||||
|
* @param array &$persons
|
||||||
|
*/
|
||||||
|
function construct_episode_person_array($episode_persons, &$persons)
|
||||||
|
{
|
||||||
|
foreach ($episode_persons as $episodePerson) {
|
||||||
|
if (array_key_exists($episodePerson->person->id, $persons)) {
|
||||||
|
$persons[$episodePerson->person->id]['roles'] .=
|
||||||
|
empty($episodePerson->person_group) ||
|
||||||
|
empty($episodePerson->person_role)
|
||||||
|
? ''
|
||||||
|
: (empty($persons[$episodePerson->person->id]['roles'])
|
||||||
|
? ''
|
||||||
|
: ', ') .
|
||||||
|
lang(
|
||||||
|
'PersonsTaxonomy.persons.' .
|
||||||
|
$episodePerson->person_group .
|
||||||
|
'.roles.' .
|
||||||
|
$episodePerson->person_role .
|
||||||
|
'.label'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$persons[$episodePerson->person->id] = [
|
||||||
|
'full_name' => $episodePerson->person->full_name,
|
||||||
|
'information_url' => $episodePerson->person->information_url,
|
||||||
|
'thumbnail_url' => $episodePerson->person->image->thumbnail_url,
|
||||||
|
'roles' =>
|
||||||
|
empty($episodePerson->person_group) ||
|
||||||
|
empty($episodePerson->person_role)
|
||||||
|
? ''
|
||||||
|
: lang(
|
||||||
|
'PersonsTaxonomy.persons.' .
|
||||||
|
$episodePerson->person_group .
|
||||||
|
'.roles.' .
|
||||||
|
$episodePerson->person_role .
|
||||||
|
'.label'
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,3 +38,39 @@ if (!function_exists('current_season_url')) {
|
||||||
return current_url() . $season_query_string;
|
return current_url() . $season_query_string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!function_exists('location_url')) {
|
||||||
|
/**
|
||||||
|
* Returns URL to display from location info
|
||||||
|
*
|
||||||
|
* @param string $locationName
|
||||||
|
* @param string $locationGeo
|
||||||
|
* @param string $locationOsmid
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function location_url($locationName, $locationGeo, $locationOsmid)
|
||||||
|
{
|
||||||
|
$uri = '';
|
||||||
|
|
||||||
|
if (!empty($locationOsmid)) {
|
||||||
|
$uri =
|
||||||
|
'https://www.openstreetmap.org/' .
|
||||||
|
['N' => 'node', 'W' => 'way', 'R' => 'relation'][
|
||||||
|
substr($locationOsmid, 0, 1)
|
||||||
|
] .
|
||||||
|
'/' .
|
||||||
|
substr($locationOsmid, 1);
|
||||||
|
} elseif (!empty($locationGeo)) {
|
||||||
|
$uri =
|
||||||
|
'https://www.openstreetmap.org/#map=17/' .
|
||||||
|
str_replace(',', '/', substr($locationGeo, 4));
|
||||||
|
} elseif (!empty($locationName)) {
|
||||||
|
$uri =
|
||||||
|
'https://www.openstreetmap.org/search?query=' .
|
||||||
|
urlencode($locationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -32,4 +32,5 @@ return [
|
||||||
'listening-time' => 'listening time',
|
'listening-time' => 'listening time',
|
||||||
'time-periods' => 'time periods',
|
'time-periods' => 'time periods',
|
||||||
'soundbites' => 'soundbites',
|
'soundbites' => 'soundbites',
|
||||||
|
'embeddable-player' => 'embeddable player',
|
||||||
];
|
];
|
||||||
|
|
|
@ -109,4 +109,16 @@ return [
|
||||||
'Click while playing to get current position, click again to get duration.',
|
'Click while playing to get current position, click again to get duration.',
|
||||||
'submit_edit' => 'Save all soundbites',
|
'submit_edit' => 'Save all soundbites',
|
||||||
],
|
],
|
||||||
|
'embeddable_player' => [
|
||||||
|
'add' => 'Add embeddable player',
|
||||||
|
'title' => 'Embeddable player',
|
||||||
|
'label' =>
|
||||||
|
'Pick a theme color, copy the embeddable player to clipboard, then paste it on your website.',
|
||||||
|
'clipboard_iframe' => 'Copy embeddable player to clipboard',
|
||||||
|
'clipboard_url' => 'Copy address to clipboard',
|
||||||
|
'dark' => 'Dark',
|
||||||
|
'dark-transparent' => 'Dark transparent',
|
||||||
|
'light' => 'Light',
|
||||||
|
'light-transparent' => 'Light transparent',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -11,6 +11,7 @@ return [
|
||||||
'home_url' => 'Go to {platformName} website',
|
'home_url' => 'Go to {platformName} website',
|
||||||
'submit_url' => 'Submit your podcast on {platformName}',
|
'submit_url' => 'Submit your podcast on {platformName}',
|
||||||
'visible' => 'Display in podcast homepage?',
|
'visible' => 'Display in podcast homepage?',
|
||||||
|
'on_embeddable_player' => 'Display on embeddable player?',
|
||||||
'remove' => 'Remove {platformName}',
|
'remove' => 'Remove {platformName}',
|
||||||
'submit' => 'Save',
|
'submit' => 'Save',
|
||||||
'messages' => [
|
'messages' => [
|
||||||
|
|
|
@ -32,4 +32,5 @@ return [
|
||||||
'listening-time' => 'drée d’écoute',
|
'listening-time' => 'drée d’écoute',
|
||||||
'time-periods' => 'périodes',
|
'time-periods' => 'périodes',
|
||||||
'soundbites' => 'extraits sonores',
|
'soundbites' => 'extraits sonores',
|
||||||
|
'embeddable-player' => 'lecteur intégré',
|
||||||
];
|
];
|
||||||
|
|
|
@ -111,4 +111,16 @@ return [
|
||||||
'Cliquez pour récupérer la position actuelle, cliquez à nouveau pour récupérer la durée.',
|
'Cliquez pour récupérer la position actuelle, cliquez à nouveau pour récupérer la durée.',
|
||||||
'submit_edit' => 'Enregistrer tous les extraits sonores',
|
'submit_edit' => 'Enregistrer tous les extraits sonores',
|
||||||
],
|
],
|
||||||
|
'embeddable_player' => [
|
||||||
|
'add' => 'Ajouter un lecteur intégré',
|
||||||
|
'title' => 'Lecteur intégré',
|
||||||
|
'label' =>
|
||||||
|
'Sélectionnez une couleur de thème, copiez le code dans le presse-papier, puis collez-le sur votre site internet.',
|
||||||
|
'clipboard_iframe' => 'Copier le lecteur dans le presse papier',
|
||||||
|
'clipboard_url' => 'Copier l’adresse dans le presse papier',
|
||||||
|
'dark' => 'Sombre',
|
||||||
|
'dark-transparent' => 'Sombre transparent',
|
||||||
|
'light' => 'Clair',
|
||||||
|
'light-transparent' => 'Clair transparent',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -11,6 +11,7 @@ return [
|
||||||
'home_url' => 'Aller au site {platformName}',
|
'home_url' => 'Aller au site {platformName}',
|
||||||
'submit_url' => 'Soumettez votre podcast sur {platformName}',
|
'submit_url' => 'Soumettez votre podcast sur {platformName}',
|
||||||
'visible' => 'Afficher sur la page d’accueil du podcast ?',
|
'visible' => 'Afficher sur la page d’accueil du podcast ?',
|
||||||
|
'on_embeddable_player' => 'Afficher sur le lecteur intégré ?',
|
||||||
'remove' => 'Supprimer {platformName}',
|
'remove' => 'Supprimer {platformName}',
|
||||||
'submit' => 'Enregistrer',
|
'submit' => 'Enregistrer',
|
||||||
'messages' => [
|
'messages' => [
|
||||||
|
|
|
@ -69,6 +69,35 @@ class EpisodeModel extends Model
|
||||||
protected $afterUpdate = ['writeEnclosureMetadata'];
|
protected $afterUpdate = ['writeEnclosureMetadata'];
|
||||||
protected $beforeDelete = ['clearCache'];
|
protected $beforeDelete = ['clearCache'];
|
||||||
|
|
||||||
|
public static $themes = [
|
||||||
|
'light-transparent' => [
|
||||||
|
'style' =>
|
||||||
|
'background-color: #fff; background-image: linear-gradient(45deg, #ccc 12.5%, transparent 12.5%, transparent 50%, #ccc 50%, #ccc 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;',
|
||||||
|
'background' => 'transparent',
|
||||||
|
'text' => '#000',
|
||||||
|
'inverted' => '#fff',
|
||||||
|
],
|
||||||
|
'light' => [
|
||||||
|
'style' => 'background-color: #fff;',
|
||||||
|
'background' => '#fff',
|
||||||
|
'text' => '#000',
|
||||||
|
'inverted' => '#fff',
|
||||||
|
],
|
||||||
|
'dark-transparent' => [
|
||||||
|
'style' =>
|
||||||
|
'background-color: #001f1a; background-image: linear-gradient(45deg, #888 12.5%, transparent 12.5%, transparent 50%, #888 50%, #888 62.5%, transparent 62.5%, transparent 100%); background-size: 5.66px 5.66px;',
|
||||||
|
'background' => 'transparent',
|
||||||
|
'text' => '#fff',
|
||||||
|
'inverted' => '#000',
|
||||||
|
],
|
||||||
|
'dark' => [
|
||||||
|
'style' => 'background-color: #001f1a;',
|
||||||
|
'background' => '#001f1a',
|
||||||
|
'text' => '#fff',
|
||||||
|
'inverted' => '#000',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
public function getEpisodeBySlug($podcastId, $episodeSlug)
|
public function getEpisodeBySlug($podcastId, $episodeSlug)
|
||||||
{
|
{
|
||||||
if (!($found = cache("podcast{$podcastId}_episode@{$episodeSlug}"))) {
|
if (!($found = cache("podcast{$podcastId}_episode@{$episodeSlug}"))) {
|
||||||
|
@ -411,6 +440,14 @@ class EpisodeModel extends Model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (array_keys(self::$themes) as $themeKey) {
|
||||||
|
foreach ($supportedLocales as $locale) {
|
||||||
|
cache()->delete(
|
||||||
|
"page_podcast{$episode->podcast_id}_episode{$episode->id}_embeddable_player_{$themeKey}_{$locale}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// delete query cache
|
// delete query cache
|
||||||
cache()->delete("podcast{$episode->podcast_id}_defaultQuery");
|
cache()->delete("podcast{$episode->podcast_id}_defaultQuery");
|
||||||
cache()->delete("podcast{$episode->podcast_id}_years");
|
cache()->delete("podcast{$episode->podcast_id}_years");
|
||||||
|
|
|
@ -75,7 +75,7 @@ class PlatformModel extends Model
|
||||||
!($found = cache("podcast{$podcastId}_platforms_{$platformType}"))
|
!($found = cache("podcast{$podcastId}_platforms_{$platformType}"))
|
||||||
) {
|
) {
|
||||||
$found = $this->select(
|
$found = $this->select(
|
||||||
'platforms.*, podcasts_platforms.link_url, podcasts_platforms.link_content, podcasts_platforms.is_visible'
|
'platforms.*, podcasts_platforms.link_url, podcasts_platforms.link_content, podcasts_platforms.is_visible, podcasts_platforms.is_on_embeddable_player'
|
||||||
)
|
)
|
||||||
->join(
|
->join(
|
||||||
'podcasts_platforms',
|
'podcasts_platforms',
|
||||||
|
@ -103,7 +103,7 @@ class PlatformModel extends Model
|
||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
$found = $this->select(
|
$found = $this->select(
|
||||||
'platforms.*, podcasts_platforms.link_url, podcasts_platforms.link_content, podcasts_platforms.is_visible'
|
'platforms.*, podcasts_platforms.link_url, podcasts_platforms.link_content, podcasts_platforms.is_visible, podcasts_platforms.is_on_embeddable_player'
|
||||||
)
|
)
|
||||||
->join(
|
->join(
|
||||||
'podcasts_platforms',
|
'podcasts_platforms',
|
||||||
|
@ -168,6 +168,8 @@ EOD;
|
||||||
|
|
||||||
public function clearCache($podcastId)
|
public function clearCache($podcastId)
|
||||||
{
|
{
|
||||||
|
$podcast = (new PodcastModel())->getPodcastById($podcastId);
|
||||||
|
|
||||||
foreach (['podcasting', 'social', 'funding'] as $platformType) {
|
foreach (['podcasting', 'social', 'funding'] as $platformType) {
|
||||||
cache()->delete("podcast{$podcastId}_platforms_{$platformType}");
|
cache()->delete("podcast{$podcastId}_platforms_{$platformType}");
|
||||||
cache()->delete(
|
cache()->delete(
|
||||||
|
@ -195,5 +197,22 @@ EOD;
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear cache for every localized podcast episode page
|
||||||
|
foreach ($podcast->episodes as $episode) {
|
||||||
|
foreach ($supportedLocales as $locale) {
|
||||||
|
cache()->delete(
|
||||||
|
"page_podcast{$podcast->id}_episode{$episode->id}_{$locale}"
|
||||||
|
);
|
||||||
|
foreach (
|
||||||
|
array_keys(\App\Models\EpisodeModel::$themes)
|
||||||
|
as $themeKey
|
||||||
|
) {
|
||||||
|
cache()->delete(
|
||||||
|
"page_podcast{$podcast->id}_episode{$episode->id}_embeddable_player_{$themeKey}_{$locale}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,6 +193,14 @@ class PodcastModel extends Model
|
||||||
cache()->delete(
|
cache()->delete(
|
||||||
"page_podcast{$podcast->id}_episode{$episode->id}_{$locale}"
|
"page_podcast{$podcast->id}_episode{$episode->id}_{$locale}"
|
||||||
);
|
);
|
||||||
|
foreach (
|
||||||
|
array_keys(\App\Models\EpisodeModel::$themes)
|
||||||
|
as $themeKey
|
||||||
|
) {
|
||||||
|
cache()->delete(
|
||||||
|
"page_podcast{$podcast->id}_episode{$episode->id}_embeddable_player_{$themeKey}_{$locale}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// clear cache for every credit page
|
// clear cache for every credit page
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import ClientTimezone from "./modules/ClientTimezone";
|
import ClientTimezone from "./modules/ClientTimezone";
|
||||||
|
import Clipboard from "./modules/Clipboard";
|
||||||
|
import ThemePicker from "./modules/ThemePicker";
|
||||||
import DateTimePicker from "./modules/DateTimePicker";
|
import DateTimePicker from "./modules/DateTimePicker";
|
||||||
import Dropdown from "./modules/Dropdown";
|
import Dropdown from "./modules/Dropdown";
|
||||||
import MarkdownEditor from "./modules/MarkdownEditor";
|
import MarkdownEditor from "./modules/MarkdownEditor";
|
||||||
|
@ -19,3 +21,5 @@ ClientTimezone();
|
||||||
DateTimePicker();
|
DateTimePicker();
|
||||||
Time();
|
Time();
|
||||||
Soundbites();
|
Soundbites();
|
||||||
|
Clipboard();
|
||||||
|
ThemePicker();
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 6V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1.001 1.001 0 0 1 3 21l.003-14c0-.552.45-1 1.006-1H7zM5.002 8L5 20h10V8H5.002zM9 6h8v10h2V4H9v2zm-2 5h6v2H7v-2zm0 4h6v2H7v-2z"/></svg>
|
After Width: | Height: | Size: 350 B |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M2 3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993zM4 5v14h16V5H4zm6.622 3.415l4.879 3.252a.4.4 0 0 1 0 .666l-4.88 3.252a.4.4 0 0 1-.621-.332V8.747a.4.4 0 0 1 .622-.332z"/></svg>
|
After Width: | Height: | Size: 376 B |
|
@ -0,0 +1,23 @@
|
||||||
|
const Clipboard = (): void => {
|
||||||
|
const buttons: NodeListOf<
|
||||||
|
HTMLButtonElement
|
||||||
|
> | null = document.querySelectorAll("button[data-type='clipboard-copy']");
|
||||||
|
|
||||||
|
if (buttons) {
|
||||||
|
for (let i = 0; i < buttons.length; i++) {
|
||||||
|
const button: HTMLButtonElement = buttons[i];
|
||||||
|
const textArea: HTMLTextAreaElement | null = document.querySelector(
|
||||||
|
`textarea[id="${button.dataset.clipboardTarget}"]`
|
||||||
|
);
|
||||||
|
if (textArea) {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
textArea.select();
|
||||||
|
textArea.setSelectionRange(0, textArea.value.length);
|
||||||
|
document.execCommand("copy");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Clipboard;
|
|
@ -0,0 +1,30 @@
|
||||||
|
const ThemePicker = (): void => {
|
||||||
|
const buttons: NodeListOf<
|
||||||
|
HTMLButtonElement
|
||||||
|
> | null = document.querySelectorAll("button[data-type='theme-picker']");
|
||||||
|
const iframe: HTMLIFrameElement | null = document.querySelector(
|
||||||
|
`iframe[id="embeddable_player"]`
|
||||||
|
);
|
||||||
|
const iframeTextArea: HTMLTextAreaElement | null = document.querySelector(
|
||||||
|
`textarea[id="iframe"]`
|
||||||
|
);
|
||||||
|
const urlTextArea: HTMLTextAreaElement | null = document.querySelector(
|
||||||
|
`textarea[id="url"]`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (buttons && iframe && iframeTextArea && urlTextArea) {
|
||||||
|
for (let i = 0; i < buttons.length; i++) {
|
||||||
|
const button: HTMLButtonElement = buttons[i];
|
||||||
|
const url: string | undefined = button.dataset.url;
|
||||||
|
if (url) {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
iframeTextArea.value = `<iframe width="100%" height="280" frameborder="0" scrolling="no" style="width: 100%; height: 280px; overflow: hidden;" src="${url}"></iframe>`;
|
||||||
|
urlTextArea.value = url;
|
||||||
|
iframe.src = url;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemePicker;
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?= $this->extend('admin/_layout') ?>
|
||||||
|
|
||||||
|
<?= $this->section('title') ?>
|
||||||
|
<?= lang('Episode.embeddable_player.title') ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('pageTitle') ?>
|
||||||
|
<?= lang('Episode.embeddable_player.title') ?>
|
||||||
|
<?= $this->endSection() ?>
|
||||||
|
|
||||||
|
<?= $this->section('content') ?>
|
||||||
|
|
||||||
|
<?= form_label(lang('Episode.embeddable_player.label'), 'label') ?>
|
||||||
|
|
||||||
|
<div class="flex w-full mt-6 mb-6">
|
||||||
|
<?php foreach ($themes as $themeKey => $theme): ?>
|
||||||
|
<button style="<?= $theme[
|
||||||
|
'style'
|
||||||
|
] ?>" class="w-12 h-12 mr-1 border-2 border-gray-400 rounded-lg hover:border-white" title="<?= lang("Episode.embeddable_player.{$themeKey}") ?>" data-type="theme-picker" data-url="<?= $episode->getEmbeddablePlayer(
|
||||||
|
$themeKey
|
||||||
|
) ?>"></button>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<iframe name="embeddable_player" id="embeddable_player" width="100%" height="280" frameborder="0" scrolling="no" style="width: 100%; height: 280; overflow: hidden;" src="<?= $episode->embeddable_player ?>"></iframe>
|
||||||
|
|
||||||
|
<div class="flex items-center w-full mt-8">
|
||||||
|
<?= form_textarea(
|
||||||
|
[
|
||||||
|
'id' => 'iframe',
|
||||||
|
'name' => 'iframe',
|
||||||
|
'class' => 'form-textarea w-full h-20 mr-2',
|
||||||
|
],
|
||||||
|
"<iframe width=\"100%\" height=\"280\" frameborder=\"0\" scrolling=\"no\" style=\"width: 100%; height: 280px; overflow: hidden;\" src=\"{$episode->embeddable_player}\"></iframe>"
|
||||||
|
) ?>
|
||||||
|
<?= icon_button(
|
||||||
|
'file-copy',
|
||||||
|
lang('Episode.embeddable_player.clipboard_iframe'),
|
||||||
|
null,
|
||||||
|
['variant' => 'default'],
|
||||||
|
[
|
||||||
|
'data-type' => 'clipboard-copy',
|
||||||
|
'data-clipboard-target' => 'iframe',
|
||||||
|
]
|
||||||
|
) ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center w-full mt-4">
|
||||||
|
<?= form_textarea(
|
||||||
|
[
|
||||||
|
'id' => 'url',
|
||||||
|
'name' => 'url',
|
||||||
|
'class' => 'form-textarea w-full h-10 mr-2',
|
||||||
|
],
|
||||||
|
$episode->embeddable_player
|
||||||
|
) ?>
|
||||||
|
<?= icon_button(
|
||||||
|
'file-copy',
|
||||||
|
lang('Episode.embeddable_player.clipboard_url'),
|
||||||
|
null,
|
||||||
|
['variant' => 'default'],
|
||||||
|
['data-type' => 'clipboard-copy', 'data-clipboard-target' => 'url']
|
||||||
|
) ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= $this->endSection() ?>
|
|
@ -61,6 +61,13 @@
|
||||||
$podcast->id,
|
$podcast->id,
|
||||||
$episode->id
|
$episode->id
|
||||||
) ?>"><?= lang('Episode.edit') ?></a>
|
) ?>"><?= lang('Episode.edit') ?></a>
|
||||||
|
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
|
||||||
|
'embeddable-player-add',
|
||||||
|
$podcast->id,
|
||||||
|
$episode->id
|
||||||
|
) ?>"><?= lang(
|
||||||
|
'Episode.embeddable_player.add'
|
||||||
|
) ?></a>
|
||||||
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
|
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
|
||||||
'episode-person-manage',
|
'episode-person-manage',
|
||||||
$podcast->id,
|
$podcast->id,
|
||||||
|
|
|
@ -58,6 +58,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-12">
|
<div class="mb-12">
|
||||||
|
<?= button(
|
||||||
|
lang('Episode.embeddable_player.add'),
|
||||||
|
route_to('embeddable-player-add', $podcast->id, $episode->id),
|
||||||
|
['variant' => 'info', 'iconLeft' => 'movie'],
|
||||||
|
['class' => 'mb-4']
|
||||||
|
) ?>
|
||||||
<?= button(
|
<?= button(
|
||||||
lang('Episode.soundbites_form.title'),
|
lang('Episode.soundbites_form.title'),
|
||||||
route_to('soundbites-edit', $podcast->id, $episode->id),
|
route_to('soundbites-edit', $podcast->id, $episode->id),
|
||||||
|
|
|
@ -58,6 +58,13 @@
|
||||||
$podcast->id,
|
$podcast->id,
|
||||||
$episode->id
|
$episode->id
|
||||||
) ?>"><?= lang('Episode.edit') ?></a>
|
) ?>"><?= lang('Episode.edit') ?></a>
|
||||||
|
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
|
||||||
|
'embeddable-player-add',
|
||||||
|
$podcast->id,
|
||||||
|
$episode->id
|
||||||
|
) ?>"><?= lang(
|
||||||
|
'Episode.embeddable_player.add'
|
||||||
|
) ?></a>
|
||||||
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
|
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
|
||||||
'episode-person-manage',
|
'episode-person-manage',
|
||||||
$podcast->id,
|
$podcast->id,
|
||||||
|
|
|
@ -115,6 +115,22 @@
|
||||||
$platform->slug . '_visible',
|
$platform->slug . '_visible',
|
||||||
$platform->is_visible ? $platform->is_visible : false
|
$platform->is_visible ? $platform->is_visible : false
|
||||||
),
|
),
|
||||||
|
'text-sm mb-1'
|
||||||
|
) ?>
|
||||||
|
<?= form_switch(
|
||||||
|
lang('Platforms.on_embeddable_player'),
|
||||||
|
[
|
||||||
|
'id' => $platform->slug . '_on_embeddable_player',
|
||||||
|
'name' =>
|
||||||
|
'platforms[' . $platform->slug . '][on_embeddable_player]',
|
||||||
|
],
|
||||||
|
'yes',
|
||||||
|
old(
|
||||||
|
$platform->slug . '_on_embeddable_player',
|
||||||
|
$platform->is_on_embeddable_player
|
||||||
|
? $platform->is_on_embeddable_player
|
||||||
|
: false
|
||||||
|
),
|
||||||
'text-sm'
|
'text-sm'
|
||||||
) ?>
|
) ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="<?= service('request')->getLocale() ?>">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title><?= $episode->title ?></title>
|
||||||
|
<meta name="description"
|
||||||
|
content="<?= htmlspecialchars($episode->description) ?>"/>
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
|
<link rel="stylesheet" href="/assets/index.css" />
|
||||||
|
<link rel="canonical" href="<?= $episode->link ?>" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="flex w-full p-1 md:p-2"style="background: <?= $theme[
|
||||||
|
'background'
|
||||||
|
] ?>; color: <?= $theme['text'] ?>;">
|
||||||
|
<img src="<?= $episode->image
|
||||||
|
->medium_url ?>" alt="<?= $episode->title ?>" class="w-32 h-32 md:w-64 md:h-64" />
|
||||||
|
<div class="flex-grow pl-4">
|
||||||
|
<div class="flex">
|
||||||
|
<a href="<?= route_to('podcast', $podcast->name) ?>"
|
||||||
|
style="color: <?= $theme['text'] ?>;"
|
||||||
|
class="flex flex-col text-base leading-tight opacity-50 md:text-lg hover:opacity-100" target="_blank">
|
||||||
|
<?= $podcast->title ?>
|
||||||
|
</a>
|
||||||
|
<address class="ml-2 text-xs opacity-50 md:text-sm">
|
||||||
|
<?= lang('Podcast.by', [
|
||||||
|
'publisher' => $podcast->publisher,
|
||||||
|
]) ?></address>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex mt-1 space-x-2 md:space-x-4 md:mt-3 md:top-0 md:mr-4 md:right-0 md:absolute ">
|
||||||
|
<?php if ($podcast->has_social_platforms): ?>
|
||||||
|
<div class="flex space-x-1">
|
||||||
|
<?php foreach (
|
||||||
|
$podcast->social_platforms
|
||||||
|
as $socialPlatform
|
||||||
|
): ?>
|
||||||
|
<?php if (
|
||||||
|
$socialPlatform->is_on_embeddable_player
|
||||||
|
): ?>
|
||||||
|
<?= anchor(
|
||||||
|
$socialPlatform->link_url,
|
||||||
|
platform_icon(
|
||||||
|
$socialPlatform->type,
|
||||||
|
$socialPlatform->slug,
|
||||||
|
'h-4 md:h-6'
|
||||||
|
),
|
||||||
|
[
|
||||||
|
'target' => '_blank',
|
||||||
|
'rel' => 'noopener noreferrer',
|
||||||
|
'title' => $socialPlatform->label,
|
||||||
|
'class' =>
|
||||||
|
'opacity-50 hover:opacity-100',
|
||||||
|
]
|
||||||
|
) ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($podcast->has_funding_platforms): ?>
|
||||||
|
<div class="flex space-x-1">
|
||||||
|
<?php foreach (
|
||||||
|
$podcast->funding_platforms
|
||||||
|
as $fundingPlatform
|
||||||
|
): ?>
|
||||||
|
<?php if (
|
||||||
|
$fundingPlatform->is_on_embeddable_player
|
||||||
|
): ?>
|
||||||
|
<?= anchor(
|
||||||
|
$fundingPlatform->link_url,
|
||||||
|
platform_icon(
|
||||||
|
$fundingPlatform->type,
|
||||||
|
$fundingPlatform->slug,
|
||||||
|
'h-4 md:h-6'
|
||||||
|
),
|
||||||
|
[
|
||||||
|
'target' => '_blank',
|
||||||
|
'rel' => 'noopener noreferrer',
|
||||||
|
'title' => $fundingPlatform->label,
|
||||||
|
'class' =>
|
||||||
|
'opacity-50 hover:opacity-100',
|
||||||
|
]
|
||||||
|
) ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="flex space-x-1">
|
||||||
|
<?php foreach (
|
||||||
|
$podcast->podcasting_platforms
|
||||||
|
as $podcastingPlatform
|
||||||
|
): ?>
|
||||||
|
<?php if ($podcastingPlatform->is_on_embeddable_player): ?>
|
||||||
|
<?= anchor(
|
||||||
|
$podcastingPlatform->link_url,
|
||||||
|
platform_icon(
|
||||||
|
$podcastingPlatform->type,
|
||||||
|
$podcastingPlatform->slug,
|
||||||
|
'h-4 md:h-6'
|
||||||
|
),
|
||||||
|
[
|
||||||
|
'target' => '_blank',
|
||||||
|
'rel' => 'noopener noreferrer',
|
||||||
|
'title' => $podcastingPlatform->label,
|
||||||
|
'class' => 'opacity-50 hover:opacity-100',
|
||||||
|
]
|
||||||
|
) ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?= anchor(
|
||||||
|
route_to('podcast_feed', $podcast->name),
|
||||||
|
icon('rss', 'mr-2') . lang('Podcast.feed'),
|
||||||
|
[
|
||||||
|
'target' => '_blank',
|
||||||
|
'class' =>
|
||||||
|
'text-white h-4 md:h-6 md:text-sm text-xs bg-gradient-to-r from-orange-400 to-red-500 hover:to-orange-500 hover:bg-orange-500 inline-flex items-center px-2 py-1 font-semibold rounded-md md:rounded-lg shadow-md hover:bg-orange-600',
|
||||||
|
]
|
||||||
|
) ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h1 class="mt-2 text-xl font-semibold opacity-100 md:text-3xl hover:opacity-75">
|
||||||
|
<a href="<?= $episode->link ?>"
|
||||||
|
style="color: <?= $theme['text'] ?>;"
|
||||||
|
target="_blank">
|
||||||
|
<?= $episode->title ?>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
<div class="flex w-full">
|
||||||
|
<div
|
||||||
|
style="color: <?= $theme['text'] ?>;"
|
||||||
|
class="text-sm opacity-50 md:text-base">
|
||||||
|
<?= episode_numbering(
|
||||||
|
$episode->number,
|
||||||
|
$episode->season_number
|
||||||
|
) ?>
|
||||||
|
<div>
|
||||||
|
<time
|
||||||
|
pubdate
|
||||||
|
datetime="<?= $episode->published_at->format(
|
||||||
|
DateTime::ATOM
|
||||||
|
) ?>"
|
||||||
|
title="<?= $episode->published_at ?>">
|
||||||
|
<?= lang('Common.mediumDate', [
|
||||||
|
$episode->published_at,
|
||||||
|
]) ?>
|
||||||
|
</time>
|
||||||
|
<span>•</span>
|
||||||
|
<time datetime="PT<?= $episode->enclosure_duration ?>S">
|
||||||
|
<?= format_duration($episode->enclosure_duration) ?>
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php if ($episode->location_name): ?>
|
||||||
|
<a href="<?= location_url(
|
||||||
|
$episode->location_name,
|
||||||
|
$episode->location_geo,
|
||||||
|
$episode->location_osmid
|
||||||
|
) ?>"
|
||||||
|
style="color: <?= $theme['inverted'] ?>; background: <?= $theme[
|
||||||
|
'text'
|
||||||
|
] ?>;" class="inline-flex items-center px-3 py-1 mt-1 ml-4 text-xs align-middle rounded-full shadow-xs outline-none opacity-50 md:mt-2 md:text-sm hover:opacity-75 focus:shadow-outline" target="_blank" rel="noreferrer noopener"><?= icon(
|
||||||
|
'map-pin'
|
||||||
|
) ?>
|
||||||
|
<?= $episode->location_name ?>
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (!empty($persons)): ?>
|
||||||
|
<div class="flex my-2 space-x-1 md:my-4 md:space-x-2">
|
||||||
|
<?php foreach ($persons as $person): ?>
|
||||||
|
<?php if (!empty($person['information_url'])): ?>
|
||||||
|
<a href="<?= $person['information_url'] ?>"
|
||||||
|
class="hover:opacity-50"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener">
|
||||||
|
<?php endif; ?>
|
||||||
|
<img src="<?= $person['thumbnail_url'] ?>"
|
||||||
|
alt="<?= $person['full_name'] ?>"
|
||||||
|
title="[<?= $person[
|
||||||
|
'full_name'
|
||||||
|
] ?>] <?= $person['roles'] ?>"
|
||||||
|
class="object-cover h-8 rounded-full md:h-12 md:w-12" />
|
||||||
|
<?php if (!empty($person['information_url'])): ?>
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<audio controls preload="none" class="flex w-full mt-2 md:mt-4">
|
||||||
|
<source
|
||||||
|
src="<?= $episode->enclosure_url .
|
||||||
|
(isset($_SERVER['HTTP_REFERER'])
|
||||||
|
? '?_from=' .
|
||||||
|
parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)
|
||||||
|
: '') ?>"
|
||||||
|
type="<?= $episode->enclosure_type ?>" />
|
||||||
|
Your browser does not support the audio tag.
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<a href="https://castopod.org/"
|
||||||
|
class="absolute bottom-0 right-0 mb-4 mr-4 hover:opacity-75"
|
||||||
|
title="<?= lang('Common.powered_by', [
|
||||||
|
'castopod' => 'Castopod',
|
||||||
|
]) ?>"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer">
|
||||||
|
<?= platform_icon('podcasting', 'castopod', 'h-6') ?>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -5,15 +5,16 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<title><?= $episode->title ?></title>
|
<title><?= $episode->title ?></title>
|
||||||
<meta name="description" content="<?= $episode->description ?>"/>
|
<meta name="description"
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
content="<?= htmlspecialchars($episode->description) ?>" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<?php if (
|
<?php if (
|
||||||
!empty($podcast->payment_pointer)
|
!empty($podcast->payment_pointer)
|
||||||
): ?> <meta name="monetization" content="<?= $podcast->payment_pointer ?>">
|
): ?> <meta name="monetization" content="<?= $podcast->payment_pointer ?>" />
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
|
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
|
||||||
<link rel="stylesheet" href="/assets/index.css"/>
|
<link rel="stylesheet" href="/assets/index.css"/>
|
||||||
<link rel="canonical" href="<?= current_url() ?>" />
|
<link rel="canonical" href="<?= $episode->link ?>" />
|
||||||
<script src="/assets/podcast.js" type="module" defer></script>
|
<script src="/assets/podcast.js" type="module" defer></script>
|
||||||
<meta property="og:title" content="<?= $episode->title ?>" />
|
<meta property="og:title" content="<?= $episode->title ?>" />
|
||||||
<meta property="og:locale" content="<?= $podcast->language_code ?>" />
|
<meta property="og:locale" content="<?= $podcast->language_code ?>" />
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<title><?= $podcast->title ?></title>
|
<title><?= $podcast->title ?></title>
|
||||||
<meta name="description" content="<?= $podcast->description ?>"/>
|
<meta name="description"
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
content="<?= htmlspecialchars($podcast->description) ?>" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<?php if (
|
<?php if (
|
||||||
!empty($podcast->payment_pointer)
|
!empty($podcast->payment_pointer)
|
||||||
): ?> <meta name="monetization" content="<?= $podcast->payment_pointer ?>">
|
): ?> <meta name="monetization" content="<?= $podcast->payment_pointer ?>" />
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
|
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
|
||||||
<link rel="stylesheet" href="/assets/index.css"/>
|
<link rel="stylesheet" href="/assets/index.css"/>
|
||||||
|
|
Loading…
Reference in New Issue