From 972bcbf65ee119b8641ca3c4e5c0e8cf9ca8dd4f Mon Sep 17 00:00:00 2001 From: Benjamin Bellamy <ben@podlibre.org> Date: Mon, 26 Oct 2020 16:13:43 +0000 Subject: [PATCH] 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 --- .env.example | 3 ++- DEPENDENCIES.md | 5 ++++- app/Config/App.php | 15 ++++++++++++- app/Controllers/Admin/Podcast.php | 7 +++--- app/Controllers/Analytics.php | 4 ++-- app/Controllers/Feed.php | 5 +---- app/Controllers/Install.php | 5 +++++ .../Seeds/FakePodcastsAnalyticsSeeder.php | 12 ++++++++++ app/Entities/Episode.php | 10 +++++++++ app/Helpers/media_helper.php | 15 +++++++++++++ app/Helpers/rss_helper.php | 6 ++++- app/Language/en/Install.php | 3 +++ app/Language/fr/Install.php | 3 +++ app/Views/_assets/styles/charts.css | 4 ++-- app/Views/_layout.php | 4 +++- .../admin/podcast/analytics/locations.php | 2 +- app/Views/admin/podcast/analytics/players.php | 2 +- .../admin/podcast/analytics/webpages.php | 2 +- app/Views/episode.php | 2 +- app/Views/install/instance_config.php | 22 ++++++++++++++++++- app/Views/podcast.php | 2 +- sha1sum | 1 + 22 files changed, 112 insertions(+), 22 deletions(-) create mode 100644 sha1sum diff --git a/.env.example b/.env.example index a1b2a68103..0ac3f1b0d4 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,7 @@ # Instance configuration app.baseURL="https://YOUR_DOMAIN_NAME/" +app.mediaBaseURL="https://YOUR_MEDIA_DOMAIN_NAME/" app.adminGateway="cp-admin" app.authGateway="cp-auth" @@ -19,7 +20,7 @@ app.authGateway="cp-auth" database.default.hostname="localhost" database.default.database="castopod" database.default.username="root" -database.default.password="root" +database.default.password="****" database.default.DBPrefix="cp_" # Cache configuration (advanced) diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md index d4c1c29cc1..f987fd2785 100644 --- a/DEPENDENCIES.md +++ b/DEPENDENCIES.md @@ -20,7 +20,7 @@ PHP Dependencies: ([ BSD-3-Clause License ](https://github.com/vlucas/phpdotenv/blob/master/LICENSE)) - [HTML To Markdown for PHP](https://github.com/thephpleague/html-to-markdown) ([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)) - [podlibre/ipcat](https://github.com/podlibre/ipcat) ([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) ([by Open Podcast Analytics Working Group](https://github.com/opawg)) ([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) ([GNU General Public License v3.0](https://github.com/client9/ipcat/blob/master/LICENSE)) - [GeoLite2 City](https://dev.maxmind.com/geoip/geoip2/geolite2/) diff --git a/app/Config/App.php b/app/Config/App.php index c81f2d891a..ffad1a4188 100644 --- a/app/Config/App.php +++ b/app/Config/App.php @@ -22,7 +22,20 @@ class App extends BaseConfig | 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/'; /* |-------------------------------------------------------------------------- diff --git a/app/Controllers/Admin/Podcast.php b/app/Controllers/Admin/Podcast.php index afac930d4a..542f4f921e 100644 --- a/app/Controllers/Admin/Podcast.php +++ b/app/Controllers/Admin/Podcast.php @@ -361,9 +361,10 @@ class Podcast extends BaseController ? $nsItunes->subtitle . "\n" . $nsItunes->summary : $item->description) ), - 'image' => empty($nsItunes->image->attributes()) - ? null - : download_file($nsItunes->image->attributes()), + 'image' => + !$nsItunes->image || empty($nsItunes->image->attributes()) + ? null + : download_file($nsItunes->image->attributes()), 'parental_advisory' => empty($nsItunes->explicit) ? null : (in_array($nsItunes->explicit, ['yes', 'true']) diff --git a/app/Controllers/Analytics.php b/app/Controllers/Analytics.php index 7066c5e340..e0cf865c57 100644 --- a/app/Controllers/Analytics.php +++ b/app/Controllers/Analytics.php @@ -55,7 +55,7 @@ class Analytics extends Controller ) { helper('media'); - $serviceName = isset($_GET['s']) ? $_GET['s'] : ''; + $serviceName = isset($_GET['_from']) ? $_GET['_from'] : ''; podcast_hit( $podcastId, @@ -64,6 +64,6 @@ class Analytics extends Controller $fileSize, $serviceName ); - return redirect()->to(media_url(implode('/', $filename))); + return redirect()->to(media_base_url($filename)); } } diff --git a/app/Controllers/Feed.php b/app/Controllers/Feed.php index 0170e05ea3..5b74f70a86 100644 --- a/app/Controllers/Feed.php +++ b/app/Controllers/Feed.php @@ -38,10 +38,7 @@ class Feed extends Controller ($service ? "_{$service['slug']}" : ''); if (!($found = cache($cacheName))) { - $found = get_rss_feed( - $podcast, - $service ? '?s=' . urlencode($service['name']) : '' - ); + $found = get_rss_feed($podcast, $service ? $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 $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode( diff --git a/app/Controllers/Install.php b/app/Controllers/Install.php index 4d71879ec5..1d53f7247f 100644 --- a/app/Controllers/Install.php +++ b/app/Controllers/Install.php @@ -150,6 +150,7 @@ class Install extends Controller { $rules = [ 'hostname' => 'required|validate_url', + 'media_base_url' => 'permit_empty|validate_url', 'admin_gateway' => 'required', 'auth_gateway' => 'required|differs[admin_gateway]', ]; @@ -165,8 +166,12 @@ class Install extends Controller } $baseUrl = $this->request->getPost('hostname'); + $mediaBaseUrl = $this->request->getPost('media_base_url'); self::writeEnv([ 'app.baseURL' => $baseUrl, + 'app.mediaBaseURL' => empty($mediaBaseUrl) + ? $baseUrl + : $mediaBaseUrl, 'app.adminGateway' => $this->request->getPost('admin_gateway'), 'app.authGateway' => $this->request->getPost('auth_gateway'), ]); diff --git a/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php b/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php index e17e177ddc..f7156f6e2a 100644 --- a/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php +++ b/app/Database/Seeds/FakePodcastsAnalyticsSeeder.php @@ -28,6 +28,13 @@ class FakePodcastsAnalyticsSeeder extends Seeder true ); + $jsonRSSUserAgents = json_decode( + file_get_contents( + 'https://raw.githubusercontent.com/opawg/podcast-rss-useragents/master/src/rss-ua.json' + ), + true + ); + if ($podcast) { $firstEpisode = (new EpisodeModel()) ->selectMin('published_at') @@ -67,6 +74,10 @@ class FakePodcastsAnalyticsSeeder extends Seeder $jsonUserAgents[ rand(1, count($jsonUserAgents) - 1) ]; + $service = + $jsonRSSUserAgents[ + rand(1, count($jsonRSSUserAgents) - 1) + ]['name']; $app = isset($player['app']) ? $player['app'] : ''; $device = isset($player['device']) ? $player['device'] @@ -132,6 +143,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder $analytics_podcasts_by_player[] = [ 'podcast_id' => $podcast->id, 'date' => date('Y-m-d', $date), + 'service'=> $service, 'app' => $app, 'device' => $device, 'os' => $os, diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index 23f7737c24..3ba5b7e0cd 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -45,6 +45,11 @@ class Episode extends Entity */ protected $enclosure_url; + /** + * @var string + */ + protected $enclosure_web_url; + /** * @var string */ @@ -194,6 +199,11 @@ class Episode extends Entity ); } + public function getWebEnclosureUrl() + { + return $this->getEnclosureUrl() . '?_from=-+Website+-'; + } + public function getLink() { return base_url( diff --git a/app/Helpers/media_helper.php b/app/Helpers/media_helper.php index a3b28279e2..77d77478f7 100644 --- a/app/Helpers/media_helper.php +++ b/app/Helpers/media_helper.php @@ -74,3 +74,18 @@ function media_url($uri = '', string $protocol = null): string { 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; +} diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php index 9627c3d128..265841ce9b 100644 --- a/app/Helpers/rss_helper.php +++ b/app/Helpers/rss_helper.php @@ -103,7 +103,11 @@ function get_rss_feed($podcast, $serviceName = '') $item->addChild('title', $episode->title); $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('type', $episode->enclosure_mimetype); diff --git a/app/Language/en/Install.php b/app/Language/en/Install.php index 131fc20691..5a5af49e41 100644 --- a/app/Language/en/Install.php +++ b/app/Language/en/Install.php @@ -13,6 +13,9 @@ return [ 'form' => [ 'instance_config' => 'Instance configuration', '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_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.', diff --git a/app/Language/fr/Install.php b/app/Language/fr/Install.php index dfff116fa9..e721ebb441 100644 --- a/app/Language/fr/Install.php +++ b/app/Language/fr/Install.php @@ -13,6 +13,9 @@ return [ 'form' => [ 'instance_config' => 'Paramètres de l’instance', 'hostname' => 'Nom d’hôte', + 'media_base_url' => 'Adresse racine des médias', + 'media_base_url_hint' => + 'Si vous utilisez un CDN et/ou un service de mesure d’audience externe, vous pouvez les définir ici.', 'admin_gateway' => 'Adresse d’administration', 'admin_gateway_hint' => 'Le chemin pour accéder à l’administration (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é.', diff --git a/app/Views/_assets/styles/charts.css b/app/Views/_assets/styles/charts.css index 37b526afae..b1bb0294dd 100644 --- a/app/Views/_assets/styles/charts.css +++ b/app/Views/_assets/styles/charts.css @@ -1,9 +1,9 @@ .chart-map { - height: 800px; + height: 600px; border: solid 10px #eee; } .chart-pie { - height: 400px; + height: 450px; width: 100%; border: solid 1px #eee; } diff --git a/app/Views/_layout.php b/app/Views/_layout.php index dd3ea6e984..831f2cb0b6 100644 --- a/app/Views/_layout.php +++ b/app/Views/_layout.php @@ -14,7 +14,9 @@ <body class="flex flex-col min-h-screen mx-auto"> <header class="border-b"> <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> </header> <main class="container flex-1 px-4 py-10 mx-auto"> diff --git a/app/Views/admin/podcast/analytics/locations.php b/app/Views/admin/podcast/analytics/locations.php index d4620e3a81..3504529bc2 100644 --- a/app/Views/admin/podcast/analytics/locations.php +++ b/app/Views/admin/podcast/analytics/locations.php @@ -10,7 +10,7 @@ <?= $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"> <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( diff --git a/app/Views/admin/podcast/analytics/players.php b/app/Views/admin/podcast/analytics/players.php index fcc2e83bf7..b48ae316c7 100644 --- a/app/Views/admin/podcast/analytics/players.php +++ b/app/Views/admin/podcast/analytics/players.php @@ -10,7 +10,7 @@ <?= $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"> <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( diff --git a/app/Views/admin/podcast/analytics/webpages.php b/app/Views/admin/podcast/analytics/webpages.php index 9b0a014225..5ec0f99a8b 100644 --- a/app/Views/admin/podcast/analytics/webpages.php +++ b/app/Views/admin/podcast/analytics/webpages.php @@ -10,7 +10,7 @@ <?= $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"> <h2><?= lang('Charts.by_domain_weekly') ?></h2> diff --git a/app/Views/episode.php b/app/Views/episode.php index c3c9e7f8a6..4e52bca255 100644 --- a/app/Views/episode.php +++ b/app/Views/episode.php @@ -96,7 +96,7 @@ </time> </div> <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. </audio> </div> diff --git a/app/Views/install/instance_config.php b/app/Views/install/instance_config.php index bdd63deecf..ef6d642895 100644 --- a/app/Views/install/instance_config.php +++ b/app/Views/install/instance_config.php @@ -22,6 +22,21 @@ '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( lang('Install.form.admin_gateway'), 'admin_gateway', @@ -51,7 +66,12 @@ ]) ?> <?= button( - lang('Install.form.next') . icon('arrow-right', 'ml-2'), + lang('Install.form.next') . + icon( + 'arrow-right', + + 'ml-2' + ), null, ['variant' => 'primary'], ['type' => 'submit', 'class' => 'self-end'] diff --git a/app/Views/podcast.php b/app/Views/podcast.php index c70db9fa25..52329cd353 100644 --- a/app/Views/podcast.php +++ b/app/Views/podcast.php @@ -144,7 +144,7 @@ </time> </div> <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. </audio> </div> diff --git a/sha1sum b/sha1sum new file mode 100644 index 0000000000..9daeafb986 --- /dev/null +++ b/sha1sum @@ -0,0 +1 @@ +test -- GitLab