refactor(database): add / update fields to optimize storage

- harmonize field types and use explicit names
- store html value alongside markdown descriptions for better performance
- add duration and bandwidth to podcast analytics
- add new analytics table for podcast hits by hour
- replace visible MAXMIND_LICENCE_KEY with variable
This commit is contained in:
Yassine Doghri 2020-10-29 15:45:19 +00:00
parent 21da91eb9d
commit 391c349daa
64 changed files with 723 additions and 657 deletions

View File

@ -59,10 +59,10 @@ $routes->group(config('App')->installGateway, function ($routes) {
]); ]);
}); });
// Route for podcast audio file analytics (/audio/podcast_id/episode_id/bytes_threshold/filesize/podcast_folder/filename.mp3) // Route for podcast audio file analytics (/audio/podcast_id/episode_id/bytes_threshold/filesize/duration/podcast_folder/filename.mp3)
$routes->add( $routes->add(
'audio/(:num)/(:num)/(:num)/(:num)/(:any)', 'audio/(:num)/(:num)/(:num)/(:num)/(:num)/(:any)',
'Analytics::hit/$1/$2/$3/$4/$5', 'Analytics::hit/$1/$2/$3/$4/$5/$6',
[ [
'as' => 'analytics_hit', 'as' => 'analytics_hit',
] ]

View File

@ -112,7 +112,7 @@ class Episode extends BaseController
'slug' => $this->request->getPost('slug'), 'slug' => $this->request->getPost('slug'),
'guid' => '', 'guid' => '',
'enclosure' => $this->request->getFile('enclosure'), 'enclosure' => $this->request->getFile('enclosure'),
'description' => $this->request->getPost('description'), 'description_markdown' => $this->request->getPost('description'),
'image' => $this->request->getFile('image'), 'image' => $this->request->getFile('image'),
'parental_advisory' => 'parental_advisory' =>
$this->request->getPost('parental_advisory') !== 'undefined' $this->request->getPost('parental_advisory') !== 'undefined'
@ -121,7 +121,7 @@ class Episode extends BaseController
'number' => $this->request->getPost('episode_number'), 'number' => $this->request->getPost('episode_number'),
'season_number' => $this->request->getPost('season_number'), 'season_number' => $this->request->getPost('season_number'),
'type' => $this->request->getPost('type'), 'type' => $this->request->getPost('type'),
'block' => $this->request->getPost('block') == 'yes', 'is_blocked' => $this->request->getPost('block') == 'yes',
'created_by' => user(), 'created_by' => user(),
'updated_by' => user(), 'updated_by' => user(),
'published_at' => Time::createFromFormat( 'published_at' => Time::createFromFormat(
@ -140,11 +140,11 @@ class Episode extends BaseController
->with('errors', $episodeModel->errors()); ->with('errors', $episodeModel->errors());
} }
// update podcast's episode_description_footer if changed // update podcast's episode_description_footer_markdown if changed
$podcastModel = new PodcastModel(); $podcastModel = new PodcastModel();
if ($this->podcast->hasChanged('episode_description_footer')) { if ($this->podcast->hasChanged('episode_description_footer_markdown')) {
$this->podcast->episode_description_footer = $this->request->getPost( $this->podcast->episode_description_footer_markdown = $this->request->getPost(
'description_footer' 'description_footer'
); );
@ -197,7 +197,9 @@ class Episode extends BaseController
$this->episode->title = $this->request->getPost('title'); $this->episode->title = $this->request->getPost('title');
$this->episode->slug = $this->request->getPost('slug'); $this->episode->slug = $this->request->getPost('slug');
$this->episode->description = $this->request->getPost('description'); $this->episode->description_markdown = $this->request->getPost(
'description'
);
$this->episode->parental_advisory = $this->episode->parental_advisory =
$this->request->getPost('parental_advisory') !== 'undefined' $this->request->getPost('parental_advisory') !== 'undefined'
? $this->request->getPost('parental_advisory') ? $this->request->getPost('parental_advisory')
@ -207,7 +209,7 @@ class Episode extends BaseController
? $this->request->getPost('season_number') ? $this->request->getPost('season_number')
: null; : null;
$this->episode->type = $this->request->getPost('type'); $this->episode->type = $this->request->getPost('type');
$this->episode->block = $this->request->getPost('block') == 'yes'; $this->episode->is_blocked = $this->request->getPost('block') == 'yes';
$this->episode->published_at = Time::createFromFormat( $this->episode->published_at = Time::createFromFormat(
'Y-m-d H:i', 'Y-m-d H:i',
$this->request->getPost('publication_date'), $this->request->getPost('publication_date'),
@ -233,12 +235,12 @@ class Episode extends BaseController
->with('errors', $episodeModel->errors()); ->with('errors', $episodeModel->errors());
} }
// update podcast's episode_description_footer if changed // update podcast's episode_description_footer_markdown if changed
$this->podcast->episode_description_footer = $this->request->getPost( $this->podcast->episode_description_footer_markdown = $this->request->getPost(
'description_footer' 'description_footer'
); );
if ($this->podcast->hasChanged('episode_description_footer')) { if ($this->podcast->hasChanged('episode_description_footer_markdown')) {
$podcastModel = new PodcastModel(); $podcastModel = new PodcastModel();
if (!$podcastModel->update($this->podcast->id, $this->podcast)) { if (!$podcastModel->update($this->podcast->id, $this->podcast)) {
return redirect() return redirect()

View File

@ -95,7 +95,7 @@ class Podcast extends BaseController
$data = ['podcast' => $this->podcast]; $data = ['podcast' => $this->podcast];
replace_breadcrumb_params([0 => $this->podcast->title]); replace_breadcrumb_params([0 => $this->podcast->title]);
return view('admin/podcast/analytics/listening-time', $data); return view('admin/podcast/analytics/listening_time', $data);
} }
public function viewAnalyticsPlayers() public function viewAnalyticsPlayers()
@ -141,9 +141,9 @@ class Podcast extends BaseController
$podcast = new \App\Entities\Podcast([ $podcast = new \App\Entities\Podcast([
'title' => $this->request->getPost('title'), 'title' => $this->request->getPost('title'),
'name' => $this->request->getPost('name'), 'name' => $this->request->getPost('name'),
'description' => $this->request->getPost('description'), 'description_markdown' => $this->request->getPost('description'),
'image' => $this->request->getFile('image'), 'image' => $this->request->getFile('image'),
'language' => $this->request->getPost('language'), 'language_code' => $this->request->getPost('language'),
'category_id' => $this->request->getPost('category'), 'category_id' => $this->request->getPost('category'),
'parental_advisory' => 'parental_advisory' =>
$this->request->getPost('parental_advisory') !== 'undefined' $this->request->getPost('parental_advisory') !== 'undefined'
@ -154,9 +154,9 @@ class Podcast extends BaseController
'publisher' => $this->request->getPost('publisher'), 'publisher' => $this->request->getPost('publisher'),
'type' => $this->request->getPost('type'), 'type' => $this->request->getPost('type'),
'copyright' => $this->request->getPost('copyright'), 'copyright' => $this->request->getPost('copyright'),
'block' => $this->request->getPost('block') === 'yes', 'is_blocked' => $this->request->getPost('is_blocked') === 'yes',
'complete' => $this->request->getPost('complete') === 'yes', 'is_completed' => $this->request->getPost('complete') === 'yes',
'lock' => $this->request->getPost('lock') === 'yes', 'is_locked' => $this->request->getPost('lock') === 'yes',
'created_by' => user(), 'created_by' => user(),
'updated_by' => user(), 'updated_by' => user(),
]); ]);
@ -259,6 +259,10 @@ class Podcast extends BaseController
->with('errors', [lang('PodcastImport.lock_import')]); ->with('errors', [lang('PodcastImport.lock_import')]);
} }
$converter = new HtmlConverter();
$channelDescriptionHtml = $feed->channel[0]->description;
$podcast = new \App\Entities\Podcast([ $podcast = new \App\Entities\Podcast([
'name' => $this->request->getPost('name'), 'name' => $this->request->getPost('name'),
'imported_feed_url' => $this->request->getPost('imported_feed_url'), 'imported_feed_url' => $this->request->getPost('imported_feed_url'),
@ -266,9 +270,12 @@ class Podcast extends BaseController
route_to('podcast_feed', $this->request->getPost('name')) route_to('podcast_feed', $this->request->getPost('name'))
), ),
'title' => $feed->channel[0]->title, 'title' => $feed->channel[0]->title,
'description' => $feed->channel[0]->description, 'description_markdown' => $converter->convert(
$channelDescriptionHtml
),
'description_html' => $channelDescriptionHtml,
'image' => download_file($nsItunes->image->attributes()), 'image' => download_file($nsItunes->image->attributes()),
'language' => $this->request->getPost('language'), 'language_code' => $this->request->getPost('language'),
'category_id' => $this->request->getPost('category'), 'category_id' => $this->request->getPost('category'),
'parental_advisory' => empty($nsItunes->explicit) 'parental_advisory' => empty($nsItunes->explicit)
? null ? null
@ -282,10 +289,10 @@ class Podcast extends BaseController
'publisher' => $nsItunes->author, 'publisher' => $nsItunes->author,
'type' => empty($nsItunes->type) ? 'episodic' : $nsItunes->type, 'type' => empty($nsItunes->type) ? 'episodic' : $nsItunes->type,
'copyright' => $feed->channel[0]->copyright, 'copyright' => $feed->channel[0]->copyright,
'block' => empty($nsItunes->block) 'is_blocked' => empty($nsItunes->block)
? false ? false
: $nsItunes->block === 'yes', : $nsItunes->block === 'yes',
'complete' => empty($nsItunes->complete) 'is_completed' => empty($nsItunes->complete)
? false ? false
: $nsItunes->complete === 'yes', : $nsItunes->complete === 'yes',
'created_by' => user(), 'created_by' => user(),
@ -314,8 +321,6 @@ class Podcast extends BaseController
$podcastAdminGroup->id $podcastAdminGroup->id
); );
$converter = new HtmlConverter();
$numberItems = $feed->channel[0]->item->count(); $numberItems = $feed->channel[0]->item->count();
$lastItem = $lastItem =
!empty($this->request->getPost('max_episodes')) && !empty($this->request->getPost('max_episodes')) &&
@ -347,20 +352,24 @@ class Podcast extends BaseController
} }
$slugs[] = $slug; $slugs[] = $slug;
$itemDescriptionHtml =
$this->request->getPost('description_field') === 'summary'
? $nsItunes->summary
: ($this->request->getPost('description_field') ===
'subtitle_summary'
? $nsItunes->subtitle . '<br/>' . $nsItunes->summary
: $item->description);
$newEpisode = new \App\Entities\Episode([ $newEpisode = new \App\Entities\Episode([
'podcast_id' => $newPodcastId, 'podcast_id' => $newPodcastId,
'guid' => empty($item->guid) ? null : $item->guid, 'guid' => empty($item->guid) ? null : $item->guid,
'title' => $item->title, 'title' => $item->title,
'slug' => $slug, 'slug' => $slug,
'enclosure' => download_file($item->enclosure->attributes()), 'enclosure' => download_file($item->enclosure->attributes()),
'description' => $converter->convert( 'description_markdown' => $converter->convert(
$this->request->getPost('description_field') === 'summary' $itemDescriptionHtml
? $nsItunes->summary
: ($this->request->getPost('description_field') ===
'subtitle_summary'
? $nsItunes->subtitle . "\n" . $nsItunes->summary
: $item->description)
), ),
'description_html' => $itemDescriptionHtml,
'image' => 'image' =>
!$nsItunes->image || empty($nsItunes->image->attributes()) !$nsItunes->image || empty($nsItunes->image->attributes())
? null ? null
@ -379,12 +388,14 @@ class Podcast extends BaseController
'season_number' => empty( 'season_number' => empty(
$this->request->getPost('season_number') $this->request->getPost('season_number')
) )
? (!empty($nsItunes->season)
? $nsItunes->season ? $nsItunes->season
: null)
: $this->request->getPost('season_number'), : $this->request->getPost('season_number'),
'type' => empty($nsItunes->episodeType) 'type' => empty($nsItunes->episodeType)
? 'full' ? 'full'
: $nsItunes->episodeType, : $nsItunes->episodeType,
'block' => empty($nsItunes->block) 'is_blocked' => empty($nsItunes->block)
? false ? false
: $nsItunes->block === 'yes', : $nsItunes->block === 'yes',
'created_by' => user(), 'created_by' => user(),
@ -441,13 +452,15 @@ class Podcast extends BaseController
$this->podcast->title = $this->request->getPost('title'); $this->podcast->title = $this->request->getPost('title');
$this->podcast->name = $this->request->getPost('name'); $this->podcast->name = $this->request->getPost('name');
$this->podcast->description = $this->request->getPost('description'); $this->podcast->description_markdown = $this->request->getPost(
'description'
);
$image = $this->request->getFile('image'); $image = $this->request->getFile('image');
if ($image->isValid()) { if ($image->isValid()) {
$this->podcast->image = $image; $this->podcast->image = $image;
} }
$this->podcast->language = $this->request->getPost('language'); $this->podcast->language_code = $this->request->getPost('language');
$this->podcast->category_id = $this->request->getPost('category'); $this->podcast->category_id = $this->request->getPost('category');
$this->podcast->parental_advisory = $this->podcast->parental_advisory =
$this->request->getPost('parental_advisory') !== 'undefined' $this->request->getPost('parental_advisory') !== 'undefined'
@ -458,10 +471,11 @@ class Podcast extends BaseController
$this->podcast->owner_email = $this->request->getPost('owner_email'); $this->podcast->owner_email = $this->request->getPost('owner_email');
$this->podcast->type = $this->request->getPost('type'); $this->podcast->type = $this->request->getPost('type');
$this->podcast->copyright = $this->request->getPost('copyright'); $this->podcast->copyright = $this->request->getPost('copyright');
$this->podcast->block = $this->request->getPost('block') === 'yes'; $this->podcast->is_blocked =
$this->podcast->complete = $this->request->getPost('is_blocked') === 'yes';
$this->podcast->is_completed =
$this->request->getPost('complete') === 'yes'; $this->request->getPost('complete') === 'yes';
$this->podcast->lock = $this->request->getPost('lock') === 'yes'; $this->podcast->is_lock = $this->request->getPost('lock') === 'yes';
$this->updated_by = user(); $this->updated_by = user();
$db = \Config\Database::connect(); $db = \Config\Database::connect();

View File

@ -71,14 +71,14 @@ class PodcastSettings extends BaseController
'platform_id' => $platformId, 'platform_id' => $platformId,
'podcast_id' => $this->podcast->id, 'podcast_id' => $this->podcast->id,
'link_url' => $platformLinkUrl, 'link_url' => $platformLinkUrl,
'visible' => array_key_exists('visible', $platformLink) 'is_visible' => array_key_exists('visible', $platformLink)
? $platformLink['visible'] == 'yes' ? $platformLink['visible'] == 'yes'
: false, : false,
]); ]);
} }
} }
$platformModel->savePlatformLinks( $platformModel->savePodcastPlatforms(
$this->podcast->id, $this->podcast->id,
$platformLinksData $platformLinksData
); );

View File

@ -51,6 +51,7 @@ class Analytics extends Controller
$episodeId, $episodeId,
$bytesThreshold, $bytesThreshold,
$fileSize, $fileSize,
$duration,
...$filename ...$filename
) { ) {
helper('media'); helper('media');
@ -62,6 +63,7 @@ class Analytics extends Controller
$episodeId, $episodeId,
$bytesThreshold, $bytesThreshold,
$fileSize, $fileSize,
$duration,
$serviceName $serviceName
); );
return redirect()->to(media_base_url($filename)); return redirect()->to(media_base_url($filename));

View File

@ -20,25 +20,23 @@ class AddCategories extends Migration
$this->forge->addField([ $this->forge->addField([
'id' => [ 'id' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
], ],
'parent_id' => [ 'parent_id' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
], ],
'code' => [ 'code' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 32,
], ],
'apple_category' => [ 'apple_category' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 32,
], ],
'google_category' => [ 'google_category' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 32,
], ],
]); ]);
$this->forge->addKey('id', true); $this->forge->addKey('id', true);

View File

@ -20,17 +20,12 @@ class AddLanguages extends Migration
$this->forge->addField([ $this->forge->addField([
'code' => [ 'code' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'comment' => 'ISO 639-1 language code.', 'comment' => 'ISO 639-1 language code',
'constraint' => 2, 'constraint' => 2,
], ],
'name' => [
'type' => 'VARCHAR',
'comment' => 'English language name.',
'constraint' => 191,
],
'native_name' => [ 'native_name' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 128,
], ],
]); ]);
$this->forge->addKey('code', true); $this->forge->addKey('code', true);

View File

@ -19,34 +19,35 @@ class AddPodcasts extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'id' => [ 'id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
'auto_increment' => true, 'auto_increment' => true,
], ],
'title' => [ 'title' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 128,
], ],
'name' => [ 'name' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 32,
'unique' => true, 'unique' => true,
], ],
'description' => [ 'description_markdown' => [
'type' => 'TEXT',
],
'description_html' => [
'type' => 'TEXT', 'type' => 'TEXT',
], ],
'image_uri' => [ 'image_uri' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 255,
], ],
'language' => [ 'language_code' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 2, 'constraint' => 2,
], ],
'category_id' => [ 'category_id' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
'default' => 0, 'default' => 0,
], ],
@ -58,15 +59,15 @@ class AddPodcasts extends Migration
], ],
'owner_name' => [ 'owner_name' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 128,
], ],
'owner_email' => [ 'owner_email' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 255,
], ],
'publisher' => [ 'publisher' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 128,
'null' => true, 'null' => true,
], ],
'type' => [ 'type' => [
@ -76,67 +77,69 @@ class AddPodcasts extends Migration
], ],
'copyright' => [ 'copyright' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 128,
'null' => true, 'null' => true,
], ],
'episode_description_footer' => [ 'episode_description_footer_markdown' => [
'type' => 'TEXT', 'type' => 'TEXT',
'null' => true, 'null' => true,
], ],
'block' => [ 'episode_description_footer_html' => [
'type' => 'TEXT',
'null' => true,
],
'is_blocked' => [
'type' => 'TINYINT', 'type' => 'TINYINT',
'constraint' => 1, 'constraint' => 1,
'default' => 0, 'default' => 0,
], ],
'complete' => [ 'is_completed' => [
'type' => 'TINYINT', 'type' => 'TINYINT',
'constraint' => 1, 'constraint' => 1,
'default' => 0, 'default' => 0,
], ],
'lock' => [ 'is_locked' => [
'type' => 'TINYINT', 'type' => 'TINYINT',
'constraint' => 1, 'constraint' => 1,
'comment' =>
'This tells other podcast platforms whether they are allowed to import this feed.',
'default' => 1, 'default' => 1,
], ],
'imported_feed_url' => [ 'imported_feed_url' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 512,
'comment' => 'comment' =>
'The RSS feed URL if this podcast was imported, NULL otherwise.', 'The RSS feed URL if this podcast was imported, NULL otherwise.',
'null' => true, 'null' => true,
], ],
'new_feed_url' => [ 'new_feed_url' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 512,
'comment' => 'comment' =>
'The RSS new feed URL if this podcast is moving out, NULL otherwise.', 'The RSS new feed URL if this podcast is moving out, NULL otherwise.',
'null' => true, 'null' => true,
], ],
'created_by' => [ 'created_by' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 11,
'unsigned' => true, 'unsigned' => true,
], ],
'updated_by' => [ 'updated_by' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 11,
'unsigned' => true, 'unsigned' => true,
], ],
'created_at' => [ 'created_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
'updated_at' => [ 'updated_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
'deleted_at' => [ 'deleted_at' => [
'type' => 'DATETIME', 'type' => 'DATETIME',
'null' => true, 'null' => true,
], ],
]); ]);
$this->forge->addKey('id', true); $this->forge->addKey('id', true);
$this->forge->addForeignKey('category_id', 'categories', 'id'); $this->forge->addForeignKey('category_id', 'categories', 'id');
$this->forge->addForeignKey('language_code', 'languages', 'code');
$this->forge->addForeignKey('created_by', 'users', 'id'); $this->forge->addForeignKey('created_by', 'users', 'id');
$this->forge->addForeignKey('updated_by', 'users', 'id'); $this->forge->addForeignKey('updated_by', 'users', 'id');
$this->forge->createTable('podcasts'); $this->forge->createTable('podcasts');

View File

@ -19,23 +19,21 @@ class AddEpisodes extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'id' => [ 'id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
'auto_increment' => true, 'auto_increment' => true,
], ],
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'guid' => [ 'guid' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 255,
], ],
'title' => [ 'title' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 128,
], ],
'slug' => [ 'slug' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
@ -43,11 +41,10 @@ class AddEpisodes extends Migration
], ],
'enclosure_uri' => [ 'enclosure_uri' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 255,
], ],
'enclosure_duration' => [ 'enclosure_duration' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
'comment' => 'Playtime in seconds', 'comment' => 'Playtime in seconds',
], ],
@ -57,23 +54,25 @@ class AddEpisodes extends Migration
], ],
'enclosure_filesize' => [ 'enclosure_filesize' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
'comment' => 'File size in bytes', 'comment' => 'File size in bytes',
], ],
'enclosure_headersize' => [ 'enclosure_headersize' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
'comment' => 'Header size in bytes', 'comment' => 'Header size in bytes',
], ],
'description' => [ 'description_markdown' => [
'type' => 'TEXT',
'null' => true,
],
'description_html' => [
'type' => 'TEXT', 'type' => 'TEXT',
'null' => true, 'null' => true,
], ],
'image_uri' => [ 'image_uri' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 255,
'null' => true, 'null' => true,
], ],
'parental_advisory' => [ 'parental_advisory' => [
@ -84,13 +83,11 @@ class AddEpisodes extends Migration
], ],
'number' => [ 'number' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
'null' => true, 'null' => true,
], ],
'season_number' => [ 'season_number' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
'null' => true, 'null' => true,
], ],
@ -99,19 +96,17 @@ class AddEpisodes extends Migration
'constraint' => ['trailer', 'full', 'bonus'], 'constraint' => ['trailer', 'full', 'bonus'],
'default' => 'full', 'default' => 'full',
], ],
'block' => [ 'is_blocked' => [
'type' => 'TINYINT', 'type' => 'TINYINT',
'constraint' => 1, 'constraint' => 1,
'default' => 0, 'default' => 0,
], ],
'created_by' => [ 'created_by' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 11,
'unsigned' => true, 'unsigned' => true,
], ],
'updated_by' => [ 'updated_by' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 11,
'unsigned' => true, 'unsigned' => true,
], ],
'published_at' => [ 'published_at' => [
@ -119,10 +114,10 @@ class AddEpisodes extends Migration
'null' => true, 'null' => true,
], ],
'created_at' => [ 'created_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
'updated_at' => [ 'updated_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
'deleted_at' => [ 'deleted_at' => [
'type' => 'DATETIME', 'type' => 'DATETIME',

View File

@ -19,39 +19,34 @@ class AddPlatforms extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'id' => [ 'id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
'auto_increment' => true, 'auto_increment' => true,
], ],
'name' => [ 'name' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 32,
'unique' => true, 'unique' => true,
], ],
'label' => [ 'label' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 32,
], ],
'home_url' => [ 'home_url' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 255,
], ],
'submit_url' => [ 'submit_url' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 512,
'null' => true, 'null' => true,
'default' => null, 'default' => null,
], ],
'icon_filename' => [
'type' => 'VARCHAR',
'constraint' => 1024,
],
'created_at' => [ 'created_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
'updated_at' => [ 'updated_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
]); ]);
$this->forge->addKey('id', true); $this->forge->addKey('id', true);

View File

@ -18,21 +18,28 @@ class AddAnalyticsPodcasts extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'date' => [ 'date' => [
'type' => 'date', 'type' => 'DATE',
], ],
'hits' => [ 'duration' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, ],
'bandwidth' => [
'type' => 'BIGINT',
'unsigned' => true,
], ],
'unique_listeners' => [ 'unique_listeners' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1,
],
'hits' => [
'type' => 'INT',
'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);

View File

@ -18,26 +18,24 @@ class AddAnalyticsPodcastsByEpisode extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'date' => [ 'date' => [
'type' => 'date', 'type' => 'DATE',
], ],
'episode_id' => [ 'episode_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'age' => [ 'age' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'comment' => 'Days since episode publication date',
'unsigned' => true, 'unsigned' => true,
], ],
'hits' => [ 'hits' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);

View File

@ -0,0 +1,52 @@
<?php
/**
* Class AddAnalyticsPodcastsByHour
* Creates analytics_podcasts_by_hour table in database
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddAnalyticsPodcastsByHour extends Migration
{
public function up()
{
$this->forge->addField([
'podcast_id' => [
'type' => 'INT',
'unsigned' => true,
],
'date' => [
'type' => 'DATE',
],
'hour' => [
'type' => 'INT',
'unsigned' => true,
],
'hits' => [
'type' => 'INT',
'unsigned' => true,
'default' => 1,
],
]);
$this->forge->addPrimaryKey(['podcast_id', 'date', 'hour']);
$this->forge->addField(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()'
);
$this->forge->addField(
'`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()'
);
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id');
$this->forge->createTable('analytics_podcasts_by_hour');
}
public function down()
{
$this->forge->dropTable('analytics_podcasts_by_hour');
}
}

View File

@ -18,12 +18,11 @@ class AddAnalyticsPodcastsByPlayer extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'date' => [ 'date' => [
'type' => 'date', 'type' => 'DATE',
], ],
'service' => [ 'service' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
@ -41,14 +40,14 @@ class AddAnalyticsPodcastsByPlayer extends Migration
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 32, 'constraint' => 32,
], ],
'bot' => [ 'is_bot' => [
'type' => 'TINYINT', 'type' => 'TINYINT',
'constraint' => 1, 'constraint' => 1,
'default' => 0, 'default' => 0,
], ],
'hits' => [ 'hits' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);
@ -59,7 +58,7 @@ class AddAnalyticsPodcastsByPlayer extends Migration
'app', 'app',
'device', 'device',
'os', 'os',
'bot', 'is_bot',
]); ]);
$this->forge->addField( $this->forge->addField(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()' '`created_at` timestamp NOT NULL DEFAULT current_timestamp()'

View File

@ -18,12 +18,11 @@ class AddAnalyticsPodcastsByCountry extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'date' => [ 'date' => [
'type' => 'date', 'type' => 'DATE',
], ],
'country_code' => [ 'country_code' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
@ -32,7 +31,7 @@ class AddAnalyticsPodcastsByCountry extends Migration
], ],
'hits' => [ 'hits' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);

View File

@ -18,12 +18,11 @@ class AddAnalyticsPodcastsByRegion extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'date' => [ 'date' => [
'type' => 'date', 'type' => 'DATE',
], ],
'country_code' => [ 'country_code' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
@ -45,7 +44,7 @@ class AddAnalyticsPodcastsByRegion extends Migration
], ],
'hits' => [ 'hits' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);

View File

@ -13,37 +13,36 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration; use CodeIgniter\Database\Migration;
class AddPlatformLinks extends Migration class AddPodcastsPlatforms extends Migration
{ {
public function up() public function up()
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'platform_id' => [ 'platform_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'link_url' => [ 'link_url' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 191, 'constraint' => 512,
], ],
'visible' => [ 'is_visible' => [
'type' => 'TINYINT', 'type' => 'TINYINT',
'constraint' => 1, 'constraint' => 1,
'default' => 0, 'default' => 0,
], ],
'created_at' => [ 'created_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
'updated_at' => [ 'updated_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
]); ]);
$this->forge->addPrimaryKey(['podcast_id', 'platform_id']); $this->forge->addPrimaryKey(['podcast_id', 'platform_id']);
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); $this->forge->addForeignKey('podcast_id', 'podcasts', 'id');
$this->forge->addForeignKey('platform_id', 'platforms', 'id'); $this->forge->addForeignKey('platform_id', 'platforms', 'id');

View File

@ -18,12 +18,11 @@ class AddAnalyticsWebsiteByBrowser extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'date' => [ 'date' => [
'type' => 'date', 'type' => 'DATE',
], ],
'browser' => [ 'browser' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
@ -32,10 +31,11 @@ class AddAnalyticsWebsiteByBrowser extends Migration
'hits' => [ 'hits' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);
$this->forge->addPrimaryKey(['podcast_id', 'date', 'browser']); $this->forge->addPrimaryKey(['podcast_id', 'date', 'browser']);
$this->forge->addField( $this->forge->addField(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()' '`created_at` timestamp NOT NULL DEFAULT current_timestamp()'

View File

@ -18,17 +18,15 @@ class AddAnalyticsWebsiteByReferer extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'date' => [ 'date' => [
'type' => 'date', 'type' => 'DATE',
], ],
'referer' => [ 'referer_url' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 512, 'constraint' => 512,
'comment' => 'Referer URL.',
], ],
'domain' => [ 'domain' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
@ -37,16 +35,16 @@ class AddAnalyticsWebsiteByReferer extends Migration
], ],
'keywords' => [ 'keywords' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 384, 'constraint' => 384, // length of referer_url (512) - domain (128)
'null' => true, 'null' => true,
], ],
'hits' => [ 'hits' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);
$this->forge->addPrimaryKey(['podcast_id', 'date', 'referer']); $this->forge->addPrimaryKey(['podcast_id', 'date', 'referer_url']);
$this->forge->addField( $this->forge->addField(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()' '`created_at` timestamp NOT NULL DEFAULT current_timestamp()'
); );

View File

@ -18,25 +18,23 @@ class AddAnalyticsWebsiteByEntryPage extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'date' => [ 'date' => [
'type' => 'date', 'type' => 'DATE',
], ],
'entry_page' => [ 'entry_page_url' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 512, 'constraint' => 512,
'comment' => 'Entry page URL.',
], ],
'hits' => [ 'hits' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);
$this->forge->addPrimaryKey(['podcast_id', 'date', 'entry_page']); $this->forge->addPrimaryKey(['podcast_id', 'date', 'entry_page_url']);
$this->forge->addField( $this->forge->addField(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()' '`created_at` timestamp NOT NULL DEFAULT current_timestamp()'
); );

View File

@ -18,8 +18,7 @@ class AddAnalyticsUnknownUseragents extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'id' => [ 'id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
'auto_increment' => true, 'auto_increment' => true,
], ],
@ -30,12 +29,12 @@ class AddAnalyticsUnknownUseragents extends Migration
], ],
'hits' => [ 'hits' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10, 'unsigned' => true,
'default' => 1, 'default' => 1,
], ],
]); ]);
$this->forge->addKey('id', true); $this->forge->addKey('id', true);
// `created_at` and `updated_at` are created with SQL because Model class wont be used for insertion (Stored Procedure will be used instead) // `created_at` and `updated_at` are created with SQL because Model class wont be used for insertion (Procedure will be used instead)
$this->forge->addField( $this->forge->addField(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()' '`created_at` timestamp NOT NULL DEFAULT current_timestamp()'
); );

View File

@ -1,8 +1,8 @@
<?php <?php
/** /**
* Class AddAnalyticsPodcastsStoredProcedure * Class AddAnalyticsPodcastsProcedure
* Creates analytics_podcasts stored procedure in database * Creates analytics_podcasts procedure in database
* @copyright 2020 Podlibre * @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/ * @link https://castopod.org/
@ -12,18 +12,18 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration; use CodeIgniter\Database\Migration;
class AddAnalyticsPodcastsStoredProcedure extends Migration class AddAnalyticsPodcastsProcedure extends Migration
{ {
public function up() public function up()
{ {
// Creates Stored Procedure for data insertion // Creates Procedure for data insertion
// Example: CALL analytics_podcasts(1, 2, 'FR', 'IDF', 48.853, 2.349, PodcastAddict, 'phone', 'android', 0, 1); // Example: CALL analytics_podcasts(1, 2, 'FR', 'IDF', 48.853, 2.349, PodcastAddict, 'phone', 'android', 0, 1);
$prefix = $this->db->getPrefix(); $prefix = $this->db->getPrefix();
$createQuery = <<<EOD $createQuery = <<<EOD
CREATE PROCEDURE `{$prefix}analytics_podcasts` ( CREATE PROCEDURE `{$prefix}analytics_podcasts` (
IN `p_podcast_id` BIGINT(20) UNSIGNED, IN `p_podcast_id` INT UNSIGNED,
IN `p_episode_id` BIGINT(20) UNSIGNED, IN `p_episode_id` INT UNSIGNED,
IN `p_country_code` VARCHAR(3) CHARSET utf8mb4, IN `p_country_code` VARCHAR(3) CHARSET utf8mb4,
IN `p_region_code` VARCHAR(3) CHARSET utf8mb4, IN `p_region_code` VARCHAR(3) CHARSET utf8mb4,
IN `p_latitude` FLOAT, IN `p_latitude` FLOAT,
@ -33,28 +33,42 @@ CREATE PROCEDURE `{$prefix}analytics_podcasts` (
IN `p_device` VARCHAR(32) CHARSET utf8mb4, IN `p_device` VARCHAR(32) CHARSET utf8mb4,
IN `p_os` VARCHAR(32) CHARSET utf8mb4, IN `p_os` VARCHAR(32) CHARSET utf8mb4,
IN `p_bot` TINYINT(1) UNSIGNED, IN `p_bot` TINYINT(1) UNSIGNED,
IN `p_filesize` INT UNSIGNED,
IN `p_duration` INT UNSIGNED,
IN `p_new_listener` TINYINT(1) UNSIGNED IN `p_new_listener` TINYINT(1) UNSIGNED
) MODIFIES SQL DATA ) MODIFIES SQL DATA
DETERMINISTIC DETERMINISTIC
SQL SECURITY INVOKER SQL SECURITY INVOKER
COMMENT 'Add one hit in podcast logs tables.' COMMENT 'Add one hit in podcast logs tables.'
BEGIN BEGIN
SET @current_datetime = NOW();
SET @current_date = DATE(@current_datetime);
SET @current_hour = HOUR(@current_datetime);
IF NOT `p_bot` THEN IF NOT `p_bot` THEN
INSERT INTO `{$prefix}analytics_podcasts`(`podcast_id`, `date`) INSERT INTO `{$prefix}analytics_podcasts`(`podcast_id`, `date`)
VALUES (p_podcast_id, DATE(NOW())) VALUES (p_podcast_id, @current_date)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1, `unique_listeners`=`unique_listeners`+`p_new_listener`; ON DUPLICATE KEY UPDATE
`duration`=`duration`+`p_duration`,
`bandwidth`=`bandwidth`+`p_filesize`,
`hits`=`hits`+1,
`unique_listeners`=`unique_listeners`+`p_new_listener`;
INSERT INTO `{$prefix}analytics_podcasts_by_hour`(`podcast_id`, `date`, `hour`)
VALUES (p_podcast_id, @current_date, @current_hour)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
INSERT INTO `{$prefix}analytics_podcasts_by_episode`(`podcast_id`, `episode_id`, `date`, `age`) INSERT INTO `{$prefix}analytics_podcasts_by_episode`(`podcast_id`, `episode_id`, `date`, `age`)
SELECT p_podcast_id, p_episode_id, DATE(NOW()), datediff(now(),`published_at`) FROM `{$prefix}episodes` WHERE `id`= p_episode_id SELECT p_podcast_id, p_episode_id, @current_date, datediff(@current_datetime,`published_at`) FROM `{$prefix}episodes` WHERE `id`= p_episode_id
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
INSERT INTO `{$prefix}analytics_podcasts_by_country`(`podcast_id`, `country_code`, `date`) INSERT INTO `{$prefix}analytics_podcasts_by_country`(`podcast_id`, `country_code`, `date`)
VALUES (p_podcast_id, p_country_code, DATE(NOW())) VALUES (p_podcast_id, p_country_code, @current_date)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
INSERT INTO `{$prefix}analytics_podcasts_by_region`(`podcast_id`, `country_code`, `region_code`, `latitude`, `longitude`, `date`) INSERT INTO `{$prefix}analytics_podcasts_by_region`(`podcast_id`, `country_code`, `region_code`, `latitude`, `longitude`, `date`)
VALUES (p_podcast_id, p_country_code, p_region_code, p_latitude, p_longitude, DATE(NOW())) VALUES (p_podcast_id, p_country_code, p_region_code, p_latitude, p_longitude, @current_date)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
END IF; END IF;
INSERT INTO `{$prefix}analytics_podcasts_by_player`(`podcast_id`, `service`, `app`, `device`, `os`, `bot`, `date`) INSERT INTO `{$prefix}analytics_podcasts_by_player`(`podcast_id`, `service`, `app`, `device`, `os`, `is_bot`, `date`)
VALUES (p_podcast_id, p_service, p_app, p_device, p_os, p_bot, DATE(NOW())) VALUES (p_podcast_id, p_service, p_app, p_device, p_os, p_bot, @current_date)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
END END
EOD; EOD;

View File

@ -1,8 +1,8 @@
<?php <?php
/** /**
* Class AddAnalyticsUnknownUseragentsStoredProcedure * Class AddAnalyticsUnknownUseragentsProcedure
* Creates analytics_unknown_useragents stored procedure in database * Creates analytics_unknown_useragents procedure in database
* @copyright 2020 Podlibre * @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/ * @link https://castopod.org/
@ -12,11 +12,11 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration; use CodeIgniter\Database\Migration;
class AddAnalyticsUnknownUseragentsStoredProcedure extends Migration class AddAnalyticsUnknownUseragentsProcedure extends Migration
{ {
public function up() public function up()
{ {
// Creates Stored Procedure for data insertion // Creates Procedure for data insertion
// Example: CALL analytics_unknown_useragents('Podcasts/1430.46 CFNetwork/1125.2 Darwin/19.4.0'); // Example: CALL analytics_unknown_useragents('Podcasts/1430.46 CFNetwork/1125.2 Darwin/19.4.0');
$procedureName = $this->db->prefixTable('analytics_unknown_useragents'); $procedureName = $this->db->prefixTable('analytics_unknown_useragents');
$createQuery = <<<EOD $createQuery = <<<EOD

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Class AddAnalyticsWebsiteStoredProcedure * Class AddAnalyticsWebsiteProcedure
* Creates analytics_website stored procedure in database * Creates analytics_website stored procedure in database
* @copyright 2020 Podlibre * @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3 * @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
@ -12,27 +12,30 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration; use CodeIgniter\Database\Migration;
class AddAnalyticsWebsiteStoredProcedure extends Migration class AddAnalyticsWebsiteProcedure extends Migration
{ {
public function up() public function up()
{ {
// Creates Stored Procedure for data insertion // Creates Procedure for data insertion
// Example: CALL analytics_website(1,'FR','Firefox'); // Example: CALL analytics_website(1,'FR','Firefox');
$procedureName = $this->db->prefixTable('analytics_website'); $procedureName = $this->db->prefixTable('analytics_website');
$createQuery = <<<EOD $createQuery = <<<EOD
CREATE PROCEDURE `$procedureName` (IN `p_podcast_id` BIGINT(20) UNSIGNED, IN `p_browser` VARCHAR(191) CHARSET utf8mb4, IN `p_entry_page` VARCHAR(512) CHARSET utf8mb4, IN `p_referer` VARCHAR(512) CHARSET utf8mb4, IN `p_domain` VARCHAR(128) CHARSET utf8mb4, IN `p_keywords` VARCHAR(384) CHARSET utf8mb4) MODIFIES SQL DATA CREATE PROCEDURE `$procedureName` (IN `p_podcast_id` INT UNSIGNED, IN `p_browser` VARCHAR(191) CHARSET utf8mb4, IN `p_entry_page` VARCHAR(512) CHARSET utf8mb4, IN `p_referer_url` VARCHAR(512) CHARSET utf8mb4, IN `p_domain` VARCHAR(128) CHARSET utf8mb4, IN `p_keywords` VARCHAR(384) CHARSET utf8mb4) MODIFIES SQL DATA
DETERMINISTIC DETERMINISTIC
SQL SECURITY INVOKER SQL SECURITY INVOKER
COMMENT 'Add one hit in website logs tables.' COMMENT 'Add one hit in website logs tables.'
BEGIN BEGIN
SET @current_date = DATE(NOW());
INSERT INTO {$procedureName}_by_browser(`podcast_id`, `browser`, `date`) INSERT INTO {$procedureName}_by_browser(`podcast_id`, `browser`, `date`)
VALUES (p_podcast_id, p_browser, DATE(NOW())) VALUES (p_podcast_id, p_browser, @current_date)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
INSERT INTO {$procedureName}_by_referer(`podcast_id`, `referer`, `domain`, `keywords`, `date`) INSERT INTO {$procedureName}_by_referer(`podcast_id`, `referer_url`, `domain`, `keywords`, `date`)
VALUES (p_podcast_id, p_referer, p_domain, p_keywords, DATE(NOW())) VALUES (p_podcast_id, p_referer_url, p_domain, p_keywords, @current_date)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
INSERT INTO {$procedureName}_by_entry_page(`podcast_id`, `entry_page`, `date`) INSERT INTO {$procedureName}_by_entry_page(`podcast_id`, `entry_page_url`, `date`)
VALUES (p_podcast_id, p_entry_page, DATE(NOW())) VALUES (p_podcast_id, p_entry_page, @current_date)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1; ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
END END
EOD; EOD;

View File

@ -13,24 +13,21 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration; use CodeIgniter\Database\Migration;
class AddUsersPodcasts extends Migration class AddPodcastsUsers extends Migration
{ {
public function up() public function up()
{ {
$this->forge->addField([ $this->forge->addField([
'user_id' => [ 'podcast_id' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 11,
'unsigned' => true, 'unsigned' => true,
], ],
'podcast_id' => [ 'user_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'group_id' => [ 'group_id' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 11,
'unsigned' => true, 'unsigned' => true,
], ],
]); ]);
@ -38,11 +35,11 @@ class AddUsersPodcasts extends Migration
$this->forge->addForeignKey('user_id', 'users', 'id'); $this->forge->addForeignKey('user_id', 'users', 'id');
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id'); $this->forge->addForeignKey('podcast_id', 'podcasts', 'id');
$this->forge->addForeignKey('group_id', 'auth_groups', 'id'); $this->forge->addForeignKey('group_id', 'auth_groups', 'id');
$this->forge->createTable('users_podcasts'); $this->forge->createTable('podcasts_users');
} }
public function down() public function down()
{ {
$this->forge->dropTable('users_podcasts'); $this->forge->dropTable('podcasts_users');
} }
} }

View File

@ -20,13 +20,12 @@ class AddPages extends Migration
$this->forge->addField([ $this->forge->addField([
'id' => [ 'id' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 11,
'unsigned' => true, 'unsigned' => true,
'auto_increment' => true, 'auto_increment' => true,
], ],
'title' => [ 'title' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
'constraint' => 1024, 'constraint' => 255,
], ],
'slug' => [ 'slug' => [
'type' => 'VARCHAR', 'type' => 'VARCHAR',
@ -37,10 +36,10 @@ class AddPages extends Migration
'type' => 'TEXT', 'type' => 'TEXT',
], ],
'created_at' => [ 'created_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
'updated_at' => [ 'updated_at' => [
'type' => 'TIMESTAMP', 'type' => 'DATETIME',
], ],
'deleted_at' => [ 'deleted_at' => [
'type' => 'DATETIME', 'type' => 'DATETIME',

View File

@ -19,13 +19,11 @@ class AddPodcastsCategories extends Migration
{ {
$this->forge->addField([ $this->forge->addField([
'podcast_id' => [ 'podcast_id' => [
'type' => 'BIGINT', 'type' => 'INT',
'constraint' => 20,
'unsigned' => true, 'unsigned' => true,
], ],
'category_id' => [ 'category_id' => [
'type' => 'INT', 'type' => 'INT',
'constraint' => 10,
'unsigned' => true, 'unsigned' => true,
], ],
]); ]);

View File

@ -46,6 +46,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
$date = strtotime(date('Y-m-d', $date) . ' +1 day') $date = strtotime(date('Y-m-d', $date) . ' +1 day')
) { ) {
$analytics_podcasts = []; $analytics_podcasts = [];
$analytics_podcasts_by_hour = [];
$analytics_podcasts_by_country = []; $analytics_podcasts_by_country = [];
$analytics_podcasts_by_episode = []; $analytics_podcasts_by_episode = [];
$analytics_podcasts_by_player = []; $analytics_podcasts_by_player = [];
@ -83,7 +84,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
? $player['device'] ? $player['device']
: ''; : '';
$os = isset($player['os']) ? $player['os'] : ''; $os = isset($player['os']) ? $player['os'] : '';
$bot = isset($player['bot']) ? $player['bot'] : 0; $isBot = isset($player['bot']) ? $player['bot'] : 0;
$fakeIp = $fakeIp =
rand(0, 255) . rand(0, 255) .
@ -124,9 +125,17 @@ class FakePodcastsAnalyticsSeeder extends Seeder
$analytics_podcasts[] = [ $analytics_podcasts[] = [
'podcast_id' => $podcast->id, 'podcast_id' => $podcast->id,
'date' => date('Y-m-d', $date), 'date' => date('Y-m-d', $date),
'duration' => rand(60, 3600),
'bandwidth' => rand(1000000, 10000000),
'hits' => $hits, 'hits' => $hits,
'unique_listeners' => $hits, 'unique_listeners' => $hits,
]; ];
$analytics_podcasts_by_hour[] = [
'podcast_id' => $podcast->id,
'date' => date('Y-m-d', $date),
'hour' => rand(0, 23),
'hits' => $hits,
];
$analytics_podcasts_by_country[] = [ $analytics_podcasts_by_country[] = [
'podcast_id' => $podcast->id, 'podcast_id' => $podcast->id,
'date' => date('Y-m-d', $date), 'date' => date('Y-m-d', $date),
@ -147,7 +156,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
'app' => $app, 'app' => $app,
'device' => $device, 'device' => $device,
'os' => $os, 'os' => $os,
'bot' => $bot, 'is_bot' => $isBot,
'hits' => $hits, 'hits' => $hits,
]; ];
$analytics_podcasts_by_region[] = [ $analytics_podcasts_by_region[] = [
@ -165,6 +174,10 @@ class FakePodcastsAnalyticsSeeder extends Seeder
->table('analytics_podcasts') ->table('analytics_podcasts')
->ignore(true) ->ignore(true)
->insertBatch($analytics_podcasts); ->insertBatch($analytics_podcasts);
$this->db
->table('analytics_podcasts_by_hour')
->ignore(true)
->insertBatch($analytics_podcasts_by_hour);
$this->db $this->db
->table('analytics_podcasts_by_country') ->table('analytics_podcasts_by_country')
->ignore(true) ->ignore(true)

View File

@ -226,13 +226,13 @@ class FakeWebsiteAnalyticsSeeder extends Seeder
$website_by_entry_page[] = [ $website_by_entry_page[] = [
'podcast_id' => $podcast->id, 'podcast_id' => $podcast->id,
'date' => date('Y-m-d', $date), 'date' => date('Y-m-d', $date),
'entry_page' => $episode->link, 'entry_page_url' => $episode->link,
'hits' => $hits, 'hits' => $hits,
]; ];
$website_by_referer[] = [ $website_by_referer[] = [
'podcast_id' => $podcast->id, 'podcast_id' => $podcast->id,
'date' => date('Y-m-d', $date), 'date' => date('Y-m-d', $date),
'referer' => 'referer_url' =>
'http://' . $domain . '/?q=' . $keyword, 'http://' . $domain . '/?q=' . $keyword,
'domain' => $domain, 'domain' => $domain,
'keywords' => $keyword, 'keywords' => $keyword,

View File

@ -24,613 +24,506 @@ class LanguageSeeder extends Seeder
public function run() public function run()
{ {
$data = [ $data = [
['code' => 'aa', 'name' => 'Afar', 'native_name' => 'Afaraf'], ['code' => 'aa', 'native_name' => 'Afaraf'],
[ [
'code' => 'ab', 'code' => 'ab',
'name' => 'Abkhazian',
'native_name' => 'аҧсуа бызшәа, аҧсшәа', 'native_name' => 'аҧсуа бызшәа, аҧсшәа',
], ],
['code' => 'ae', 'name' => 'Avestan', 'native_name' => 'avesta'], ['code' => 'ae', 'native_name' => 'avesta'],
[ [
'code' => 'af', 'code' => 'af',
'name' => 'Afrikaans',
'native_name' => 'Afrikaans', 'native_name' => 'Afrikaans',
], ],
['code' => 'ak', 'name' => 'Akan', 'native_name' => 'Akan'], ['code' => 'ak', 'native_name' => 'Akan'],
['code' => 'am', 'name' => 'Amharic', 'native_name' => 'አማርኛ'], ['code' => 'am', 'native_name' => 'አማርኛ'],
[ [
'code' => 'an', 'code' => 'an',
'name' => 'Aragonese',
'native_name' => 'aragonés', 'native_name' => 'aragonés',
], ],
['code' => 'ar', 'name' => 'Arabic', 'native_name' => 'العربية'], ['code' => 'ar', 'native_name' => 'العربية'],
['code' => 'as', 'name' => 'Assamese', 'native_name' => 'অসমীয়া'], ['code' => 'as', 'native_name' => 'অসমীয়া'],
[ [
'code' => 'av', 'code' => 'av',
'name' => 'Avaric',
'native_name' => 'авар мацӀ, магӀарул мацӀ', 'native_name' => 'авар мацӀ, магӀарул мацӀ',
], ],
['code' => 'ay', 'name' => 'Aymara', 'native_name' => 'aymar aru'], ['code' => 'ay', 'native_name' => 'aymar aru'],
[ [
'code' => 'az', 'code' => 'az',
'name' => 'Azerbaijani',
'native_name' => 'azərbaycan dili', 'native_name' => 'azərbaycan dili',
], ],
[ [
'code' => 'ba', 'code' => 'ba',
'name' => 'Bashkir',
'native_name' => 'башҡорт теле', 'native_name' => 'башҡорт теле',
], ],
[ [
'code' => 'be', 'code' => 'be',
'name' => 'Belarusian',
'native_name' => 'беларуская мова', 'native_name' => 'беларуская мова',
], ],
[ [
'code' => 'bg', 'code' => 'bg',
'name' => 'Bulgarian',
'native_name' => 'български език', 'native_name' => 'български език',
], ],
[ [
'code' => 'bh', 'code' => 'bh',
'name' => 'Bihari languages',
'native_name' => 'भोजपुरी', 'native_name' => 'भोजपुरी',
], ],
['code' => 'bi', 'name' => 'Bislama', 'native_name' => 'Bislama'], ['code' => 'bi', 'native_name' => 'Bislama'],
[ [
'code' => 'bm', 'code' => 'bm',
'name' => 'Bambara',
'native_name' => 'bamanankan', 'native_name' => 'bamanankan',
], ],
['code' => 'bn', 'name' => 'Bengali', 'native_name' => 'বাংলা'], ['code' => 'bn', 'native_name' => 'বাংলা'],
['code' => 'bo', 'name' => 'Tibetan', 'native_name' => 'བོད་ཡིག'], ['code' => 'bo', 'native_name' => 'བོད་ཡིག'],
['code' => 'br', 'name' => 'Breton', 'native_name' => 'brezhoneg'], ['code' => 'br', 'native_name' => 'brezhoneg'],
[ [
'code' => 'bs', 'code' => 'bs',
'name' => 'Bosnian',
'native_name' => 'bosanski jezik', 'native_name' => 'bosanski jezik',
], ],
[ [
'code' => 'ca', 'code' => 'ca',
'name' => 'Catalan, Valencian',
'native_name' => 'català, valencià', 'native_name' => 'català, valencià',
], ],
[ [
'code' => 'ce', 'code' => 'ce',
'name' => 'Chechen',
'native_name' => 'нохчийн мотт', 'native_name' => 'нохчийн мотт',
], ],
['code' => 'ch', 'name' => 'Chamorro', 'native_name' => 'Chamoru'], ['code' => 'ch', 'native_name' => 'Chamoru'],
[ [
'code' => 'co', 'code' => 'co',
'name' => 'Corsican',
'native_name' => 'corsu, lingua corsa', 'native_name' => 'corsu, lingua corsa',
], ],
['code' => 'cr', 'name' => 'Cree', 'native_name' => 'ᓀᐦᐃᔭᐍᐏᐣ'], ['code' => 'cr', 'native_name' => 'ᓀᐦᐃᔭᐍᐏᐣ'],
[ [
'code' => 'cs', 'code' => 'cs',
'name' => 'Czech',
'native_name' => 'čeština, český jazyk', 'native_name' => 'čeština, český jazyk',
], ],
[ [
'code' => 'cu', 'code' => 'cu',
'name' =>
'Church Slavic, Old Slavonic, Church Slavonic, Old Bulgarian, Old Church Slavonic',
'native_name' => 'ѩзыкъ словѣньскъ', 'native_name' => 'ѩзыкъ словѣньскъ',
], ],
[ [
'code' => 'cv', 'code' => 'cv',
'name' => 'Chuvash',
'native_name' => 'чӑваш чӗлхи', 'native_name' => 'чӑваш чӗлхи',
], ],
['code' => 'cy', 'name' => 'Welsh', 'native_name' => 'Cymraeg'], ['code' => 'cy', 'native_name' => 'Cymraeg'],
['code' => 'da', 'name' => 'Danish', 'native_name' => 'dansk'], ['code' => 'da', 'native_name' => 'dansk'],
['code' => 'de', 'name' => 'German', 'native_name' => 'Deutsch'], ['code' => 'de', 'native_name' => 'Deutsch'],
[ [
'code' => 'dv', 'code' => 'dv',
'name' => 'Divehi, Dhivehi, Maldivian',
'native_name' => 'ދިވެހި', 'native_name' => 'ދިވެހި',
], ],
['code' => 'dz', 'name' => 'Dzongkha', 'native_name' => 'རྫོང་ཁ'], ['code' => 'dz', 'native_name' => 'རྫོང་ཁ'],
['code' => 'ee', 'name' => 'Ewe', 'native_name' => 'Eʋegbe'], ['code' => 'ee', 'native_name' => 'Eʋegbe'],
[ [
'code' => 'el', 'code' => 'el',
'name' => 'Greek, Modern (1453)',
'native_name' => 'ελληνικά', 'native_name' => 'ελληνικά',
], ],
['code' => 'en', 'name' => 'English', 'native_name' => 'English'], ['code' => 'en', 'native_name' => 'English'],
[ [
'code' => 'eo', 'code' => 'eo',
'name' => 'Esperanto',
'native_name' => 'Esperanto', 'native_name' => 'Esperanto',
], ],
[ [
'code' => 'es', 'code' => 'es',
'name' => 'Spanish, Castilian',
'native_name' => 'Español', 'native_name' => 'Español',
], ],
[ [
'code' => 'et', 'code' => 'et',
'name' => 'Estonian',
'native_name' => 'eesti, eesti keel', 'native_name' => 'eesti, eesti keel',
], ],
[ [
'code' => 'eu', 'code' => 'eu',
'name' => 'Basque',
'native_name' => 'euskara, euskera', 'native_name' => 'euskara, euskera',
], ],
['code' => 'fa', 'name' => 'Persian', 'native_name' => 'فارسی'], ['code' => 'fa', 'native_name' => 'فارسی'],
[ [
'code' => 'ff', 'code' => 'ff',
'name' => 'Fulah',
'native_name' => 'Fulfulde, Pulaar, Pular', 'native_name' => 'Fulfulde, Pulaar, Pular',
], ],
[ [
'code' => 'fi', 'code' => 'fi',
'name' => 'Finnish',
'native_name' => 'suomi, suomen kieli', 'native_name' => 'suomi, suomen kieli',
], ],
[ [
'code' => 'fj', 'code' => 'fj',
'name' => 'Fijian',
'native_name' => 'vosa Vakaviti', 'native_name' => 'vosa Vakaviti',
], ],
['code' => 'fo', 'name' => 'Faroese', 'native_name' => 'føroyskt'], ['code' => 'fo', 'native_name' => 'føroyskt'],
[ [
'code' => 'fr', 'code' => 'fr',
'name' => 'French',
'native_name' => 'français, langue française', 'native_name' => 'français, langue française',
], ],
[ [
'code' => 'fy', 'code' => 'fy',
'name' => 'Western Frisian',
'native_name' => 'Frysk', 'native_name' => 'Frysk',
], ],
['code' => 'ga', 'name' => 'Irish', 'native_name' => 'Gaeilge'], ['code' => 'ga', 'native_name' => 'Gaeilge'],
[ [
'code' => 'gd', 'code' => 'gd',
'name' => 'Gaelic, Scottish Gaelic',
'native_name' => 'Gàidhlig', 'native_name' => 'Gàidhlig',
], ],
['code' => 'gl', 'name' => 'Galician', 'native_name' => 'Galego'], ['code' => 'gl', 'native_name' => 'Galego'],
['code' => 'gn', 'name' => 'Guarani', 'native_name' => 'Avañe\'ẽ'], ['code' => 'gn', 'native_name' => 'Avañe\'ẽ'],
['code' => 'gu', 'name' => 'Gujarati', 'native_name' => 'ગુજરાતી'], ['code' => 'gu', 'native_name' => 'ગુજરાતી'],
[ [
'code' => 'gv', 'code' => 'gv',
'name' => 'Manx',
'native_name' => 'Gaelg, Gailck', 'native_name' => 'Gaelg, Gailck',
], ],
[ [
'code' => 'ha', 'code' => 'ha',
'name' => 'Hausa',
'native_name' => '(Hausa) هَوُسَ', 'native_name' => '(Hausa) هَوُسَ',
], ],
['code' => 'he', 'name' => 'Hebrew', 'native_name' => 'עברית'], ['code' => 'he', 'native_name' => 'עברית'],
[ [
'code' => 'hi', 'code' => 'hi',
'name' => 'Hindi',
'native_name' => 'हिन्दी, हिंदी', 'native_name' => 'हिन्दी, हिंदी',
], ],
[ [
'code' => 'ho', 'code' => 'ho',
'name' => 'Hiri Motu',
'native_name' => 'Hiri Motu', 'native_name' => 'Hiri Motu',
], ],
[ [
'code' => 'hr', 'code' => 'hr',
'name' => 'Croatian',
'native_name' => 'hrvatski jezik', 'native_name' => 'hrvatski jezik',
], ],
[ [
'code' => 'ht', 'code' => 'ht',
'name' => 'Haitian, Haitian Creole',
'native_name' => 'Kreyòl ayisyen', 'native_name' => 'Kreyòl ayisyen',
], ],
['code' => 'hu', 'name' => 'Hungarian', 'native_name' => 'magyar'], ['code' => 'hu', 'native_name' => 'magyar'],
['code' => 'hy', 'name' => 'Armenian', 'native_name' => 'Հայերեն'], ['code' => 'hy', 'native_name' => 'Հայերեն'],
['code' => 'hz', 'name' => 'Herero', 'native_name' => 'Otjiherero'], ['code' => 'hz', 'native_name' => 'Otjiherero'],
[ [
'code' => 'ia', 'code' => 'ia',
'name' =>
'Interlingua (International Auxiliary Language Association)',
'native_name' => 'Interlingua', 'native_name' => 'Interlingua',
], ],
[ [
'code' => 'id', 'code' => 'id',
'name' => 'Indonesian',
'native_name' => 'Bahasa Indonesia', 'native_name' => 'Bahasa Indonesia',
], ],
[ [
'code' => 'ie', 'code' => 'ie',
'name' => 'Interlingue, Occidental',
'native_name' => 'native_name' =>
'(originally:) Occidental, (after WWII:) Interlingue', '(originally:) Occidental, (after WWII:) Interlingue',
], ],
['code' => 'ig', 'name' => 'Igbo', 'native_name' => 'Asụsụ Igbo'], ['code' => 'ig', 'native_name' => 'Asụsụ Igbo'],
[ [
'code' => 'ii', 'code' => 'ii',
'name' => 'Sichuan Yi, Nuosu',
'native_name' => 'ꆈꌠ꒿ Nuosuhxop', 'native_name' => 'ꆈꌠ꒿ Nuosuhxop',
], ],
[ [
'code' => 'ik', 'code' => 'ik',
'name' => 'Inupiaq',
'native_name' => 'Iñupiaq, Iñupiatun', 'native_name' => 'Iñupiaq, Iñupiatun',
], ],
['code' => 'io', 'name' => 'Ido', 'native_name' => 'Ido'], ['code' => 'io', 'native_name' => 'Ido'],
[ [
'code' => 'is', 'code' => 'is',
'name' => 'Icelandic',
'native_name' => 'Íslenska', 'native_name' => 'Íslenska',
], ],
['code' => 'it', 'name' => 'Italian', 'native_name' => 'Italiano'], ['code' => 'it', 'native_name' => 'Italiano'],
['code' => 'iu', 'name' => 'Inuktitut', 'native_name' => 'ᐃᓄᒃᑎᑐᑦ'], ['code' => 'iu', 'native_name' => 'ᐃᓄᒃᑎᑐᑦ'],
[ [
'code' => 'ja', 'code' => 'ja',
'name' => 'Japanese',
'native_name' => '日本語 (にほんご)', 'native_name' => '日本語 (にほんご)',
], ],
[ [
'code' => 'jv', 'code' => 'jv',
'name' => 'Javanese',
'native_name' => 'ꦧꦱꦗꦮ, Basa Jawa', 'native_name' => 'ꦧꦱꦗꦮ, Basa Jawa',
], ],
['code' => 'ka', 'name' => 'Georgian', 'native_name' => 'ქართული'], ['code' => 'ka', 'native_name' => 'ქართული'],
['code' => 'kg', 'name' => 'Kongo', 'native_name' => 'Kikongo'], ['code' => 'kg', 'native_name' => 'Kikongo'],
[ [
'code' => 'ki', 'code' => 'ki',
'name' => 'Kikuyu, Gikuyu',
'native_name' => 'Gĩkũyũ', 'native_name' => 'Gĩkũyũ',
], ],
[ [
'code' => 'kj', 'code' => 'kj',
'name' => 'Kuanyama, Kwanyama',
'native_name' => 'Kuanyama', 'native_name' => 'Kuanyama',
], ],
['code' => 'kk', 'name' => 'Kazakh', 'native_name' => 'қазақ тілі'], ['code' => 'kk', 'native_name' => 'қазақ тілі'],
[ [
'code' => 'kl', 'code' => 'kl',
'name' => 'Kalaallisut, Greenlandic',
'native_name' => 'kalaallisut, kalaallit oqaasii', 'native_name' => 'kalaallisut, kalaallit oqaasii',
], ],
[ [
'code' => 'km', 'code' => 'km',
'name' => 'Central Khmer',
'native_name' => 'ខ្មែរ, ខេមរភាសា, ភាសាខ្មែរ', 'native_name' => 'ខ្មែរ, ខេមរភាសា, ភាសាខ្មែរ',
], ],
['code' => 'kn', 'name' => 'Kannada', 'native_name' => 'ಕನ್ನಡ'], ['code' => 'kn', 'native_name' => 'ಕನ್ನಡ'],
['code' => 'ko', 'name' => 'Korean', 'native_name' => '한국어'], ['code' => 'ko', 'native_name' => '한국어'],
['code' => 'kr', 'name' => 'Kanuri', 'native_name' => 'Kanuri'], ['code' => 'kr', 'native_name' => 'Kanuri'],
[ [
'code' => 'ks', 'code' => 'ks',
'name' => 'Kashmiri',
'native_name' => 'कश्मीरी, كشميري‎', 'native_name' => 'कश्मीरी, كشميري‎',
], ],
[ [
'code' => 'ku', 'code' => 'ku',
'name' => 'Kurdish',
'native_name' => 'Kurdî, کوردی‎', 'native_name' => 'Kurdî, کوردی‎',
], ],
['code' => 'kv', 'name' => 'Komi', 'native_name' => 'коми кыв'], ['code' => 'kv', 'native_name' => 'коми кыв'],
['code' => 'kw', 'name' => 'Cornish', 'native_name' => 'Kernewek'], ['code' => 'kw', 'native_name' => 'Kernewek'],
[ [
'code' => 'ky', 'code' => 'ky',
'name' => 'Kirghiz, Kyrgyz',
'native_name' => 'Кыргызча, Кыргыз тили', 'native_name' => 'Кыргызча, Кыргыз тили',
], ],
[ [
'code' => 'la', 'code' => 'la',
'name' => 'Latin',
'native_name' => 'latine, lingua latina', 'native_name' => 'latine, lingua latina',
], ],
[ [
'code' => 'lb', 'code' => 'lb',
'name' => 'Luxembourgish, Letzeburgesch',
'native_name' => 'Lëtzebuergesch', 'native_name' => 'Lëtzebuergesch',
], ],
['code' => 'lg', 'name' => 'Ganda', 'native_name' => 'Luganda'], ['code' => 'lg', 'native_name' => 'Luganda'],
[ [
'code' => 'li', 'code' => 'li',
'name' => 'Limburgan, Limburger, Limburgish',
'native_name' => 'Limburgs', 'native_name' => 'Limburgs',
], ],
['code' => 'ln', 'name' => 'Lingala', 'native_name' => 'Lingála'], ['code' => 'ln', 'native_name' => 'Lingála'],
['code' => 'lo', 'name' => 'Lao', 'native_name' => 'ພາສາລາວ'], ['code' => 'lo', 'native_name' => 'ພາສາລາວ'],
[ [
'code' => 'lt', 'code' => 'lt',
'name' => 'Lithuanian',
'native_name' => 'lietuvių kalba', 'native_name' => 'lietuvių kalba',
], ],
[ [
'code' => 'lu', 'code' => 'lu',
'name' => 'Luba-Katanga',
'native_name' => 'Kiluba', 'native_name' => 'Kiluba',
], ],
[ [
'code' => 'lv', 'code' => 'lv',
'name' => 'Latvian',
'native_name' => 'latviešu valoda', 'native_name' => 'latviešu valoda',
], ],
[ [
'code' => 'mg', 'code' => 'mg',
'name' => 'Malagasy',
'native_name' => 'fiteny malagasy', 'native_name' => 'fiteny malagasy',
], ],
[ [
'code' => 'mh', 'code' => 'mh',
'name' => 'Marshallese',
'native_name' => 'Kajin M̧ajeļ', 'native_name' => 'Kajin M̧ajeļ',
], ],
[ [
'code' => 'mi', 'code' => 'mi',
'name' => 'Maori',
'native_name' => 'te reo Māori', 'native_name' => 'te reo Māori',
], ],
[ [
'code' => 'mk', 'code' => 'mk',
'name' => 'Macedonian',
'native_name' => 'македонски јазик', 'native_name' => 'македонски јазик',
], ],
['code' => 'ml', 'name' => 'Malayalam', 'native_name' => 'മലയാളം'], ['code' => 'ml', 'native_name' => 'മലയാളം'],
[ [
'code' => 'mn', 'code' => 'mn',
'name' => 'Mongolian',
'native_name' => 'Монгол хэл', 'native_name' => 'Монгол хэл',
], ],
['code' => 'mr', 'name' => 'Marathi', 'native_name' => 'मराठी'], ['code' => 'mr', 'native_name' => 'मराठी'],
[ [
'code' => 'ms', 'code' => 'ms',
'name' => 'Malay',
'native_name' => 'Bahasa Melayu, بهاس ملايو‎', 'native_name' => 'Bahasa Melayu, بهاس ملايو‎',
], ],
['code' => 'mt', 'name' => 'Maltese', 'native_name' => 'Malti'], ['code' => 'mt', 'native_name' => 'Malti'],
['code' => 'my', 'name' => 'Burmese', 'native_name' => 'ဗမာစာ'], ['code' => 'my', 'native_name' => 'ဗမာစာ'],
[ [
'code' => 'na', 'code' => 'na',
'name' => 'Nauru',
'native_name' => 'Dorerin Naoero', 'native_name' => 'Dorerin Naoero',
], ],
[ [
'code' => 'nb', 'code' => 'nb',
'name' => 'Norwegian Bokmål',
'native_name' => 'Norsk Bokmål', 'native_name' => 'Norsk Bokmål',
], ],
[ [
'code' => 'nd', 'code' => 'nd',
'name' => 'North Ndebele',
'native_name' => 'isiNdebele', 'native_name' => 'isiNdebele',
], ],
['code' => 'ne', 'name' => 'Nepali', 'native_name' => 'नेपाली'], ['code' => 'ne', 'native_name' => 'नेपाली'],
['code' => 'ng', 'name' => 'Ndonga', 'native_name' => 'Owambo'], ['code' => 'ng', 'native_name' => 'Owambo'],
[ [
'code' => 'nl', 'code' => 'nl',
'name' => 'Dutch, Flemish',
'native_name' => 'Nederlands, Vlaams', 'native_name' => 'Nederlands, Vlaams',
], ],
[ [
'code' => 'nn', 'code' => 'nn',
'name' => 'Norwegian Nynorsk',
'native_name' => 'Norsk Nynorsk', 'native_name' => 'Norsk Nynorsk',
], ],
['code' => 'no', 'name' => 'Norwegian', 'native_name' => 'Norsk'], ['code' => 'no', 'native_name' => 'Norsk'],
[ [
'code' => 'nr', 'code' => 'nr',
'name' => 'South Ndebele',
'native_name' => 'isiNdebele', 'native_name' => 'isiNdebele',
], ],
[ [
'code' => 'nv', 'code' => 'nv',
'name' => 'Navajo, Navaho',
'native_name' => 'Diné bizaad', 'native_name' => 'Diné bizaad',
], ],
[ [
'code' => 'ny', 'code' => 'ny',
'name' => 'Chichewa, Chewa, Nyanja',
'native_name' => 'chiCheŵa, chinyanja', 'native_name' => 'chiCheŵa, chinyanja',
], ],
[ [
'code' => 'oc', 'code' => 'oc',
'name' => 'Occitan',
'native_name' => 'occitan, lenga dòc', 'native_name' => 'occitan, lenga dòc',
], ],
['code' => 'oj', 'name' => 'Ojibwa', 'native_name' => 'ᐊᓂᔑᓈᐯᒧᐎᓐ'], ['code' => 'oj', 'native_name' => 'ᐊᓂᔑᓈᐯᒧᐎᓐ'],
[ [
'code' => 'om', 'code' => 'om',
'name' => 'Oromo',
'native_name' => 'Afaan Oromoo', 'native_name' => 'Afaan Oromoo',
], ],
['code' => 'or', 'name' => 'Oriya', 'native_name' => 'ଓଡ଼ିଆ'], ['code' => 'or', 'native_name' => 'ଓଡ଼ିଆ'],
[ [
'code' => 'os', 'code' => 'os',
'name' => 'Ossetian, Ossetic',
'native_name' => 'ирон æвзаг', 'native_name' => 'ирон æвзаг',
], ],
[ [
'code' => 'pa', 'code' => 'pa',
'name' => 'Punjabi, Panjabi',
'native_name' => 'ਪੰਜਾਬੀ, پنجابی‎', 'native_name' => 'ਪੰਜਾਬੀ, پنجابی‎',
], ],
['code' => 'pi', 'name' => 'Pali', 'native_name' => 'पालि, पाळि'], ['code' => 'pi', 'native_name' => 'पालि, पाळि'],
[ [
'code' => 'pl', 'code' => 'pl',
'name' => 'Polish',
'native_name' => 'język polski, polszczyzna', 'native_name' => 'język polski, polszczyzna',
], ],
[ [
'code' => 'ps', 'code' => 'ps',
'name' => 'Pashto, Pushto',
'native_name' => 'پښتو', 'native_name' => 'پښتو',
], ],
[ [
'code' => 'pt', 'code' => 'pt',
'name' => 'Portuguese',
'native_name' => 'Português', 'native_name' => 'Português',
], ],
[ [
'code' => 'qu', 'code' => 'qu',
'name' => 'Quechua',
'native_name' => 'Runa Simi, Kichwa', 'native_name' => 'Runa Simi, Kichwa',
], ],
[ [
'code' => 'rm', 'code' => 'rm',
'name' => 'Romansh',
'native_name' => 'Rumantsch Grischun', 'native_name' => 'Rumantsch Grischun',
], ],
['code' => 'rn', 'name' => 'Rundi', 'native_name' => 'Ikirundi'], ['code' => 'rn', 'native_name' => 'Ikirundi'],
[ [
'code' => 'ro', 'code' => 'ro',
'name' => 'Romanian, Moldavian, Moldovan',
'native_name' => 'Română', 'native_name' => 'Română',
], ],
['code' => 'ru', 'name' => 'Russian', 'native_name' => 'русский'], ['code' => 'ru', 'native_name' => 'русский'],
[ [
'code' => 'rw', 'code' => 'rw',
'name' => 'Kinyarwanda',
'native_name' => 'Ikinyarwanda', 'native_name' => 'Ikinyarwanda',
], ],
[ [
'code' => 'sa', 'code' => 'sa',
'name' => 'Sanskrit',
'native_name' => 'संस्कृतम्', 'native_name' => 'संस्कृतम्',
], ],
['code' => 'sc', 'name' => 'Sardinian', 'native_name' => 'sardu'], ['code' => 'sc', 'native_name' => 'sardu'],
[ [
'code' => 'sd', 'code' => 'sd',
'name' => 'Sindhi',
'native_name' => 'सिन्धी, سنڌي، سندھی‎', 'native_name' => 'सिन्धी, سنڌي، سندھی‎',
], ],
[ [
'code' => 'se', 'code' => 'se',
'name' => 'Northern Sami',
'native_name' => 'Davvisámegiella', 'native_name' => 'Davvisámegiella',
], ],
[ [
'code' => 'sg', 'code' => 'sg',
'name' => 'Sango',
'native_name' => 'yângâ tî sängö', 'native_name' => 'yângâ tî sängö',
], ],
[ [
'code' => 'si', 'code' => 'si',
'name' => 'Sinhala, Sinhalese',
'native_name' => 'සිංහල', 'native_name' => 'සිංහල',
], ],
[ [
'code' => 'sk', 'code' => 'sk',
'name' => 'Slovak',
'native_name' => 'Slovenčina, Slovenský Jazyk', 'native_name' => 'Slovenčina, Slovenský Jazyk',
], ],
[ [
'code' => 'sl', 'code' => 'sl',
'name' => 'Slovenian',
'native_name' => 'Slovenski Jezik, Slovenščina', 'native_name' => 'Slovenski Jezik, Slovenščina',
], ],
[ [
'code' => 'sm', 'code' => 'sm',
'name' => 'Samoan',
'native_name' => 'gagana fa\'a Samoa', 'native_name' => 'gagana fa\'a Samoa',
], ],
['code' => 'sn', 'name' => 'Shona', 'native_name' => 'chiShona'], ['code' => 'sn', 'native_name' => 'chiShona'],
[ [
'code' => 'so', 'code' => 'so',
'name' => 'Somali',
'native_name' => 'Soomaaliga, af Soomaali', 'native_name' => 'Soomaaliga, af Soomaali',
], ],
['code' => 'sq', 'name' => 'Albanian', 'native_name' => 'Shqip'], ['code' => 'sq', 'native_name' => 'Shqip'],
[ [
'code' => 'sr', 'code' => 'sr',
'name' => 'Serbian',
'native_name' => 'српски језик', 'native_name' => 'српски језик',
], ],
['code' => 'ss', 'name' => 'Swati', 'native_name' => 'SiSwati'], ['code' => 'ss', 'native_name' => 'SiSwati'],
[ [
'code' => 'st', 'code' => 'st',
'name' => 'Southern Sotho',
'native_name' => 'Sesotho', 'native_name' => 'Sesotho',
], ],
[ [
'code' => 'su', 'code' => 'su',
'name' => 'Sundanese',
'native_name' => 'Basa Sunda', 'native_name' => 'Basa Sunda',
], ],
['code' => 'sv', 'name' => 'Swedish', 'native_name' => 'Svenska'], ['code' => 'sv', 'native_name' => 'Svenska'],
['code' => 'sw', 'name' => 'Swahili', 'native_name' => 'Kiswahili'], ['code' => 'sw', 'native_name' => 'Kiswahili'],
['code' => 'ta', 'name' => 'Tamil', 'native_name' => 'தமிழ்'], ['code' => 'ta', 'native_name' => 'தமிழ்'],
['code' => 'te', 'name' => 'Telugu', 'native_name' => 'తెలుగు'], ['code' => 'te', 'native_name' => 'తెలుగు'],
[ [
'code' => 'tg', 'code' => 'tg',
'name' => 'Tajik',
'native_name' => 'тоҷикӣ, toçikī, تاجیکی‎', 'native_name' => 'тоҷикӣ, toçikī, تاجیکی‎',
], ],
['code' => 'th', 'name' => 'Thai', 'native_name' => 'ไทย'], ['code' => 'th', 'native_name' => 'ไทย'],
['code' => 'ti', 'name' => 'Tigrinya', 'native_name' => 'ትግርኛ'], ['code' => 'ti', 'native_name' => 'ትግርኛ'],
[ [
'code' => 'tk', 'code' => 'tk',
'name' => 'Turkmen',
'native_name' => 'Türkmen, Түркмен', 'native_name' => 'Türkmen, Түркмен',
], ],
[ [
'code' => 'tl', 'code' => 'tl',
'name' => 'Tagalog',
'native_name' => 'Wikang Tagalog', 'native_name' => 'Wikang Tagalog',
], ],
['code' => 'tn', 'name' => 'Tswana', 'native_name' => 'Setswana'], ['code' => 'tn', 'native_name' => 'Setswana'],
[ [
'code' => 'to', 'code' => 'to',
'name' => 'Tonga (Tonga Islands)',
'native_name' => 'Faka Tonga', 'native_name' => 'Faka Tonga',
], ],
['code' => 'tr', 'name' => 'Turkish', 'native_name' => 'Türkçe'], ['code' => 'tr', 'native_name' => 'Türkçe'],
['code' => 'ts', 'name' => 'Tsonga', 'native_name' => 'Xitsonga'], ['code' => 'ts', 'native_name' => 'Xitsonga'],
[ [
'code' => 'tt', 'code' => 'tt',
'name' => 'Tatar',
'native_name' => 'татар теле, tatar tele', 'native_name' => 'татар теле, tatar tele',
], ],
['code' => 'tw', 'name' => 'Twi', 'native_name' => 'Twi'], ['code' => 'tw', 'native_name' => 'Twi'],
[ [
'code' => 'ty', 'code' => 'ty',
'name' => 'Tahitian',
'native_name' => 'Reo Tahiti', 'native_name' => 'Reo Tahiti',
], ],
[ [
'code' => 'ug', 'code' => 'ug',
'name' => 'Uighur, Uyghur',
'native_name' => 'ئۇيغۇرچە‎, Uyghurche', 'native_name' => 'ئۇيغۇرچە‎, Uyghurche',
], ],
[ [
'code' => 'uk', 'code' => 'uk',
'name' => 'Ukrainian',
'native_name' => 'Українська', 'native_name' => 'Українська',
], ],
['code' => 'ur', 'name' => 'Urdu', 'native_name' => 'اردو'], ['code' => 'ur', 'native_name' => 'اردو'],
[ [
'code' => 'uz', 'code' => 'uz',
'name' => 'Uzbek',
'native_name' => 'Oʻzbek, Ўзбек, أۇزبېك‎', 'native_name' => 'Oʻzbek, Ўзбек, أۇزبېك‎',
], ],
['code' => 've', 'name' => 'Venda', 'native_name' => 'Tshivenḓa'], ['code' => 've', 'native_name' => 'Tshivenḓa'],
[ [
'code' => 'vi', 'code' => 'vi',
'name' => 'Vietnamese',
'native_name' => 'Tiếng Việt', 'native_name' => 'Tiếng Việt',
], ],
['code' => 'vo', 'name' => 'Volapük', 'native_name' => 'Volapük'], ['code' => 'vo', 'native_name' => 'Volapük'],
['code' => 'wa', 'name' => 'Walloon', 'native_name' => 'Walon'], ['code' => 'wa', 'native_name' => 'Walon'],
['code' => 'wo', 'name' => 'Wolof', 'native_name' => 'Wollof'], ['code' => 'wo', 'native_name' => 'Wollof'],
['code' => 'xh', 'name' => 'Xhosa', 'native_name' => 'isiXhosa'], ['code' => 'xh', 'native_name' => 'isiXhosa'],
['code' => 'yi', 'name' => 'Yiddish', 'native_name' => 'ייִדיש'], ['code' => 'yi', 'native_name' => 'ייִדיש'],
['code' => 'yo', 'name' => 'Yoruba', 'native_name' => 'Yorùbá'], ['code' => 'yo', 'native_name' => 'Yorùbá'],
[ [
'code' => 'za', 'code' => 'za',
'name' => 'Zhuang, Chuang',
'native_name' => 'Saɯ cueŋƅ, Saw cuengh', 'native_name' => 'Saɯ cueŋƅ, Saw cuengh',
], ],
[ [
'code' => 'zh', 'code' => 'zh',
'name' => 'Chinese',
'native_name' => '中文 (Zhōngwén), 汉语, 漢語', 'native_name' => '中文 (Zhōngwén), 汉语, 漢語',
], ],
['code' => 'zu', 'name' => 'Zulu', 'native_name' => 'isiZulu'], ['code' => 'zu', 'native_name' => 'isiZulu'],
]; ];
$this->db $this->db

View File

@ -24,14 +24,12 @@ class PlatformSeeder extends Seeder
'home_url' => 'https://www.apple.com/itunes/podcasts/', 'home_url' => 'https://www.apple.com/itunes/podcasts/',
'submit_url' => 'submit_url' =>
'https://podcastsconnect.apple.com/my-podcasts/new-feed', 'https://podcastsconnect.apple.com/my-podcasts/new-feed',
'icon_filename' => 'apple-podcasts.svg',
], ],
[ [
'name' => 'blubrry', 'name' => 'blubrry',
'label' => 'Blubrry', 'label' => 'Blubrry',
'home_url' => 'https://www.blubrry.com/', 'home_url' => 'https://www.blubrry.com/',
'submit_url' => 'https://www.blubrry.com/addpodcast.php', 'submit_url' => 'https://www.blubrry.com/addpodcast.php',
'icon_filename' => 'blubrry.svg',
], ],
[ [
'name' => 'castbox', 'name' => 'castbox',
@ -39,21 +37,18 @@ class PlatformSeeder extends Seeder
'home_url' => 'https://castbox.fm/', 'home_url' => 'https://castbox.fm/',
'submit_url' => 'submit_url' =>
'https://helpcenter.castbox.fm/portal/kb/articles/submit-my-podcast', 'https://helpcenter.castbox.fm/portal/kb/articles/submit-my-podcast',
'icon_filename' => 'castbox.svg',
], ],
[ [
'name' => 'castro', 'name' => 'castro',
'label' => 'Castro', 'label' => 'Castro',
'home_url' => 'http://castro.fm/', 'home_url' => 'http://castro.fm/',
'submit_url' => null, 'submit_url' => null,
'icon_filename' => 'castro.svg',
], ],
[ [
'name' => 'deezer', 'name' => 'deezer',
'label' => 'Deezer', 'label' => 'Deezer',
'home_url' => 'https://www.deezer.com/', 'home_url' => 'https://www.deezer.com/',
'submit_url' => 'https://podcasters.deezer.com/submission', 'submit_url' => 'https://podcasters.deezer.com/submission',
'icon_filename' => 'deezer.svg',
], ],
[ [
'name' => 'google-podcasts', 'name' => 'google-podcasts',
@ -61,98 +56,84 @@ class PlatformSeeder extends Seeder
'home_url' => 'https://podcasts.google.com/about', 'home_url' => 'https://podcasts.google.com/about',
'submit_url' => 'submit_url' =>
'https://search.google.com/search-console/about', 'https://search.google.com/search-console/about',
'icon_filename' => 'google-podcasts.svg',
], ],
[ [
'name' => 'ivoox', 'name' => 'ivoox',
'label' => 'Ivoox', 'label' => 'Ivoox',
'home_url' => 'https://www.ivoox.com/', 'home_url' => 'https://www.ivoox.com/',
'submit_url' => null, 'submit_url' => null,
'icon_filename' => 'ivoox.svg',
], ],
[ [
'name' => 'listennotes', 'name' => 'listennotes',
'label' => 'ListenNotes', 'label' => 'ListenNotes',
'home_url' => 'https://www.listennotes.com/', 'home_url' => 'https://www.listennotes.com/',
'submit_url' => 'https://www.listennotes.com/submit/', 'submit_url' => 'https://www.listennotes.com/submit/',
'icon_filename' => 'listennotes.svg',
], ],
[ [
'name' => 'overcast', 'name' => 'overcast',
'label' => 'Overcast', 'label' => 'Overcast',
'home_url' => 'https://overcast.fm/', 'home_url' => 'https://overcast.fm/',
'submit_url' => 'https://overcast.fm/podcasterinfo', 'submit_url' => 'https://overcast.fm/podcasterinfo',
'icon_filename' => 'overcast.svg',
], ],
[ [
'name' => 'playerfm', 'name' => 'playerfm',
'label' => 'Player.Fm', 'label' => 'Player.Fm',
'home_url' => 'https://player.fm/', 'home_url' => 'https://player.fm/',
'submit_url' => 'https://player.fm/importer/feed', 'submit_url' => 'https://player.fm/importer/feed',
'icon_filename' => 'playerfm.svg',
], ],
[ [
'name' => 'pocketcasts', 'name' => 'pocketcasts',
'label' => 'Pocketcasts', 'label' => 'Pocketcasts',
'home_url' => 'https://www.pocketcasts.com/', 'home_url' => 'https://www.pocketcasts.com/',
'submit_url' => 'https://www.pocketcasts.com/submit/', 'submit_url' => 'https://www.pocketcasts.com/submit/',
'icon_filename' => 'pocketcasts.svg',
], ],
[ [
'name' => 'podbean', 'name' => 'podbean',
'label' => 'Podbean', 'label' => 'Podbean',
'home_url' => 'https://www.podbean.com/', 'home_url' => 'https://www.podbean.com/',
'submit_url' => 'https://www.podbean.com/site/submitPodcast', 'submit_url' => 'https://www.podbean.com/site/submitPodcast',
'icon_filename' => 'podbean.svg',
], ],
[ [
'name' => 'podcast-addict', 'name' => 'podcast-addict',
'label' => 'Podcast Addict', 'label' => 'Podcast Addict',
'home_url' => 'https://podcastaddict.com/', 'home_url' => 'https://podcastaddict.com/',
'submit_url' => 'https://podcastaddict.com/submit', 'submit_url' => 'https://podcastaddict.com/submit',
'icon_filename' => 'podcast-addict.svg',
], ],
[ [
'name' => 'podchaser', 'name' => 'podchaser',
'label' => 'Podchaser', 'label' => 'Podchaser',
'home_url' => 'https://www.podchaser.com/', 'home_url' => 'https://www.podchaser.com/',
'submit_url' => 'https://www.podchaser.com/creators/edit', 'submit_url' => 'https://www.podchaser.com/creators/edit',
'icon_filename' => 'podchaser.svg',
], ],
[ [
'name' => 'podtail', 'name' => 'podtail',
'label' => 'Podtail', 'label' => 'Podtail',
'home_url' => 'https://podtail.com/', 'home_url' => 'https://podtail.com/',
'submit_url' => 'https://podtail.com/about/faq/', 'submit_url' => 'https://podtail.com/about/faq/',
'icon_filename' => 'podtail.svg',
], ],
[ [
'name' => 'radiopublic', 'name' => 'radiopublic',
'label' => 'Radiopublic', 'label' => 'Radiopublic',
'home_url' => 'https://radiopublic.com/', 'home_url' => 'https://radiopublic.com/',
'submit_url' => 'https://podcasters.radiopublic.com/signup', 'submit_url' => 'https://podcasters.radiopublic.com/signup',
'icon_filename' => 'radiopublic.svg',
], ],
[ [
'name' => 'spotify', 'name' => 'spotify',
'label' => 'Spotify', 'label' => 'Spotify',
'home_url' => 'https://www.spotify.com/', 'home_url' => 'https://www.spotify.com/',
'submit_url' => 'https://podcasters.spotify.com/submit', 'submit_url' => 'https://podcasters.spotify.com/submit',
'icon_filename' => 'spotify.svg',
], ],
[ [
'name' => 'spreaker', 'name' => 'spreaker',
'label' => 'Spreaker', 'label' => 'Spreaker',
'home_url' => 'https://www.spreaker.com/', 'home_url' => 'https://www.spreaker.com/',
'submit_url' => 'https://www.spreaker.com/cms/shows/rss-import', 'submit_url' => 'https://www.spreaker.com/cms/shows/rss-import',
'icon_filename' => 'spreaker.svg',
], ],
[ [
'name' => 'stitcher', 'name' => 'stitcher',
'label' => 'Stitcher', 'label' => 'Stitcher',
'home_url' => 'https://www.stitcher.com/', 'home_url' => 'https://www.stitcher.com/',
'submit_url' => 'https://www.stitcher.com/content-providers', 'submit_url' => 'https://www.stitcher.com/content-providers',
'icon_filename' => 'stitcher.svg',
], ],
[ [
'name' => 'tunein', 'name' => 'tunein',
@ -160,7 +141,6 @@ class PlatformSeeder extends Seeder
'home_url' => 'https://tunein.com/', 'home_url' => 'https://tunein.com/',
'submit_url' => 'submit_url' =>
'https://help.tunein.com/contact/add-podcast-S19TR3Sdf', 'https://help.tunein.com/contact/add-podcast-S19TR3Sdf',
'icon_filename' => 'tunein.svg',
], ],
]; ];
$this->db $this->db

View File

@ -17,7 +17,9 @@ class AnalyticsPodcasts extends Entity
protected $casts = [ protected $casts = [
'podcast_id' => 'integer', 'podcast_id' => 'integer',
'date' => 'datetime', 'date' => 'datetime',
'hits' => 'integer', 'duration' => 'integer',
'bandwidth' => 'integer',
'unique_listeners' => 'integer', 'unique_listeners' => 'integer',
'hits' => 'integer',
]; ];
} }

View File

@ -0,0 +1,23 @@
<?php
/**
* Class AnalyticsPodcastsByHour
* Entity for AnalyticsPodcastsByHour
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Entities;
use CodeIgniter\Entity;
class AnalyticsPodcastsByHour extends Entity
{
protected $casts = [
'podcast_id' => 'integer',
'date' => 'datetime',
'hour' => 'integer',
'hits' => 'integer',
];
}

View File

@ -19,7 +19,7 @@ class AnalyticsPodcastsByPlayer extends Entity
'app' => '?string', 'app' => '?string',
'device' => '?string', 'device' => '?string',
'os' => '?string', 'os' => '?string',
'bot' => 'boolean', 'is_bot' => 'boolean',
'date' => 'datetime', 'date' => 'datetime',
'hits' => 'integer', 'hits' => 'integer',
]; ];

View File

@ -16,7 +16,7 @@ class AnalyticsWebsiteByEntryPage extends Entity
{ {
protected $casts = [ protected $casts = [
'podcast_id' => 'integer', 'podcast_id' => 'integer',
'entry_page' => '?string', 'entry_page_url' => 'string',
'date' => 'datetime', 'date' => 'datetime',
'hits' => 'integer', 'hits' => 'integer',
]; ];

View File

@ -16,7 +16,7 @@ class AnalyticsWebsiteByReferer extends Entity
{ {
protected $casts = [ protected $casts = [
'podcast_id' => 'integer', 'podcast_id' => 'integer',
'referer' => 'string', 'referer_url' => 'string',
'date' => 'datetime', 'date' => 'datetime',
'hits' => 'integer', 'hits' => 'integer',
]; ];

View File

@ -51,9 +51,11 @@ class Episode extends Entity
protected $enclosure_web_url; protected $enclosure_web_url;
/** /**
* Holds text only description, striped of any markdown or html special characters
*
* @var string * @var string
*/ */
protected $description_html; protected $description;
/** /**
* @var boolean * @var boolean
@ -76,13 +78,14 @@ class Episode extends Entity
'enclosure_mimetype' => 'string', 'enclosure_mimetype' => 'string',
'enclosure_filesize' => 'integer', 'enclosure_filesize' => 'integer',
'enclosure_headersize' => 'integer', 'enclosure_headersize' => 'integer',
'description' => 'string', 'description_markdown' => 'string',
'description_html' => 'string',
'image_uri' => '?string', 'image_uri' => '?string',
'parental_advisory' => '?string', 'parental_advisory' => '?string',
'number' => '?integer', 'number' => '?integer',
'season_number' => '?integer', 'season_number' => '?integer',
'type' => 'string', 'type' => 'string',
'block' => 'boolean', 'is_blocked' => 'boolean',
'created_by' => 'integer', 'created_by' => 'integer',
'updated_by' => 'integer', 'updated_by' => 'integer',
]; ];
@ -194,6 +197,7 @@ class Episode extends Entity
60 60
), ),
$this->attributes['enclosure_filesize'], $this->attributes['enclosure_filesize'],
$this->attributes['enclosure_duration'],
$this->attributes['enclosure_uri'] $this->attributes['enclosure_uri']
) )
); );
@ -229,23 +233,49 @@ class Episode extends Entity
); );
} }
public function getDescriptionHtml() public function setDescriptionMarkdown(string $descriptionMarkdown)
{ {
$converter = new CommonMarkConverter([ $converter = new CommonMarkConverter([
'html_input' => 'strip', 'html_input' => 'strip',
'allow_unsafe_links' => false, 'allow_unsafe_links' => false,
]); ]);
$this->attributes['description_markdown'] = $descriptionMarkdown;
$this->attributes['description_html'] = $converter->convertToHtml(
$descriptionMarkdown
);
return $this;
}
public function getDescriptionHtml()
{
if ( if (
$descriptionFooter = $this->getPodcast()->episode_description_footer $descriptionFooter = $this->getPodcast()
->episode_description_footer_html
) { ) {
return $converter->convertToHtml($this->attributes['description']) . return $this->attributes['description_html'] .
'<footer>' . '<footer>' .
$converter->convertToHtml($descriptionFooter) . $descriptionFooter .
'</footer>'; '</footer>';
} }
return $converter->convertToHtml($this->attributes['description']); return $this->attributes['description_html'];
}
public function getDescription()
{
if ($this->description) {
return $this->description;
}
return trim(
preg_replace(
'/\s+/',
' ',
strip_tags($this->attributes['description_html'])
)
);
} }
public function setCreatedBy(\App\Entities\User $user) public function setCreatedBy(\App\Entities\User $user)

View File

@ -19,6 +19,6 @@ class Platform extends Entity
'home_url' => 'string', 'home_url' => 'string',
'submit_url' => '?string', 'submit_url' => '?string',
'link_url' => '?string', 'link_url' => '?string',
'visible' => '?boolean', 'is_visible' => '?boolean',
]; ];
} }

View File

@ -52,34 +52,38 @@ class Podcast extends Entity
*/ */
protected $contributors; protected $contributors;
/**
* @var string
*/
protected $description_html;
/** /**
* @var \App\Entities\Platform * @var \App\Entities\Platform
*/ */
protected $platforms; protected $platforms;
/**
* Holds text only description, striped of any markdown or html special characters
*
* @var string
*/
protected $description;
protected $casts = [ protected $casts = [
'id' => 'integer', 'id' => 'integer',
'title' => 'string', 'title' => 'string',
'name' => 'string', 'name' => 'string',
'description' => 'string', 'description_markdown' => 'string',
'description_html' => 'string',
'image_uri' => 'string', 'image_uri' => 'string',
'language' => 'string', 'language_code' => 'string',
'category_id' => 'integer', 'category_id' => 'integer',
'parental_advisory' => '?string', 'parental_advisory' => '?string',
'publisher' => '?string', 'publisher' => '?string',
'owner_name' => '?string', 'owner_name' => 'string',
'owner_email' => '?string', 'owner_email' => 'string',
'type' => 'string', 'type' => 'string',
'copyright' => '?string', 'copyright' => '?string',
'episode_description_footer' => '?string', 'episode_description_footer_markdown' => '?string',
'block' => 'boolean', 'episode_description_footer_html' => '?string',
'complete' => 'boolean', 'is_blocked' => 'boolean',
'lock' => 'boolean', 'is_completed' => 'boolean',
'is_locked' => 'boolean',
'imported_feed_url' => '?string', 'imported_feed_url' => '?string',
'new_feed_url' => '?string', 'new_feed_url' => '?string',
'created_by' => 'integer', 'created_by' => 'integer',
@ -191,14 +195,54 @@ class Podcast extends Entity
return $this->contributors; return $this->contributors;
} }
public function getDescriptionHtml() public function setDescriptionMarkdown(string $descriptionMarkdown)
{ {
$converter = new CommonMarkConverter([ $converter = new CommonMarkConverter([
'html_input' => 'strip', 'html_input' => 'strip',
'allow_unsafe_links' => false, 'allow_unsafe_links' => false,
]); ]);
return $converter->convertToHtml($this->attributes['description']); $this->attributes['description_markdown'] = $descriptionMarkdown;
$this->attributes['description_html'] = $converter->convertToHtml(
$descriptionMarkdown
);
return $this;
}
public function setEpisodeDescriptionFooterMarkdown(
string $episodeDescriptionFooterMarkdown = null
) {
if ($episodeDescriptionFooterMarkdown) {
$converter = new CommonMarkConverter([
'html_input' => 'strip',
'allow_unsafe_links' => false,
]);
$this->attributes[
'episode_description_footer_markdown'
] = $episodeDescriptionFooterMarkdown;
$this->attributes[
'episode_description_footer_html'
] = $converter->convertToHtml($episodeDescriptionFooterMarkdown);
}
return $this;
}
public function getDescription()
{
if ($this->description) {
return $this->description;
}
return trim(
preg_replace(
'/\s+/',
' ',
strip_tags($this->attributes['description_html'])
)
);
} }
public function setCreatedBy(\App\Entities\User $user) public function setCreatedBy(\App\Entities\User $user)
@ -229,7 +273,7 @@ class Podcast extends Entity
} }
if (empty($this->platforms)) { if (empty($this->platforms)) {
$this->platforms = (new PlatformModel())->getPodcastPlatformLinks( $this->platforms = (new PlatformModel())->getPodcastPlatforms(
$this->id $this->id
); );
} }

View File

@ -244,6 +244,7 @@ function podcast_hit(
$episodeId, $episodeId,
$bytesThreshold, $bytesThreshold,
$fileSize, $fileSize,
$duration,
$serviceName $serviceName
) { ) {
$session = \Config\Services::session(); $session = \Config\Services::session();
@ -335,7 +336,9 @@ function podcast_hit(
// We save the download count for this user until midnight: // We save the download count for this user until midnight:
cache()->save($listenerHashId, $downloadsByUser, $midnightTTL); cache()->save($listenerHashId, $downloadsByUser, $midnightTTL);
$db->query("CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?,?);", [ $db->query(
"CALL $procedureName(?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
[
$podcastId, $podcastId,
$episodeId, $episodeId,
$session->get('location')['countryCode'], $session->get('location')['countryCode'],
@ -347,8 +350,11 @@ function podcast_hit(
$session->get('player')['device'], $session->get('player')['device'],
$session->get('player')['os'], $session->get('player')['os'],
$session->get('player')['bot'], $session->get('player')['bot'],
$fileSize,
$duration,
$newListener, $newListener,
]); ]
);
} }
} }
} catch (\Exception $e) { } catch (\Exception $e) {

View File

@ -60,13 +60,17 @@ function write_enclosure_tags($episode)
// populate data array // populate data array
$TagData = [ $TagData = [
'title' => [$episode->title], 'title' => [$episode->title],
'artist' => [$episode->podcast->author], 'artist' => [
empty($episode->podcast->publisher)
? $episode->podcast->owner_name
: $episode->podcast->publisher,
],
'album' => [$episode->podcast->title], 'album' => [$episode->podcast->title],
'year' => [ 'year' => [
$episode->published_at ? $episode->published_at->format('Y') : '', $episode->published_at ? $episode->published_at->format('Y') : '',
], ],
'genre' => ['Podcast'], 'genre' => ['Podcast'],
'comment' => [$episode->description], 'comment' => [$episode->description_html],
'track_number' => [strval($episode->number)], 'track_number' => [strval($episode->number)],
'copyright_message' => [$episode->podcast->copyright], 'copyright_message' => [$episode->podcast->copyright],
'publisher' => [ 'publisher' => [
@ -81,7 +85,7 @@ function write_enclosure_tags($episode)
// 'podcast' => [], // 'podcast' => [],
// 'podcast_identifier' => [$episode_media_url], // 'podcast_identifier' => [$episode_media_url],
// 'podcast_feed' => [$podcast_feed_url], // 'podcast_feed' => [$podcast_feed_url],
// 'podcast_description' => [$podcast->description], // 'podcast_description' => [$podcast->description_markdown],
]; ];
$TagData['attached_picture'][] = [ $TagData['attached_picture'][] = [

View File

@ -63,9 +63,13 @@ function get_rss_feed($podcast, $serviceName = '')
$channel->addChildWithCDATA('description', $podcast->description_html); $channel->addChildWithCDATA('description', $podcast->description_html);
$itunes_image = $channel->addChild('image', null, $itunes_namespace); $itunes_image = $channel->addChild('image', null, $itunes_namespace);
$itunes_image->addAttribute('href', $podcast->image->original_url); $itunes_image->addAttribute('href', $podcast->image->original_url);
$channel->addChild('language', $podcast->language); $channel->addChild('language', $podcast->language_code);
$channel $channel
->addChild('locked', $podcast->lock ? 'yes' : 'no', $podcast_namespace) ->addChild(
'locked',
$podcast->is_locked ? 'yes' : 'no',
$podcast_namespace
)
->addAttribute('owner', $podcast->owner_email); ->addAttribute('owner', $podcast->owner_email);
// set main category first, then other categories as apple // set main category first, then other categories as apple
add_category_tag($channel, $podcast->category); add_category_tag($channel, $podcast->category);
@ -89,8 +93,9 @@ function get_rss_feed($podcast, $serviceName = '')
$channel->addChild('type', $podcast->type, $itunes_namespace); $channel->addChild('type', $podcast->type, $itunes_namespace);
$podcast->copyright && $channel->addChild('copyright', $podcast->copyright); $podcast->copyright && $channel->addChild('copyright', $podcast->copyright);
$podcast->block && $channel->addChild('block', 'Yes', $itunes_namespace); $podcast->is_blocked &&
$podcast->complete && $channel->addChild('block', 'Yes', $itunes_namespace);
$podcast->is_completed &&
$channel->addChild('complete', 'Yes', $itunes_namespace); $channel->addChild('complete', 'Yes', $itunes_namespace);
$image = $channel->addChild('image'); $image = $channel->addChild('image');
@ -146,7 +151,8 @@ function get_rss_feed($podcast, $serviceName = '')
); );
$item->addChild('episodeType', $episode->type, $itunes_namespace); $item->addChild('episodeType', $episode->type, $itunes_namespace);
$episode->block && $item->addChild('block', 'Yes', $itunes_namespace); $episode->is_blocked &&
$item->addChild('block', 'Yes', $itunes_namespace);
} }
return $rss->asXML(); return $rss->asXML();

View File

@ -57,7 +57,9 @@ function svg($name, $class = null)
function platform_icon($name, $class = null) function platform_icon($name, $class = null)
{ {
try { try {
$svg_contents = file_get_contents('assets/images/platforms/' . $name); $svg_contents = file_get_contents(
'assets/images/platforms/' . $name . '.svg'
);
} catch (\Exception $e) { } catch (\Exception $e) {
$svg_contents = file_get_contents( $svg_contents = file_get_contents(
'assets/images/platforms/_default.svg' 'assets/images/platforms/_default.svg'

View File

@ -65,7 +65,7 @@ return [
'status_section_subtitle' => 'Dead or alive?', 'status_section_subtitle' => 'Dead or alive?',
'block' => 'Podcast should be hidden from all platforms', 'block' => 'Podcast should be hidden from all platforms',
'complete' => 'Podcast will not be having new episodes', 'complete' => 'Podcast will not be having new episodes',
'lock' => 'Podcast is locked for export', 'lock' => 'Prevent podcast from being copied',
'lock_hint' => 'lock_hint' =>
'The purpose is to tell other podcast platforms whether they are allowed to import this feed. A value of yes means that any attempt to import this feed into a new platform should be rejected.', 'The purpose is to tell other podcast platforms whether they are allowed to import this feed. A value of yes means that any attempt to import this feed into a new platform should be rejected.',
'submit_create' => 'Create podcast', 'submit_create' => 'Create podcast',

View File

@ -66,7 +66,7 @@ return [
'status_section_subtitle' => 'Vivant ou mort?', 'status_section_subtitle' => 'Vivant ou mort?',
'block' => 'Le podcast doit être masqué sur toutes les plateformes', 'block' => 'Le podcast doit être masqué sur toutes les plateformes',
'complete' => 'Le podcast naura plus de nouveaux épisodes.', 'complete' => 'Le podcast naura plus de nouveaux épisodes.',
'lock' => 'Le podcast est fermé à lexport', 'lock' => 'Empêcher la copie du podcast',
'lock_hint' => 'lock_hint' =>
'Le but est dindiquer aux autres plates-formes de podcast si elles sont autorisées à importer ce flux. La valeur «oui» signifie que toute tentative dimportation de ce flux dans une nouvelle plateforme doit être rejetée.', 'Le but est dindiquer aux autres plates-formes de podcast si elles sont autorisées à importer ce flux. La valeur «oui» signifie que toute tentative dimportation de ce flux dans une nouvelle plateforme doit être rejetée.',
'submit_create' => 'Créer le podcast', 'submit_create' => 'Créer le podcast',

View File

@ -142,71 +142,4 @@ class AnalyticsPodcastByEpisodeModel extends Model
} }
return $found; return $found;
} }
/**
* Gets listening-time data for a podcast
*
* @param int $podcastId
*
* @return array
*/
public function getDataTotalListeningTimeByDay(int $podcastId): array
{
if (
!($found = cache(
"{$podcastId}_analytics_podcast_listening_time_by_day"
))
) {
$found = $this->select('date as labels')
->selectSum('(enclosure_duration * hits)', 'values')
->join('episodes', 'id = episode_id', 'inner')
->where([
$this->table . '.podcast_id' => $podcastId,
'date >' => date('Y-m-d', strtotime('-60 days')),
])
->groupBy('labels')
->orderBy('labels', 'ASC')
->findAll();
cache()->save(
"{$podcastId}_analytics_podcast_listening_time_by_day",
$found,
600
);
}
return $found;
}
/**
* Gets listening-time data for a podcast
*
* @param int $podcastId
*
* @return array
*/
public function getDataTotalListeningTimeByMonth(int $podcastId): array
{
if (
!($found = cache(
"{$podcastId}_analytics_podcast_listening_time_by_month"
))
) {
$found = $this->select('DATE_FORMAT(`date`,"%Y-%m-01") as `labels`')
->selectSum('(enclosure_duration * hits)', 'values')
->join('episodes', 'id = episode_id', 'inner')
->where([
$this->table . '.podcast_id' => $podcastId,
])
->groupBy('`labels`')
->orderBy('`labels`', 'ASC')
->findAll();
cache()->save(
"{$podcastId}_analytics_podcast_listening_time_by_month",
$found,
600
);
}
return $found;
}
} }

View File

@ -0,0 +1,25 @@
<?php
/**
* Class AnalyticsPodcastByHour
* Model for analytics_podcasts_by_hour table in database
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Models;
use CodeIgniter\Model;
class AnalyticsPodcastByHourModel extends Model
{
protected $table = 'analytics_podcasts_by_hour';
protected $allowedFields = [];
protected $returnType = \App\Entities\AnalyticsPodcastsByHour::class;
protected $useSoftDeletes = false;
protected $useTimestamps = false;
}

View File

@ -42,7 +42,7 @@ class AnalyticsPodcastByPlayerModel extends Model
->where([ ->where([
'`podcast_id`' => $podcastId, '`podcast_id`' => $podcastId,
'`service` !=' => '', '`service` !=' => '',
'`bot`' => 0, '`is_bot`' => 0,
'`date` >' => date('Y-m-d', strtotime('-1 week')), '`date` >' => date('Y-m-d', strtotime('-1 week')),
]) ])
->groupBy('`labels`') ->groupBy('`labels`')
@ -77,7 +77,7 @@ class AnalyticsPodcastByPlayerModel extends Model
->where([ ->where([
'`podcast_id`' => $podcastId, '`podcast_id`' => $podcastId,
'`app` !=' => '', '`app` !=' => '',
'`bot`' => 0, '`is_bot`' => 0,
'`date` >' => date('Y-m-d', strtotime('-1 week')), '`date` >' => date('Y-m-d', strtotime('-1 week')),
]) ])
->groupBy('`labels`') ->groupBy('`labels`')
@ -112,7 +112,7 @@ class AnalyticsPodcastByPlayerModel extends Model
->where([ ->where([
'`podcast_id`' => $podcastId, '`podcast_id`' => $podcastId,
'`app` !=' => '', '`app` !=' => '',
'`bot`' => 0, '`is_bot`' => 0,
'`date` >' => date('Y-m-d', strtotime('-1 year')), '`date` >' => date('Y-m-d', strtotime('-1 year')),
]) ])
->groupBy('`labels`') ->groupBy('`labels`')
@ -148,7 +148,7 @@ class AnalyticsPodcastByPlayerModel extends Model
'`podcast_id`' => $podcastId, '`podcast_id`' => $podcastId,
'`app` !=' => '', '`app` !=' => '',
'`os` !=' => '', '`os` !=' => '',
'`bot`' => 0, '`is_bot`' => 0,
'`date` >' => date('Y-m-d', strtotime('-1 week')), '`date` >' => date('Y-m-d', strtotime('-1 week')),
]) ])
->groupBy('`labels`') ->groupBy('`labels`')
@ -183,7 +183,7 @@ class AnalyticsPodcastByPlayerModel extends Model
->where([ ->where([
'`podcast_id`' => $podcastId, '`podcast_id`' => $podcastId,
'`device` !=' => '', '`device` !=' => '',
'`bot`' => 0, '`is_bot`' => 0,
'`date` >' => date('Y-m-d', strtotime('-1 week')), '`date` >' => date('Y-m-d', strtotime('-1 week')),
]) ])
->groupBy('`labels`') ->groupBy('`labels`')
@ -215,7 +215,7 @@ class AnalyticsPodcastByPlayerModel extends Model
->selectSum('`hits`', '`values`') ->selectSum('`hits`', '`values`')
->where([ ->where([
'`podcast_id`' => $podcastId, '`podcast_id`' => $podcastId,
'`bot`' => 1, '`is_bot`' => 1,
'`date` >' => date('Y-m-d', strtotime('-1 year')), '`date` >' => date('Y-m-d', strtotime('-1 year')),
]) ])
->groupBy('`labels`') ->groupBy('`labels`')

View File

@ -138,4 +138,69 @@ class AnalyticsPodcastModel extends Model
} }
return $found; return $found;
} }
/**
* Gets listening-time data for a podcast
*
* @param int $podcastId
*
* @return array
*/
public function getDataTotalListeningTimeByDay(int $podcastId): array
{
if (
!($found = cache(
"{$podcastId}_analytics_podcast_listening_time_by_day"
))
) {
$found = $this->select('date as labels')
->selectSum('duration', 'values')
->where([
$this->table . '.podcast_id' => $podcastId,
'date >' => date('Y-m-d', strtotime('-60 days')),
])
->groupBy('labels')
->orderBy('labels', 'ASC')
->findAll();
cache()->save(
"{$podcastId}_analytics_podcast_listening_time_by_day",
$found,
600
);
}
return $found;
}
/**
* Gets listening-time data for a podcast
*
* @param int $podcastId
*
* @return array
*/
public function getDataTotalListeningTimeByMonth(int $podcastId): array
{
if (
!($found = cache(
"{$podcastId}_analytics_podcast_listening_time_by_month"
))
) {
$found = $this->select('DATE_FORMAT(`date`,"%Y-%m-01") as `labels`')
->selectSum('duration', 'values')
->where([
$this->table . '.podcast_id' => $podcastId,
])
->groupBy('`labels`')
->orderBy('`labels`', 'ASC')
->findAll();
cache()->save(
"{$podcastId}_analytics_podcast_listening_time_by_month",
$found,
600
);
}
return $found;
}
} }

View File

@ -34,7 +34,7 @@ class AnalyticsWebsiteByEntryPageModel extends Model
{ {
if (!($found = cache("{$podcastId}_analytics_website_by_entry_page"))) { if (!($found = cache("{$podcastId}_analytics_website_by_entry_page"))) {
$found = $this->select( $found = $this->select(
'IF(`entry_page`=\'/\',\'/\',SUBSTRING_INDEX(`entry_page`,\'/\',-1)) as `labels`' 'IF(`entry_page_url`=\'/\',\'/\',SUBSTRING_INDEX(`entry_page_url`,\'/\',-1)) as `labels`'
) )
->selectSum('`hits`', '`values`') ->selectSum('`hits`', '`values`')
->where([ ->where([

View File

@ -33,7 +33,7 @@ class AnalyticsWebsiteByRefererModel extends Model
public function getData(int $podcastId): array public function getData(int $podcastId): array
{ {
if (!($found = cache("{$podcastId}_analytics_website_by_referer"))) { if (!($found = cache("{$podcastId}_analytics_website_by_referer"))) {
$found = $this->select('`referer` as `labels`') $found = $this->select('`referer_url` as `labels`')
->selectSum('`hits`', '`values`') ->selectSum('`hits`', '`values`')
->where([ ->where([
'`podcast_id`' => $podcastId, '`podcast_id`' => $podcastId,

View File

@ -25,13 +25,14 @@ class EpisodeModel extends Model
'enclosure_mimetype', 'enclosure_mimetype',
'enclosure_filesize', 'enclosure_filesize',
'enclosure_headersize', 'enclosure_headersize',
'description', 'description_markdown',
'description_html',
'image_uri', 'image_uri',
'parental_advisory', 'parental_advisory',
'number', 'number',
'season_number', 'season_number',
'type', 'type',
'block', 'is_blocked',
'published_at', 'published_at',
'created_by', 'created_by',
'updated_by', 'updated_by',
@ -47,7 +48,7 @@ class EpisodeModel extends Model
'title' => 'required', 'title' => 'required',
'slug' => 'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]', 'slug' => 'required|regex_match[/^[a-zA-Z0-9\-]{1,191}$/]',
'enclosure_uri' => 'required', 'enclosure_uri' => 'required',
'description' => 'required', 'description_markdown' => 'required',
'number' => 'is_natural_no_zero|permit_empty', 'number' => 'is_natural_no_zero|permit_empty',
'season_number' => 'is_natural_no_zero|permit_empty', 'season_number' => 'is_natural_no_zero|permit_empty',
'type' => 'required', 'type' => 'required',

View File

@ -18,18 +18,7 @@ class PlatformModel extends Model
protected $table = 'platforms'; protected $table = 'platforms';
protected $primaryKey = 'id'; protected $primaryKey = 'id';
protected $allowedFields = [ protected $allowedFields = ['name', 'label', 'home_url', 'submit_url'];
'name',
'home_url',
'submit_url',
'iosapp_url',
'androidapp_url',
'comment',
'display_by_default',
'ios_deeplink',
'android_deeplink',
'logo_file_name',
];
protected $returnType = \App\Entities\Platform::class; protected $returnType = \App\Entities\Platform::class;
protected $useSoftDeletes = false; protected $useSoftDeletes = false;
@ -40,7 +29,7 @@ class PlatformModel extends Model
{ {
if (!($found = cache("podcast{$podcastId}_platforms"))) { if (!($found = cache("podcast{$podcastId}_platforms"))) {
$found = $this->select( $found = $this->select(
'platforms.*, platform_links.link_url, platform_links.visible' 'platforms.*, platform_links.link_url, platform_links.is_visible'
) )
->join( ->join(
'platform_links', 'platform_links',
@ -55,11 +44,11 @@ class PlatformModel extends Model
return $found; return $found;
} }
public function getPodcastPlatformLinks($podcastId) public function getPodcastPlatforms($podcastId)
{ {
if (!($found = cache("podcast{$podcastId}_platformLinks"))) { if (!($found = cache("podcast{$podcastId}_podcastPlatforms"))) {
$found = $this->select( $found = $this->select(
'platforms.*, platform_links.link_url, platform_links.visible' 'platforms.*, platform_links.link_url, platform_links.is_visible'
) )
->join( ->join(
'platform_links', 'platform_links',
@ -68,13 +57,17 @@ class PlatformModel extends Model
->where('platform_links.podcast_id', $podcastId) ->where('platform_links.podcast_id', $podcastId)
->findAll(); ->findAll();
cache()->save("podcast{$podcastId}_platformLinks", $found, DECADE); cache()->save(
"podcast{$podcastId}_podcastPlatforms",
$found,
DECADE
);
} }
return $found; return $found;
} }
public function savePlatformLinks($podcastId, $platformLinksData) public function savePodcastPlatforms($podcastId, $platformLinksData)
{ {
$this->clearCache($podcastId); $this->clearCache($podcastId);
@ -83,22 +76,18 @@ class PlatformModel extends Model
->table('platform_links') ->table('platform_links')
->delete(['podcast_id' => $podcastId]); ->delete(['podcast_id' => $podcastId]);
// Set platformLinks // Set podcastPlatforms
return $this->db return $this->db
->table('platform_links') ->table('platform_links')
->insertBatch($platformLinksData); ->insertBatch($platformLinksData);
} }
public function getPlatformId($platform) public function getPlatformId(string $platformName)
{ {
if (is_numeric($platform)) { $p = $this->where('name', $platformName)->first();
return (int) $platform;
}
$p = $this->where('name', $platform)->first();
if (!$p) { if (!$p) {
$this->error = lang('Platform.platformNotFound', [$platform]); $this->error = lang('Platform.platformNotFound', [$platformName]);
return false; return false;
} }
@ -106,7 +95,7 @@ class PlatformModel extends Model
return (int) $p->id; return (int) $p->id;
} }
public function removePlatformLink($podcastId, $platformId) public function removePodcastPlatform($podcastId, $platformId)
{ {
$this->clearCache($podcastId); $this->clearCache($podcastId);
@ -119,7 +108,7 @@ class PlatformModel extends Model
public function clearCache($podcastId) public function clearCache($podcastId)
{ {
cache()->delete("podcast{$podcastId}_platforms"); cache()->delete("podcast{$podcastId}_platforms");
cache()->delete("podcast{$podcastId}_platformLinks"); cache()->delete("podcast{$podcastId}_podcastPlatforms");
// delete localized podcast page cache // delete localized podcast page cache
$episodeModel = new EpisodeModel(); $episodeModel = new EpisodeModel();

View File

@ -19,10 +19,12 @@ class PodcastModel extends Model
'id', 'id',
'title', 'title',
'name', 'name',
'description', 'description_markdown',
'episode_description_footer', 'description_html',
'episode_description_footer_markdown',
'episode_description_footer_html',
'image_uri', 'image_uri',
'language', 'language_code',
'category_id', 'category_id',
'parental_advisory', 'parental_advisory',
'owner_name', 'owner_name',
@ -30,13 +32,13 @@ class PodcastModel extends Model
'publisher', 'publisher',
'type', 'type',
'copyright', 'copyright',
'block',
'complete',
'lock',
'created_by',
'updated_by',
'imported_feed_url', 'imported_feed_url',
'new_feed_url', 'new_feed_url',
'is_blocked',
'is_completed',
'is_locked',
'created_by',
'updated_by',
]; ];
protected $returnType = \App\Entities\Podcast::class; protected $returnType = \App\Entities\Podcast::class;
@ -48,9 +50,9 @@ class PodcastModel extends Model
'title' => 'required', 'title' => 'required',
'name' => 'name' =>
'required|regex_match[/^[a-zA-Z0-9\_]{1,191}$/]|is_unique[podcasts.name,id,{id}]', 'required|regex_match[/^[a-zA-Z0-9\_]{1,191}$/]|is_unique[podcasts.name,id,{id}]',
'description' => 'required', 'description_markdown' => 'required',
'image_uri' => 'required', 'image_uri' => 'required',
'language' => 'required', 'language_code' => 'required',
'category_id' => 'required', 'category_id' => 'required',
'owner_email' => 'required|valid_email', 'owner_email' => 'required|valid_email',
'type' => 'required', 'type' => 'required',
@ -97,10 +99,10 @@ class PodcastModel extends Model
if (!($found = cache("user{$userId}_podcasts"))) { if (!($found = cache("user{$userId}_podcasts"))) {
$found = $this->select('podcasts.*') $found = $this->select('podcasts.*')
->join( ->join(
'users_podcasts', 'podcasts_users',
'users_podcasts.podcast_id = podcasts.id' 'podcasts_users.podcast_id = podcasts.id'
) )
->where('users_podcasts.user_id', $userId) ->where('podcasts_users.user_id', $userId)
->findAll(); ->findAll();
cache()->save("user{$userId}_podcasts", $found, DECADE); cache()->save("user{$userId}_podcasts", $found, DECADE);
@ -119,7 +121,7 @@ class PodcastModel extends Model
'group_id' => (int) $groupId, 'group_id' => (int) $groupId,
]; ];
return $this->db->table('users_podcasts')->insert($data); return $this->db->table('podcasts_users')->insert($data);
} }
public function updatePodcastContributor($userId, $podcastId, $groupId) public function updatePodcastContributor($userId, $podcastId, $groupId)
@ -127,7 +129,7 @@ class PodcastModel extends Model
cache()->delete("podcast{$podcastId}_contributors"); cache()->delete("podcast{$podcastId}_contributors");
return $this->db return $this->db
->table('users_podcasts') ->table('podcasts_users')
->where([ ->where([
'user_id' => (int) $userId, 'user_id' => (int) $userId,
'podcast_id' => (int) $podcastId, 'podcast_id' => (int) $podcastId,
@ -140,7 +142,7 @@ class PodcastModel extends Model
cache()->delete("podcast{$podcastId}_contributors"); cache()->delete("podcast{$podcastId}_contributors");
return $this->db return $this->db
->table('users_podcasts') ->table('podcasts_users')
->where([ ->where([
'user_id' => $userId, 'user_id' => $userId,
'podcast_id' => $podcastId, 'podcast_id' => $podcastId,
@ -151,7 +153,7 @@ class PodcastModel extends Model
public function getContributorGroupId($userId, $podcastId) public function getContributorGroupId($userId, $podcastId)
{ {
$user_podcast = $this->db $user_podcast = $this->db
->table('users_podcasts') ->table('podcasts_users')
->select('group_id') ->select('group_id')
->where([ ->where([
'user_id' => $userId, 'user_id' => $userId,

View File

@ -16,12 +16,12 @@ class UserModel extends \Myth\Auth\Models\UserModel
{ {
if (!($found = cache("podcast{$podcastId}_contributors"))) { if (!($found = cache("podcast{$podcastId}_contributors"))) {
$found = $this->select('users.*, auth_groups.name as podcast_role') $found = $this->select('users.*, auth_groups.name as podcast_role')
->join('users_podcasts', 'users_podcasts.user_id = users.id') ->join('podcasts_users', 'podcasts_users.user_id = users.id')
->join( ->join(
'auth_groups', 'auth_groups',
'auth_groups.id = users_podcasts.group_id' 'auth_groups.id = podcasts_users.group_id'
) )
->where('users_podcasts.podcast_id', $podcastId) ->where('podcasts_users.podcast_id', $podcastId)
->findAll(); ->findAll();
cache()->save("podcast{$podcastId}_contributors", $found, DECADE); cache()->save("podcast{$podcastId}_contributors", $found, DECADE);
@ -33,10 +33,10 @@ class UserModel extends \Myth\Auth\Models\UserModel
public function getPodcastContributor($user_id, $podcast_id) public function getPodcastContributor($user_id, $podcast_id)
{ {
return $this->select( return $this->select(
'users.*, users_podcasts.podcast_id as podcast_id, auth_groups.name as podcast_role' 'users.*, podcasts_users.podcast_id as podcast_id, auth_groups.name as podcast_role'
) )
->join('users_podcasts', 'users_podcasts.user_id = users.id') ->join('podcasts_users', 'podcasts_users.user_id = users.id')
->join('auth_groups', 'auth_groups.id = users_podcasts.group_id') ->join('auth_groups', 'auth_groups.id = podcasts_users.group_id')
->where([ ->where([
'users.id' => $user_id, 'users.id' => $user_id,
'podcast_id' => $podcast_id, 'podcast_id' => $podcast_id,

View File

@ -179,7 +179,7 @@
], ],
old( old(
'description_footer', 'description_footer',
$podcast->episode_description_footer ?? '', $podcast->episode_description_footer_markdown ?? '',
false false
), ),
'data-editor="markdown"' 'data-editor="markdown"'

View File

@ -163,7 +163,7 @@
'class' => 'form-textarea', 'class' => 'form-textarea',
'required' => 'required', 'required' => 'required',
], ],
old('description', $episode->description, false), old('description', $episode->description_markdown, false),
'data-editor="markdown"' 'data-editor="markdown"'
) ?> ) ?>
</div> </div>
@ -183,7 +183,7 @@
], ],
old( old(
'description_footer', 'description_footer',
$podcast->episode_description_footer ?? '', $podcast->episode_description_footer_markdown ?? '',
false false
), ),
'data-editor="markdown"' 'data-editor="markdown"'
@ -269,10 +269,9 @@
<?= form_switch( <?= form_switch(
lang('Episode.form.block') . lang('Episode.form.block') .
hint_tooltip(lang('Episode.form.block_hint'), 'ml-1'), hint_tooltip(lang('Episode.form.block_hint'), 'ml-1'),
['id' => 'block', 'name' => 'block'], ['id' => 'block', 'name' => 'block'],
'yes', 'yes',
old('block', $episode->block) old('block', $episode->is_blocked)
) ?> ) ?>
<?= form_section_close() ?> <?= form_section_close() ?>

View File

@ -15,7 +15,7 @@
<div class="chart-xy" id="by-day-listening-time-graph" data-chart-type="xy-duration-chart" data-chart-url="<?= route_to( <div class="chart-xy" id="by-day-listening-time-graph" data-chart-type="xy-duration-chart" data-chart-url="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByEpisode', 'Podcast',
'TotalListeningTimeByDay' 'TotalListeningTimeByDay'
) ?>"></div> ) ?>"></div>
</div> </div>
@ -25,7 +25,7 @@
<div class="chart-xy" id="by-month-listening-time-graph" data-chart-type="xy-duration-chart" data-chart-url="<?= route_to( <div class="chart-xy" id="by-month-listening-time-graph" data-chart-type="xy-duration-chart" data-chart-url="<?= route_to(
'analytics-data', 'analytics-data',
$podcast->id, $podcast->id,
'PodcastByEpisode', 'Podcast',
'TotalListeningTimeByMonth' 'TotalListeningTimeByMonth'
) ?>"></div> ) ?>"></div>
</div> </div>

View File

@ -27,7 +27,6 @@
'id' => 'image', 'id' => 'image',
'name' => 'image', 'name' => 'image',
'class' => 'form-input', 'class' => 'form-input',
'required' => 'required', 'required' => 'required',
'type' => 'file', 'type' => 'file',
'accept' => '.jpg,.jpeg,.png', 'accept' => '.jpg,.jpeg,.png',
@ -59,27 +58,21 @@
'required' => 'required', 'required' => 'required',
]) ?> ]) ?>
<?= form_fieldset('', ['class' => 'mb-4']) ?> <?= form_fieldset('', [
'class' => 'mb-4',
]) ?>
<legend> <legend>
<?= lang('Podcast.form.type.label') . <?= lang('Podcast.form.type.label') .
hint_tooltip(lang('Podcast.form.type.hint'), 'ml-1') ?> hint_tooltip(lang('Podcast.form.type.hint'), 'ml-1') ?>
</legend> </legend>
<?= form_radio( <?= form_radio(
[ ['id' => 'episodic', 'name' => 'type', 'class' => 'form-radio-btn'],
'id' => 'episodic',
'name' => 'type',
'class' => 'form-radio-btn',
],
'episodic', 'episodic',
old('type') ? old('type') == 'episodic' : true old('type') ? old('type') == 'episodic' : true
) ?> ) ?>
<label for="episodic"><?= lang('Podcast.form.type.episodic') ?></label> <label for="episodic"><?= lang('Podcast.form.type.episodic') ?></label>
<?= form_radio( <?= form_radio(
[ ['id' => 'serial', 'name' => 'type', 'class' => 'form-radio-btn'],
'id' => 'serial',
'name' => 'type',
'class' => 'form-radio-btn',
],
'serial', 'serial',
old('type') ? old('type') == 'serial' : false old('type') ? old('type') == 'serial' : false
) ?> ) ?>
@ -238,14 +231,7 @@
'value' => old('publisher'), 'value' => old('publisher'),
]) ?> ]) ?>
<?= form_label( <?= form_label(lang('Podcast.form.copyright'), 'copyright', [], '', true) ?>
lang('Podcast.form.copyright'),
'copyright',
[],
'',
true
) ?>
<?= form_input([ <?= form_input([
'id' => 'copyright', 'id' => 'copyright',
'name' => 'copyright', 'name' => 'copyright',
@ -263,22 +249,29 @@
<?= form_switch( <?= form_switch(
lang('Podcast.form.block'), lang('Podcast.form.block'),
['id' => 'block', 'name' => 'block'], [
'id' => 'block',
'name' => 'block',
],
'yes', 'yes',
old('block', false), old('block', false),
'mb-2' 'mb-2'
) ?> ) ?>
<?= form_switch( <?= form_switch(
lang('Podcast.form.complete'), lang('Podcast.form.complete'),
['id' => 'complete', 'name' => 'complete'], [
'id' => 'complete',
'name' => 'complete',
],
'yes', 'yes',
old('complete', false) old('complete', false),
'mb-2'
) ?> ) ?>
<?= form_switch( <?= form_switch(
lang('Podcast.form.lock'), lang('Podcast.form.lock') .
hint_tooltip(lang('Podcast.form.lock_hint'), 'ml-1'),
['id' => 'lock', 'name' => 'lock'], ['id' => 'lock', 'name' => 'lock'],
'yes', 'yes',
old('lock', true) old('lock', true)

View File

@ -88,7 +88,7 @@
'class' => 'form-textarea', 'class' => 'form-textarea',
'required' => 'required', 'required' => 'required',
], ],
old('description', $podcast->description, false), old('description', $podcast->description_markdown, false),
'data-editor="markdown"' 'data-editor="markdown"'
) ?> ) ?>
</div> </div>
@ -105,7 +105,7 @@
<?= form_dropdown( <?= form_dropdown(
'language', 'language',
$languageOptions, $languageOptions,
old('language', $podcast->language), old('language', $podcast->language_code),
[ [
'id' => 'language', 'id' => 'language',
'class' => 'form-select mb-4', 'class' => 'form-select mb-4',
@ -261,7 +261,7 @@
lang('Podcast.form.block'), lang('Podcast.form.block'),
['id' => 'block', 'name' => 'block'], ['id' => 'block', 'name' => 'block'],
'yes', 'yes',
old('block', $podcast->block), old('block', $podcast->is_blocked),
'mb-2' 'mb-2'
) ?> ) ?>
@ -269,7 +269,7 @@
lang('Podcast.form.complete'), lang('Podcast.form.complete'),
['id' => 'complete', 'name' => 'complete'], ['id' => 'complete', 'name' => 'complete'],
'yes', 'yes',
old('complete', $podcast->complete), old('complete', $podcast->is_completed),
'mb-2' 'mb-2'
) ?> ) ?>
@ -278,7 +278,7 @@
hint_tooltip(lang('Podcast.form.lock_hint'), 'ml-1'), hint_tooltip(lang('Podcast.form.lock_hint'), 'ml-1'),
['id' => 'lock', 'name' => 'lock'], ['id' => 'lock', 'name' => 'lock'],
'yes', 'yes',
old('lock', $podcast->lock) old('lock', $podcast->is_locked)
) ?> ) ?>
<?= form_section_close() ?> <?= form_section_close() ?>

View File

@ -19,7 +19,7 @@
<div class="relative flex items-start mb-4"> <div class="relative flex items-start mb-4">
<div class="flex flex-col w-12 mr-4"> <div class="flex flex-col w-12 mr-4">
<?= platform_icon($platform->icon_filename, 'w-full mb-1') ?> <?= platform_icon($platform->name, 'w-full mb-1') ?>
<div class="inline-flex bg-gray-200"> <div class="inline-flex bg-gray-200">
<?= anchor($platform->home_url, icon('external-link', 'mx-auto'), [ <?= anchor($platform->home_url, icon('external-link', 'mx-auto'), [
'class' => 'flex-1 text-gray-600 hover:text-gray-900', 'class' => 'flex-1 text-gray-600 hover:text-gray-900',
@ -81,7 +81,7 @@
'yes', 'yes',
old( old(
$platform->name . '_visible', $platform->name . '_visible',
$platform->visible ? $platform->visible : false $platform->is_visible ? $platform->is_visible : false
), ),
'text-sm' 'text-sm'
) ?> ) ?>

View File

@ -5,9 +5,7 @@
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<title><?= $episode->title ?></title> <title><?= $episode->title ?></title>
<meta name="description" content="<?= trim( <meta name="description" content="<?= $episode->description ?>"/>
preg_replace('/\s+/', ' ', strip_tags($episode->description_html))
) ?>"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<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"/>

View File

@ -6,9 +6,7 @@
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<title><?= $podcast->title ?></title> <title><?= $podcast->title ?></title>
<meta name="description" content="<?= trim( <meta name="description" content="<?= $podcast->description ?>"/>
preg_replace('/\s+/', ' ', strip_tags($podcast->description_html))
) ?>"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<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"/>
@ -46,12 +44,9 @@
] ]
) ?> ) ?>
<?php foreach ($podcast->platforms as $platform): ?> <?php foreach ($podcast->platforms as $platform): ?>
<?php if ($platform->visible): ?> <?php if ($platform->is_visible): ?>
<a href="<?= $platform->link_url ?>" title="<?= $platform->label ?>" target="_blank" rel="noopener noreferrer" class="ml-2"> <a href="<?= $platform->link_url ?>" title="<?= $platform->label ?>" target="_blank" rel="noopener noreferrer" class="ml-2">
<?= platform_icon( <?= platform_icon($platform->name, 'h-8') ?>
$platform->icon_filename,
'h-8'
) ?>
</a> </a>
<?php endif; ?> <?php endif; ?>
<?php endforeach; ?> <?php endforeach; ?>

View File

@ -10,7 +10,7 @@ echo "$( jq '.version = "'$COMPOSER_VERSION'"' composer.json )" > composer.json
sed -i "s/^defined('CP_VERSION').*/defined('CP_VERSION') || define('CP_VERSION', '$VERSION');/" ./app/Config/Constants.php sed -i "s/^defined('CP_VERSION').*/defined('CP_VERSION') || define('CP_VERSION', '$VERSION');/" ./app/Config/Constants.php
# download GeoLite2-City archive and extract it to writable/uploads # download GeoLite2-City archive and extract it to writable/uploads
wget -c "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=v3PguJMcmZMb9Ld0&suffix=tar.gz" -O - | tar -xz -C ./writable/uploads/ wget -c "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=$MAXMIND_LICENCE_KEY&suffix=tar.gz" -O - | tar -xz -C ./writable/uploads/
# rename extracted archives' folders # rename extracted archives' folders
mv ./writable/uploads/GeoLite2-City* ./writable/uploads/GeoLite2-City mv ./writable/uploads/GeoLite2-City* ./writable/uploads/GeoLite2-City