diff --git a/.env.example b/.env.example
index a1b2a681038cc172a9d68b9cfe873d606149614b..0ac3f1b0d4abf00d257142d23351c01b0bd3db9a 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 d4c1c29cc1b68810408f6048748918729c463d12..f987fd2785b74bfdb7ab61ffdd16b740acb5b565 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 c81f2d891a028d9a811ddb443716cbb3f8c72cd4..ffad1a4188ba1afdd926dde049976e014a9bc723 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 afac930d4a8f1bcc79fe81ae0e3054cff66eb726..542f4f921e474000c1335d1d7bcb08893c36935d 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 7066c5e340fbd4ca2e7ae373a665c418516778b8..e0cf865c57b012d815c466e29e91810b0b4f2a6a 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 0170e05ea3fa9c35f2bc2dc1e76546447604ab95..5b74f70a867c6714f7fa885215f0df0765935dc4 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 4d71879ec52423904dcb0f8fc0406810ae406ad0..1d53f7247ff02a761c86ecd36ecd7451dc0dfe2e 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 e17e177ddc6f93cb48f811e3511838887293b219..f7156f6e2aa8b39be4bfd112c27ad3f6b8fd9340 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 23f7737c24273c01c771e7176d628ef9e0937b63..3ba5b7e0cd6871d7c60788696d59e6b8ddd77305 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 a3b28279e285c68abe46ff91745b2200e1be5252..77d77478f7a777bd26d4a867f4c651cf636a0365 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 9627c3d128688858b99a5b29951532a0abdf39fc..265841ce9b0c2d48425fff950b36480f5d09961f 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 131fc20691f52409a585eb17816889ad6520f2de..5a5af49e416b9cd56c96079556ea863be72b3937 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 dfff116fa9277cfc8f19bd3d4ad97fffa9a536df..e721ebb441b36632e147704aa8bbaf8c12e3f4e8 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 37b526afae0a6495bb4d98610ec85a31b88a921d..b1bb0294dda5919e8a3ab9bee3b0b06ec14c9b5c 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 dd3ea6e984c8240ab8dd014b91c487eac451cb8e..831f2cb0b682eb1bbea719fcefaef7831c03603e 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 d4620e3a814fb98be95b07c48d054136b126cdce..3504529bc257701177b2b0a3d3af44bbe2b0ab65 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 fcc2e83bf7bdec3f1b305f8564b8c94f359bb8b5..b48ae316c7b4df950b4b64667ec7eb7da3e58a66 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 9b0a0142252363790753bd92e464af9dbfcba322..5ec0f99a8b4ebc4e818a1c692bbff10f9c6e0329 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 c3c9e7f8a618ba2b1fc474014a8d67e86237f3a1..4e52bca255d64d1dbffa6a7508c479fa3f011825 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 bdd63deecf345430cfc2d6e293f7faa4eaa588fb..ef6d64289599057db1495ee5b77e2b4b3315d5a5 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 c70db9fa25da936366b8fea7f885149502a0df9f..52329cd3535d6a4394d2275028ae535a6870f922 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 0000000000000000000000000000000000000000..9daeafb9864cf43055ae93beb0afd6c7d144bfa4
--- /dev/null
+++ b/sha1sum
@@ -0,0 +1 @@
+test