mirror of
https://code.castopod.org/adaures/castopod.git
synced 2024-09-28 12:41:47 +02:00
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',
|
||||
]
|
||||
);
|
||||
$routes->get(
|
||||
'embeddable-player',
|
||||
'Episode::embeddablePlayer/$1/$2',
|
||||
[
|
||||
'as' => 'embeddable-player-add',
|
||||
'filter' => 'permission:podcast_episodes-edit',
|
||||
]
|
||||
);
|
||||
|
||||
$routes->group('persons', function ($routes) {
|
||||
$routes->get('/', 'EpisodePerson/$1/$2', [
|
||||
@ -565,9 +573,19 @@ $routes->group(config('App')->authGateway, function ($routes) {
|
||||
// Public routes
|
||||
$routes->group('@(:podcastName)', function ($routes) {
|
||||
$routes->get('/', 'Podcast/$1', ['as' => 'podcast']);
|
||||
$routes->get('(:slug)', 'Episode/$1/$2', [
|
||||
$routes->group('(:slug)', function ($routes) {
|
||||
$routes->get('/', 'Episode/$1/$2', [
|
||||
'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->get('feed.xml', 'Feed/$1', ['as' => 'podcast_feed']);
|
||||
});
|
||||
|
@ -420,4 +420,21 @@ class Episode extends BaseController
|
||||
$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'
|
||||
: 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)
|
||||
{
|
||||
helper('media', 'analytics');
|
||||
|
||||
$serviceName = isset($_GET['_from']) ? $_GET['_from'] : '';
|
||||
$session = \Config\Services::session();
|
||||
$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(
|
||||
'IpodcastId/IepisodeId/IbytesThreshold/IfileSize/Iduration/IpublicationDate',
|
||||
|
@ -36,8 +36,9 @@ class Episode extends BaseController
|
||||
) {
|
||||
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
|
||||
}
|
||||
|
||||
return $this->$method();
|
||||
unset($params[1]);
|
||||
unset($params[0]);
|
||||
return $this->$method(...$params);
|
||||
}
|
||||
|
||||
public function index()
|
||||
@ -54,48 +55,12 @@ class Episode extends BaseController
|
||||
$this->podcast->type
|
||||
);
|
||||
|
||||
helper(['persons']);
|
||||
$persons = [];
|
||||
foreach ($this->episode->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'
|
||||
construct_episode_person_array(
|
||||
$this->episode->episode_persons,
|
||||
$persons
|
||||
);
|
||||
} 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 = [
|
||||
'previousEpisode' => $previousNextEpisodes['previous'],
|
||||
@ -120,4 +85,58 @@ class Episode extends BaseController
|
||||
|
||||
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 = [];
|
||||
foreach ($this->podcast->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'
|
||||
constructs_podcast_person_array(
|
||||
$this->podcast->podcast_persons,
|
||||
$persons
|
||||
);
|
||||
} 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 = [
|
||||
'podcast' => $this->podcast,
|
||||
|
@ -40,6 +40,11 @@ class AddPodcastsPlatforms extends Migration
|
||||
'constraint' => 1,
|
||||
'default' => 0,
|
||||
],
|
||||
'is_on_embeddable_player' => [
|
||||
'type' => 'TINYINT',
|
||||
'constraint' => 1,
|
||||
'default' => 0,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->forge->addPrimaryKey(['podcast_id', 'platform_slug']);
|
||||
|
@ -94,6 +94,13 @@ class Episode extends Entity
|
||||
*/
|
||||
protected $description;
|
||||
|
||||
/**
|
||||
* The embeddable player URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $embeddable_player;
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
return $this->attributes['guid'] = empty($guid)
|
||||
|
@ -21,5 +21,6 @@ class Platform extends Entity
|
||||
'link_url' => '?string',
|
||||
'link_content' => '?string',
|
||||
'is_visible' => '?boolean',
|
||||
'is_on_embeddable_player' => '?boolean',
|
||||
];
|
||||
}
|
||||
|
@ -324,6 +324,24 @@ class Podcast extends Entity
|
||||
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
|
||||
*
|
||||
@ -347,6 +365,24 @@ class Podcast extends Entity
|
||||
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
|
||||
*
|
||||
@ -370,6 +406,24 @@ class Podcast extends Entity
|
||||
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()
|
||||
{
|
||||
if (empty($this->id)) {
|
||||
|
@ -111,9 +111,6 @@ function set_user_session_player()
|
||||
$session->start();
|
||||
|
||||
if (!$session->has('player')) {
|
||||
$session = \Config\Services::session();
|
||||
$session->start();
|
||||
|
||||
$playerFound = null;
|
||||
$userAgent = $_SERVER['HTTP_USER_AGENT'];
|
||||
|
||||
|
@ -384,29 +384,12 @@ if (!function_exists('location_link')) {
|
||||
$locationOsmid,
|
||||
$class = ''
|
||||
) {
|
||||
$link = null;
|
||||
$link = '';
|
||||
|
||||
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(
|
||||
$locationName,
|
||||
$uri,
|
||||
location_url($locationName, $locationGeo, $locationOsmid),
|
||||
[
|
||||
'variant' => 'default',
|
||||
'size' => 'small',
|
||||
@ -421,6 +404,7 @@ if (!function_exists('location_link')) {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $link;
|
||||
}
|
||||
}
|
||||
|
97
app/Helpers/persons_helper.php
Normal file
97
app/Helpers/persons_helper.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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',
|
||||
'time-periods' => 'time periods',
|
||||
'soundbites' => 'soundbites',
|
||||
'embeddable-player' => 'embeddable player',
|
||||
];
|
||||
|
@ -109,4 +109,16 @@ return [
|
||||
'Click while playing to get current position, click again to get duration.',
|
||||
'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',
|
||||
'submit_url' => 'Submit your podcast on {platformName}',
|
||||
'visible' => 'Display in podcast homepage?',
|
||||
'on_embeddable_player' => 'Display on embeddable player?',
|
||||
'remove' => 'Remove {platformName}',
|
||||
'submit' => 'Save',
|
||||
'messages' => [
|
||||
|
@ -32,4 +32,5 @@ return [
|
||||
'listening-time' => 'drée d’écoute',
|
||||
'time-periods' => 'périodes',
|
||||
'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.',
|
||||
'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}',
|
||||
'submit_url' => 'Soumettez votre podcast sur {platformName}',
|
||||
'visible' => 'Afficher sur la page d’accueil du podcast ?',
|
||||
'on_embeddable_player' => 'Afficher sur le lecteur intégré ?',
|
||||
'remove' => 'Supprimer {platformName}',
|
||||
'submit' => 'Enregistrer',
|
||||
'messages' => [
|
||||
|
@ -69,6 +69,35 @@ class EpisodeModel extends Model
|
||||
protected $afterUpdate = ['writeEnclosureMetadata'];
|
||||
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)
|
||||
{
|
||||
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
|
||||
cache()->delete("podcast{$episode->podcast_id}_defaultQuery");
|
||||
cache()->delete("podcast{$episode->podcast_id}_years");
|
||||
|
@ -75,7 +75,7 @@ class PlatformModel extends Model
|
||||
!($found = cache("podcast{$podcastId}_platforms_{$platformType}"))
|
||||
) {
|
||||
$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(
|
||||
'podcasts_platforms',
|
||||
@ -103,7 +103,7 @@ class PlatformModel extends Model
|
||||
))
|
||||
) {
|
||||
$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(
|
||||
'podcasts_platforms',
|
||||
@ -168,6 +168,8 @@ EOD;
|
||||
|
||||
public function clearCache($podcastId)
|
||||
{
|
||||
$podcast = (new PodcastModel())->getPodcastById($podcastId);
|
||||
|
||||
foreach (['podcasting', 'social', 'funding'] as $platformType) {
|
||||
cache()->delete("podcast{$podcastId}_platforms_{$platformType}");
|
||||
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(
|
||||
"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
|
||||
|
@ -1,4 +1,6 @@
|
||||
import ClientTimezone from "./modules/ClientTimezone";
|
||||
import Clipboard from "./modules/Clipboard";
|
||||
import ThemePicker from "./modules/ThemePicker";
|
||||
import DateTimePicker from "./modules/DateTimePicker";
|
||||
import Dropdown from "./modules/Dropdown";
|
||||
import MarkdownEditor from "./modules/MarkdownEditor";
|
||||
@ -19,3 +21,5 @@ ClientTimezone();
|
||||
DateTimePicker();
|
||||
Time();
|
||||
Soundbites();
|
||||
Clipboard();
|
||||
ThemePicker();
|
||||
|
1
app/Views/_assets/icons/file-copy.svg
Normal file
1
app/Views/_assets/icons/file-copy.svg
Normal file
@ -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 |
1
app/Views/_assets/icons/movie.svg
Normal file
1
app/Views/_assets/icons/movie.svg
Normal file
@ -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 |
23
app/Views/_assets/modules/Clipboard.ts
Normal file
23
app/Views/_assets/modules/Clipboard.ts
Normal file
@ -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;
|
30
app/Views/_assets/modules/ThemePicker.ts
Normal file
30
app/Views/_assets/modules/ThemePicker.ts
Normal file
@ -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;
|
66
app/Views/admin/episode/embeddable_player.php
Normal file
66
app/Views/admin/episode/embeddable_player.php
Normal file
@ -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,
|
||||
$episode->id
|
||||
) ?>"><?= 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(
|
||||
'episode-person-manage',
|
||||
$podcast->id,
|
||||
|
@ -58,6 +58,12 @@
|
||||
</div>
|
||||
|
||||
<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(
|
||||
lang('Episode.soundbites_form.title'),
|
||||
route_to('soundbites-edit', $podcast->id, $episode->id),
|
||||
|
@ -58,6 +58,13 @@
|
||||
$podcast->id,
|
||||
$episode->id
|
||||
) ?>"><?= 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(
|
||||
'episode-person-manage',
|
||||
$podcast->id,
|
||||
|
@ -115,6 +115,22 @@
|
||||
$platform->slug . '_visible',
|
||||
$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'
|
||||
) ?>
|
||||
</div>
|
||||
|
214
app/Views/embeddable_player.php
Normal file
214
app/Views/embeddable_player.php
Normal file
@ -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>
|
||||
<meta charset="UTF-8"/>
|
||||
<title><?= $episode->title ?></title>
|
||||
<meta name="description" content="<?= $episode->description ?>"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta name="description"
|
||||
content="<?= htmlspecialchars($episode->description) ?>" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<?php if (
|
||||
!empty($podcast->payment_pointer)
|
||||
): ?> <meta name="monetization" content="<?= $podcast->payment_pointer ?>">
|
||||
): ?> <meta name="monetization" content="<?= $podcast->payment_pointer ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
|
||||
<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>
|
||||
<meta property="og:title" content="<?= $episode->title ?>" />
|
||||
<meta property="og:locale" content="<?= $podcast->language_code ?>" />
|
||||
|
@ -6,11 +6,12 @@
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title><?= $podcast->title ?></title>
|
||||
<meta name="description" content="<?= $podcast->description ?>"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta name="description"
|
||||
content="<?= htmlspecialchars($podcast->description) ?>" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<?php if (
|
||||
!empty($podcast->payment_pointer)
|
||||
): ?> <meta name="monetization" content="<?= $podcast->payment_pointer ?>">
|
||||
): ?> <meta name="monetization" content="<?= $podcast->payment_pointer ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="/assets/index.css"/>
|
||||
|
Loading…
Reference in New Issue
Block a user