feat: add CDN url

feat: rename ?s to ?_from to match podcastindex recommendation
feat: adust map height
feat: display pie chart on 1 column for small and medium screens
feat: handle empty rss user agent
fix: correct bug when importing episode with empty image
fix: add service to FakePodcastsAnalyticsSeeder, resize pie charts so that all lines fit
fix: set page title to ->title when it exists
fix: replace %20 with +

closes #37
This commit is contained in:
Benjamin Bellamy 2020-10-26 16:13:43 +00:00
parent 384b6b27a3
commit 972bcbf65e
22 changed files with 112 additions and 22 deletions

View File

@ -12,6 +12,7 @@
# Instance configuration # Instance configuration
app.baseURL="https://YOUR_DOMAIN_NAME/" app.baseURL="https://YOUR_DOMAIN_NAME/"
app.mediaBaseURL="https://YOUR_MEDIA_DOMAIN_NAME/"
app.adminGateway="cp-admin" app.adminGateway="cp-admin"
app.authGateway="cp-auth" app.authGateway="cp-auth"
@ -19,7 +20,7 @@ app.authGateway="cp-auth"
database.default.hostname="localhost" database.default.hostname="localhost"
database.default.database="castopod" database.default.database="castopod"
database.default.username="root" database.default.username="root"
database.default.password="root" database.default.password="****"
database.default.DBPrefix="cp_" database.default.DBPrefix="cp_"
# Cache configuration (advanced) # Cache configuration (advanced)

View File

@ -20,7 +20,7 @@ PHP Dependencies:
([ BSD-3-Clause License ](https://github.com/vlucas/phpdotenv/blob/master/LICENSE)) ([ BSD-3-Clause License ](https://github.com/vlucas/phpdotenv/blob/master/LICENSE))
- [HTML To Markdown for PHP](https://github.com/thephpleague/html-to-markdown) - [HTML To Markdown for PHP](https://github.com/thephpleague/html-to-markdown)
([MIT License](https://github.com/thephpleague/html-to-markdown/blob/master/LICENSE)) ([MIT License](https://github.com/thephpleague/html-to-markdown/blob/master/LICENSE))
- [podlibre/user-agents-php](https://github.com/podlibre/user-agents-php) - [opawg/user-agents-php](https://github.com/opawg/user-agents-php)
([MIT License](https://github.com/podlibre/user-agents-php/blob/main/LICENSE)) ([MIT License](https://github.com/podlibre/user-agents-php/blob/main/LICENSE))
- [podlibre/ipcat](https://github.com/podlibre/ipcat) - [podlibre/ipcat](https://github.com/podlibre/ipcat)
([GNU General Public License v3.0](https://github.com/podlibre/ipcat/blob/master/LICENSE)) ([GNU General Public License v3.0](https://github.com/podlibre/ipcat/blob/master/LICENSE))
@ -47,6 +47,9 @@ Other:
- [OPAWG/User agent list](https://github.com/opawg/user-agents) - [OPAWG/User agent list](https://github.com/opawg/user-agents)
([by Open Podcast Analytics Working Group](https://github.com/opawg)) ([by Open Podcast Analytics Working Group](https://github.com/opawg))
([MIT license](https://github.com/opawg/user-agents/blob/master/LICENSE)) ([MIT license](https://github.com/opawg/user-agents/blob/master/LICENSE))
- [OPAWG/podcast-rss-useragents](https://github.com/opawg/podcast-rss-useragents)
([by Open Podcast Analytics Working Group](https://github.com/opawg))
([MIT license](https://github.com/opawg/podcast-rss-useragents/blob/master/LICENSE))
- [client9/ipcat](https://github.com/client9/ipcat) - [client9/ipcat](https://github.com/client9/ipcat)
([GNU General Public License v3.0](https://github.com/client9/ipcat/blob/master/LICENSE)) ([GNU General Public License v3.0](https://github.com/client9/ipcat/blob/master/LICENSE))
- [GeoLite2 City](https://dev.maxmind.com/geoip/geoip2/geolite2/) - [GeoLite2 City](https://dev.maxmind.com/geoip/geoip2/geolite2/)

View File

@ -22,7 +22,20 @@ class App extends BaseConfig
| environments. | environments.
| |
*/ */
public $baseURL = 'http://localhost:8080/'; public $baseURL = 'http://127.0.0.1:8080/';
/*
|--------------------------------------------------------------------------
| Media Base URL
|--------------------------------------------------------------------------
|
| URL to your media root. Typically this will be your base URL,
| WITH a trailing slash:
|
| http://cdn.example.com/
|
*/
public $mediaBaseURL = 'http://127.0.0.2:8080/';
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -361,9 +361,10 @@ class Podcast extends BaseController
? $nsItunes->subtitle . "\n" . $nsItunes->summary ? $nsItunes->subtitle . "\n" . $nsItunes->summary
: $item->description) : $item->description)
), ),
'image' => empty($nsItunes->image->attributes()) 'image' =>
? null !$nsItunes->image || empty($nsItunes->image->attributes())
: download_file($nsItunes->image->attributes()), ? null
: download_file($nsItunes->image->attributes()),
'parental_advisory' => empty($nsItunes->explicit) 'parental_advisory' => empty($nsItunes->explicit)
? null ? null
: (in_array($nsItunes->explicit, ['yes', 'true']) : (in_array($nsItunes->explicit, ['yes', 'true'])

View File

@ -55,7 +55,7 @@ class Analytics extends Controller
) { ) {
helper('media'); helper('media');
$serviceName = isset($_GET['s']) ? $_GET['s'] : ''; $serviceName = isset($_GET['_from']) ? $_GET['_from'] : '';
podcast_hit( podcast_hit(
$podcastId, $podcastId,
@ -64,6 +64,6 @@ class Analytics extends Controller
$fileSize, $fileSize,
$serviceName $serviceName
); );
return redirect()->to(media_url(implode('/', $filename))); return redirect()->to(media_base_url($filename));
} }
} }

View File

@ -38,10 +38,7 @@ class Feed extends Controller
($service ? "_{$service['slug']}" : ''); ($service ? "_{$service['slug']}" : '');
if (!($found = cache($cacheName))) { if (!($found = cache($cacheName))) {
$found = get_rss_feed( $found = get_rss_feed($podcast, $service ? $service['name'] : '');
$podcast,
$service ? '?s=' . urlencode($service['name']) : ''
);
// The page cache is set to expire after next episode publication or a decade by default so it is deleted manually upon podcast update // The page cache is set to expire after next episode publication or a decade by default so it is deleted manually upon podcast update
$secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode( $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode(

View File

@ -150,6 +150,7 @@ class Install extends Controller
{ {
$rules = [ $rules = [
'hostname' => 'required|validate_url', 'hostname' => 'required|validate_url',
'media_base_url' => 'permit_empty|validate_url',
'admin_gateway' => 'required', 'admin_gateway' => 'required',
'auth_gateway' => 'required|differs[admin_gateway]', 'auth_gateway' => 'required|differs[admin_gateway]',
]; ];
@ -165,8 +166,12 @@ class Install extends Controller
} }
$baseUrl = $this->request->getPost('hostname'); $baseUrl = $this->request->getPost('hostname');
$mediaBaseUrl = $this->request->getPost('media_base_url');
self::writeEnv([ self::writeEnv([
'app.baseURL' => $baseUrl, 'app.baseURL' => $baseUrl,
'app.mediaBaseURL' => empty($mediaBaseUrl)
? $baseUrl
: $mediaBaseUrl,
'app.adminGateway' => $this->request->getPost('admin_gateway'), 'app.adminGateway' => $this->request->getPost('admin_gateway'),
'app.authGateway' => $this->request->getPost('auth_gateway'), 'app.authGateway' => $this->request->getPost('auth_gateway'),
]); ]);

View File

@ -28,6 +28,13 @@ class FakePodcastsAnalyticsSeeder extends Seeder
true true
); );
$jsonRSSUserAgents = json_decode(
file_get_contents(
'https://raw.githubusercontent.com/opawg/podcast-rss-useragents/master/src/rss-ua.json'
),
true
);
if ($podcast) { if ($podcast) {
$firstEpisode = (new EpisodeModel()) $firstEpisode = (new EpisodeModel())
->selectMin('published_at') ->selectMin('published_at')
@ -67,6 +74,10 @@ class FakePodcastsAnalyticsSeeder extends Seeder
$jsonUserAgents[ $jsonUserAgents[
rand(1, count($jsonUserAgents) - 1) rand(1, count($jsonUserAgents) - 1)
]; ];
$service =
$jsonRSSUserAgents[
rand(1, count($jsonRSSUserAgents) - 1)
]['name'];
$app = isset($player['app']) ? $player['app'] : ''; $app = isset($player['app']) ? $player['app'] : '';
$device = isset($player['device']) $device = isset($player['device'])
? $player['device'] ? $player['device']
@ -132,6 +143,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
$analytics_podcasts_by_player[] = [ $analytics_podcasts_by_player[] = [
'podcast_id' => $podcast->id, 'podcast_id' => $podcast->id,
'date' => date('Y-m-d', $date), 'date' => date('Y-m-d', $date),
'service'=> $service,
'app' => $app, 'app' => $app,
'device' => $device, 'device' => $device,
'os' => $os, 'os' => $os,

View File

@ -45,6 +45,11 @@ class Episode extends Entity
*/ */
protected $enclosure_url; protected $enclosure_url;
/**
* @var string
*/
protected $enclosure_web_url;
/** /**
* @var string * @var string
*/ */
@ -194,6 +199,11 @@ class Episode extends Entity
); );
} }
public function getWebEnclosureUrl()
{
return $this->getEnclosureUrl() . '?_from=-+Website+-';
}
public function getLink() public function getLink()
{ {
return base_url( return base_url(

View File

@ -74,3 +74,18 @@ function media_url($uri = '', string $protocol = null): string
{ {
return base_url(config('App')->mediaRoot . '/' . $uri, $protocol); return base_url(config('App')->mediaRoot . '/' . $uri, $protocol);
} }
function media_base_url($uri = '')
{
// convert segment array to string
if (is_array($uri)) {
$uri = implode('/', $uri);
}
$uri = trim($uri, '/');
return rtrim(config('App')->mediaBaseURL, '/') .
'/' .
config('App')->mediaRoot .
'/' .
$uri;
}

View File

@ -103,7 +103,11 @@ function get_rss_feed($podcast, $serviceName = '')
$item->addChild('title', $episode->title); $item->addChild('title', $episode->title);
$enclosure = $item->addChild('enclosure'); $enclosure = $item->addChild('enclosure');
$enclosure->addAttribute('url', $episode->enclosure_url . $serviceName); $enclosure->addAttribute(
'url',
$episode->enclosure_url .
(empty($serviceName) ? '' : '?_from=' . urlencode($serviceName))
);
$enclosure->addAttribute('length', $episode->enclosure_filesize); $enclosure->addAttribute('length', $episode->enclosure_filesize);
$enclosure->addAttribute('type', $episode->enclosure_mimetype); $enclosure->addAttribute('type', $episode->enclosure_mimetype);

View File

@ -13,6 +13,9 @@ return [
'form' => [ 'form' => [
'instance_config' => 'Instance configuration', 'instance_config' => 'Instance configuration',
'hostname' => 'Hostname', 'hostname' => 'Hostname',
'media_base_url' => 'Media base URL',
'media_base_url_hint' =>
'If you use a CDN and/or an external analytics service, you may set them here.',
'admin_gateway' => 'Admin gateway', 'admin_gateway' => 'Admin gateway',
'admin_gateway_hint' => 'admin_gateway_hint' =>
'The route to access the admin area (eg. https://example.com/cp-admin). It is set by default as cp-admin, we recommend you change it for security reasons.', 'The route to access the admin area (eg. https://example.com/cp-admin). It is set by default as cp-admin, we recommend you change it for security reasons.',

View File

@ -13,6 +13,9 @@ return [
'form' => [ 'form' => [
'instance_config' => 'Paramètres de linstance', 'instance_config' => 'Paramètres de linstance',
'hostname' => 'Nom dhôte', 'hostname' => 'Nom dhôte',
'media_base_url' => 'Adresse racine des médias',
'media_base_url_hint' =>
'Si vous utilisez un CDN et/ou un service de mesure daudience externe, vous pouvez les définir ici.',
'admin_gateway' => 'Adresse dadministration', 'admin_gateway' => 'Adresse dadministration',
'admin_gateway_hint' => 'admin_gateway_hint' =>
'Le chemin pour accéder à ladministration (par exemple https://example.com/cp-admin). Il est défini par défaut à cp-admin, nous vous recommandons de le changer par mesure de sécurité.', 'Le chemin pour accéder à ladministration (par exemple https://example.com/cp-admin). Il est défini par défaut à cp-admin, nous vous recommandons de le changer par mesure de sécurité.',

View File

@ -1,9 +1,9 @@
.chart-map { .chart-map {
height: 800px; height: 600px;
border: solid 10px #eee; border: solid 10px #eee;
} }
.chart-pie { .chart-pie {
height: 400px; height: 450px;
width: 100%; width: 100%;
border: solid 1px #eee; border: solid 1px #eee;
} }

View File

@ -14,7 +14,9 @@
<body class="flex flex-col min-h-screen mx-auto"> <body class="flex flex-col min-h-screen mx-auto">
<header class="border-b"> <header class="border-b">
<div class="container flex items-center justify-between px-2 py-4 mx-auto"> <div class="container flex items-center justify-between px-2 py-4 mx-auto">
<a href="<?= route_to('home') ?>" class="text-2xl">Castopod</a> <a href="<?= route_to('home') ?>" class="text-2xl"><?= isset($page)
? $page->title
: 'Castopod' ?></a>
</div> </div>
</header> </header>
<main class="container flex-1 px-4 py-10 mx-auto"> <main class="container flex-1 px-4 py-10 mx-auto">

View File

@ -10,7 +10,7 @@
<?= $this->section('content') ?> <?= $this->section('content') ?>
<div class="grid grid-cols-2 divide-x"> <div class="lg:divide-x lg:grid lg:grid-cols-2">
<div class="mb-12 mr-6 text-center"> <div class="mb-12 mr-6 text-center">
<h2><?= lang('Charts.by_country_weekly') ?></h2> <h2><?= lang('Charts.by_country_weekly') ?></h2>
<div class="chart-pie" id="by-country-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to( <div class="chart-pie" id="by-country-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(

View File

@ -10,7 +10,7 @@
<?= $this->section('content') ?> <?= $this->section('content') ?>
<div class="grid grid-cols-2 divide-x"> <div class="lg:divide-x lg:grid lg:grid-cols-2">
<div class="mb-12 mr-6 text-center"> <div class="mb-12 mr-6 text-center">
<h2><?= lang('Charts.by_player_weekly') ?></h2> <h2><?= lang('Charts.by_player_weekly') ?></h2>
<div class="chart-pie" id="by-app-weekly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to( <div class="chart-pie" id="by-app-weekly-pie" data-chart-type="pie-chart" data-chart-url="<?= route_to(

View File

@ -10,7 +10,7 @@
<?= $this->section('content') ?> <?= $this->section('content') ?>
<div class="grid grid-cols-2 divide-x"> <div class="lg:divide-x lg:grid lg:grid-cols-2">
<div class="mb-12 mr-6 text-center"> <div class="mb-12 mr-6 text-center">
<h2><?= lang('Charts.by_domain_weekly') ?></h2> <h2><?= lang('Charts.by_domain_weekly') ?></h2>

View File

@ -96,7 +96,7 @@
</time> </time>
</div> </div>
<audio controls preload="none" class="w-full mt-auto"> <audio controls preload="none" class="w-full mt-auto">
<source src="<?= $episode->enclosure_url ?>" type="<?= $episode->enclosure_type ?>"> <source src="<?= $episode->enclosure_web_url ?>" type="<?= $episode->enclosure_type ?>">
Your browser does not support the audio tag. Your browser does not support the audio tag.
</audio> </audio>
</div> </div>

View File

@ -22,6 +22,21 @@
'required' => 'required', 'required' => 'required',
]) ?> ]) ?>
<?= form_label(
lang('Install.form.media_base_url'),
'media_base_url',
[],
lang('Install.form.media_base_url_hint'),
true
) ?>
<?= form_input([
'id' => 'media_base_url',
'name' => 'media_base_url',
'class' => 'form-input mb-4',
'value' => old('media_base_url', ''),
]) ?>
<?= form_label( <?= form_label(
lang('Install.form.admin_gateway'), lang('Install.form.admin_gateway'),
'admin_gateway', 'admin_gateway',
@ -51,7 +66,12 @@
]) ?> ]) ?>
<?= button( <?= button(
lang('Install.form.next') . icon('arrow-right', 'ml-2'), lang('Install.form.next') .
icon(
'arrow-right',
'ml-2'
),
null, null,
['variant' => 'primary'], ['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end'] ['type' => 'submit', 'class' => 'self-end']

View File

@ -144,7 +144,7 @@
</time> </time>
</div> </div>
<audio controls preload="none" class="w-full mt-auto"> <audio controls preload="none" class="w-full mt-auto">
<source src="<?= $episode->enclosure_url ?>" type="<?= $episode->enclosure_type ?>"> <source src="<?= $episode->enclosure_web_url ?>" type="<?= $episode->enclosure_type ?>">
Your browser does not support the audio tag. Your browser does not support the audio tag.
</audio> </audio>
</div> </div>

1
sha1sum Normal file
View File

@ -0,0 +1 @@
test